commit e667d3f9d24821046aaf082bfb3256b0e09c372d Author: falcon <9504402@qq.com> Date: Fri Nov 22 10:48:53 2019 +0800 项目初始化 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22243cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,266 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +.vs/ + +#Log files +HosManager/Logs/ + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/Falcon.ModelSP.Test/Db/TestDbContext.cs b/Falcon.ModelSP.Test/Db/TestDbContext.cs new file mode 100644 index 0000000..113cb9f --- /dev/null +++ b/Falcon.ModelSP.Test/Db/TestDbContext.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +using Falcon.ModelSP; +using System.Collections.Generic; + +namespace Falcon.ModelSP.Test.Db +{ + class TestDbContext:DbContext + { + protected TestDbContext() { } + protected TestDbContext(DbContextOptions option) : base(option) { } + + public static TestDbContext GetDbInMemory() { + var option = new DbContextOptionsBuilder().UseInMemoryDatabase("TestDb").Options; + var db = new TestDbContext(option); + AddTestPr(db); + return db; + } + + public static void AddTestPr(TestDbContext db) { + var sql = @" + DROP PROC Pr_AddOne + CREATE PROCEDURE Pr_AddOne + @a AS INT + AS + Set Nocount On + SELECT @a+1 aa; + GO + "; + //db.Database.ExecuteSqlCommandAsync(sql).Wait(); + } + + public IEnumerable Pr_AddOne(Pr_AddOne data) { + return this.RunProcuder(data); + } + } + + public class Pr_AddOne + { + public int A { get; set; } + } + public class Pr_AddOneResult + { + public int Aa { get; set; } + } +} diff --git a/Falcon.ModelSP.Test/Falcon.ModelSP.Test.csproj b/Falcon.ModelSP.Test/Falcon.ModelSP.Test.csproj new file mode 100644 index 0000000..f523626 --- /dev/null +++ b/Falcon.ModelSP.Test/Falcon.ModelSP.Test.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + diff --git a/Falcon.ModelSP.Test/ModelSPTest.cs b/Falcon.ModelSP.Test/ModelSPTest.cs new file mode 100644 index 0000000..72ea8b8 --- /dev/null +++ b/Falcon.ModelSP.Test/ModelSPTest.cs @@ -0,0 +1,61 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Falcon.ModelSP; +using System.Linq; +using Falcon.ModelSP.Test.Db; + +namespace Falcon.ModelSP.Test +{ + [TestClass] + public class ModelSPTest + { + [TestMethod] + public void getParamsTest() { + var model = new ParmModel { Id = 1,Name = "n1",AAAge = 10 }; + + var data = DataExtend.getParams(model); + Assert.IsNotNull(data); + Assert.IsTrue(data.Count() == typeof(ParmModel).GetProperties().Length - 1); + + Assert.IsTrue(data.Any(m => m.ParameterName == "@Id" && m.Value.Equals(1))); + Assert.IsTrue(data.Any(m => m.ParameterName == "@Name" && m.Value.Equals("n1"))); + Assert.IsTrue(data.Any(m => m.ParameterName == "@age" && m.Value.Equals(10))); + Assert.IsTrue(!data.Any(m => m.ParameterName == "@Address")); + } + + [TestMethod] + public void getProcuderNameTest() { + var model = new ParmModel { }; + Assert.IsTrue(DataExtend.getProcuderName() == "ParmModel"); + Assert.IsTrue(DataExtend.getProcuderName() == "sp123"); + } + + class ParmModel + { + public int Id { get; set; } + + public string Name { get; set; } + + [FalconSPPrarmName("age")] + public int AAAge { get; set; } + + [FalconSPIgnore] + public string Address { get; set; } + } + + [FalconSPProcuderName("sp123")] + class ParmModel1 { } + + [TestMethod] + public void InMemoryDbTest() { + //ڴݿ޷ʹùϵģͣ޷ + return; + using(var db = TestDbContext.GetDbInMemory()) { + var re = db.Pr_AddOne(new Pr_AddOne { A = 1 }); + Assert.IsTrue(re != null); + Assert.IsTrue(re.Any()); + var first = re.First(); + Assert.IsTrue(first.Aa == 2); + } + } + } +} diff --git a/Falcon.ModelSP.sln b/Falcon.ModelSP.sln new file mode 100644 index 0000000..53539c7 --- /dev/null +++ b/Falcon.ModelSP.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Falcon.ModelSP", "Falcon.ModelSP\Falcon.ModelSP.csproj", "{0EBEFFE2-D7C0-4E41-9AA4-F9E130E5E825}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Falcon.ModelSP.Test", "Falcon.ModelSP.Test\Falcon.ModelSP.Test.csproj", "{F3184885-2AB1-42A5-8570-4C6429D6FCBD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0EBEFFE2-D7C0-4E41-9AA4-F9E130E5E825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EBEFFE2-D7C0-4E41-9AA4-F9E130E5E825}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EBEFFE2-D7C0-4E41-9AA4-F9E130E5E825}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EBEFFE2-D7C0-4E41-9AA4-F9E130E5E825}.Release|Any CPU.Build.0 = Release|Any CPU + {F3184885-2AB1-42A5-8570-4C6429D6FCBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3184885-2AB1-42A5-8570-4C6429D6FCBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3184885-2AB1-42A5-8570-4C6429D6FCBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3184885-2AB1-42A5-8570-4C6429D6FCBD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CA8B38ED-C59C-47BF-B783-E932E4044BA0} + EndGlobalSection +EndGlobal diff --git a/Falcon.ModelSP/DataExtend.cs b/Falcon.ModelSP/DataExtend.cs new file mode 100644 index 0000000..9389bea --- /dev/null +++ b/Falcon.ModelSP/DataExtend.cs @@ -0,0 +1,125 @@ +using System.Collections.Generic; +using System.Data.Common; +using Microsoft.Data.SqlClient; +using System.Linq; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + + +namespace Falcon.ModelSP +{ + /// + /// 数据库上下文方法扩展 + /// + public static class DataExtend + { + /// + /// 执行无返回值的存储过程 + /// + /// 参数类型 + /// 数据上下文 + /// 参数数据 + public static int RunProcuder(this DbContext db,TPrarmType data) { + var parms = getParams(data); + return db.Database.ExecuteSqlCommand(getProcuderName(),parms.ToArray()); + } + /// + /// 执行有返回值的存储过程 + /// + /// 参数类型 + /// 返回结果项类型 + /// 数据上下文 + /// 参数数据 + public static IEnumerable RunProcuder(this DbContext db,TPrarmType data) + where TResultType : class, new() { + var parms = getParams(data); + return db.Database.SqlQuery(getProcuderName(),parms.ToArray()).ToList(); + } + + /// + /// 获取存储过程参数枚举 + /// + /// 参数模型类型 + /// 参数实例 + public static IEnumerable getParams(T data) { + if(data == null) + yield break; + foreach(var p in typeof(T).GetProperties()) { + if(!p.CanRead || ignoreProp(p)) + continue; + yield return new SqlParameter($"@{getName(p)}",p.GetValue(data)); + } + } + + /// + /// 是否忽略属性 + /// + /// 要检查的属性 + private static bool ignoreProp(PropertyInfo p) { + return p.GetCustomAttribute(true) != null; + } + + /// + /// 获取存储过程参数名称 + /// + /// 对应的属性 + private static string getName(PropertyInfo p) { + var np = p.GetCustomAttribute(true); + if(np != null && np is FalconSPPrarmNameAttribute na) { + return na.Name; + } + return p.Name; + } + /// + /// 获取存储过程名 + /// + /// 参数模型 + public static string getProcuderName() { + var attr = typeof(T).GetCustomAttribute(true); + if(attr != null && attr is FalconSPProcuderNameAttribute pna && !string.IsNullOrEmpty(pna.ProcuderName)) { + return pna.ProcuderName; + } + return typeof(T).Name; + } + + /// + /// 根据传入的sql语句和参数枚举执行存储过程,并且返回类型枚举 + /// + /// 返回值类型 + /// 数据库上下文 + /// 要执行的sql语句 + /// 参数枚举 + /// + public static IEnumerable SqlQuery(this DatabaseFacade db,string sql,params SqlParameter[] paras) + where TR : class, new() { + var connection = db.GetDbConnection(); + using(var cmd = connection.CreateCommand()) { + cmd.CommandText = sql; + cmd.CommandType = System.Data.CommandType.StoredProcedure; + cmd.Parameters.AddRange(paras); + connection.Open(); + var dr = cmd.ExecuteReader(); + var result = new List(); + if(!dr.CanGetColumnSchema()) + return result; + while(dr.Read()) { + var item = new TR(); + var columnSchema = dr.GetColumnSchema(); + for(var i = 0;i < columnSchema.Count;i++) { + var name = dr.GetName(i); + var value = dr.IsDBNull(i) ? null : dr.GetValue(i); + var pi = typeof(TR).GetProperty(name); + if(pi == null || !pi.CanWrite) + continue; + pi.SetValue(item,value); + } + result.Add(item); + } + connection.Close(); + return result; + } + } + + } +} diff --git a/Falcon.ModelSP/Falcon.ModelSP.csproj b/Falcon.ModelSP/Falcon.ModelSP.csproj new file mode 100644 index 0000000..fa0ecac --- /dev/null +++ b/Falcon.ModelSP/Falcon.ModelSP.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + true + 1.0.1.0 + 1.0.1.0 + 1.0.1 + + + + C:\Documentation\develop\FalconCore\Falcon.ModelSP\Falcon.ModelSP.xml + + + + + + + + + diff --git a/Falcon.ModelSP/Falcon.ModelSP.xml b/Falcon.ModelSP/Falcon.ModelSP.xml new file mode 100644 index 0000000..3d22121 --- /dev/null +++ b/Falcon.ModelSP/Falcon.ModelSP.xml @@ -0,0 +1,124 @@ + + + + Falcon.ModelSP + + + + + 数据库上下文方法扩展 + + + + + 执行无返回值的存储过程 + + 参数类型 + 数据上下文 + 参数数据 + + + + 执行有返回值的存储过程 + + 参数类型 + 返回结果项类型 + 数据上下文 + 参数数据 + + + + 获取存储过程参数枚举 + + 参数模型类型 + 参数实例 + + + + 是否忽略属性 + + 要检查的属性 + + + + 获取存储过程参数名称 + + 对应的属性 + + + + 获取存储过程名 + + 参数模型 + + + + 根据传入的sql语句和参数枚举执行存储过程,并且返回类型枚举 + + 返回值类型 + 数据库上下文 + 要执行的sql语句 + 参数枚举 + + + + + 表示调用时候忽略此属性 + + + + + 定义名称 + + + + + 定义存储过程名称 + + + + + 存储过程执行器实现 + + + + + 执行存储过程接口 + + + + + 通过数据库上下文执行无返回值的存储过程 + + 参数类型 + 数据上下文 + 参数数据 + + + + 通过数据库上下文执行存储过程,并返回查询结果 + + 参数类型 + 返回结果项类型 + 数据上下文 + 参数数据 + + + + IServiceCollection扩展 + + + + + 注册对数据库存储过程模型化的调用扩展 + + 注册服务集合 + + + + + 存储过程没有找到的异常 + + + + diff --git a/Falcon.ModelSP/FalconSPIgnoreAttribute.cs b/Falcon.ModelSP/FalconSPIgnoreAttribute.cs new file mode 100644 index 0000000..b3bf9e6 --- /dev/null +++ b/Falcon.ModelSP/FalconSPIgnoreAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Falcon.ModelSP +{ + /// + /// 表示调用时候忽略此属性 + /// + public class FalconSPIgnoreAttribute:Attribute + { + } +} diff --git a/Falcon.ModelSP/FalconSPPrarmNameAttribute.cs b/Falcon.ModelSP/FalconSPPrarmNameAttribute.cs new file mode 100644 index 0000000..8ef4552 --- /dev/null +++ b/Falcon.ModelSP/FalconSPPrarmNameAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Falcon.ModelSP +{ + /// + /// 定义名称 + /// + public class FalconSPPrarmNameAttribute:Attribute + { + public FalconSPPrarmNameAttribute(string name) { this.Name = name; } + public string Name { get; set; } + } +} diff --git a/Falcon.ModelSP/FalconSPProcuderNameAttribute.cs b/Falcon.ModelSP/FalconSPProcuderNameAttribute.cs new file mode 100644 index 0000000..a935c62 --- /dev/null +++ b/Falcon.ModelSP/FalconSPProcuderNameAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Falcon.ModelSP +{ + /// + /// 定义存储过程名称 + /// + public class FalconSPProcuderNameAttribute:Attribute + { + public string ProcuderName { get; set; } + + public FalconSPProcuderNameAttribute(string m) => this.ProcuderName = m; + } +} diff --git a/Falcon.ModelSP/FalconSPRuner.cs b/Falcon.ModelSP/FalconSPRuner.cs new file mode 100644 index 0000000..0af56ae --- /dev/null +++ b/Falcon.ModelSP/FalconSPRuner.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; + +namespace Falcon.ModelSP +{ + /// + /// 存储过程执行器实现 + /// + public class FalconSPRuner:IFalconSPRuner + { + public int RunSP(DbContext db,TPrarmType data) { + return db.RunProcuder(data); + } + + public IEnumerable RunSP(DbContext db,TPrarmType data) + where TResultType : class, new() { + return db.RunProcuder(data); + } + } +} diff --git a/Falcon.ModelSP/IFalconSPRuner.cs b/Falcon.ModelSP/IFalconSPRuner.cs new file mode 100644 index 0000000..2967d4b --- /dev/null +++ b/Falcon.ModelSP/IFalconSPRuner.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; + +namespace Falcon.ModelSP +{ + /// + /// 执行存储过程接口 + /// + public interface IFalconSPRuner + { + /// + /// 通过数据库上下文执行无返回值的存储过程 + /// + /// 参数类型 + /// 数据上下文 + /// 参数数据 + int RunSP(DbContext db,TPrarmType data); + + /// + /// 通过数据库上下文执行存储过程,并返回查询结果 + /// + /// 参数类型 + /// 返回结果项类型 + /// 数据上下文 + /// 参数数据 + IEnumerable RunSP(DbContext db,TPrarmType data) where TResultType : class, new(); + } +} diff --git a/Falcon.ModelSP/IServiceCollectionExtend.cs b/Falcon.ModelSP/IServiceCollectionExtend.cs new file mode 100644 index 0000000..35217ff --- /dev/null +++ b/Falcon.ModelSP/IServiceCollectionExtend.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Falcon.ModelSP +{ + /// + /// IServiceCollection扩展 + /// + public static class IServiceCollectionExtend + { + /// + /// 注册对数据库存储过程模型化的调用扩展 + /// + /// 注册服务集合 + /// + public static IServiceCollection UseFalconSP(this IServiceCollection services) { + return services.AddSingleton(); + } + } +} diff --git a/Falcon.ModelSP/ProcuderNotFoundException.cs b/Falcon.ModelSP/ProcuderNotFoundException.cs new file mode 100644 index 0000000..d785eb4 --- /dev/null +++ b/Falcon.ModelSP/ProcuderNotFoundException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Falcon.ModelSP +{ + /// + /// 存储过程没有找到的异常 + /// + public class ProcuderNotFoundException:Exception + { + public ProcuderNotFoundException() : base("存储过程没有找到") { } + } +}