├── .gitattributes
├── .gitignore
├── Leo.Chimp.sln
├── README.MD
├── README.md
├── example
├── Leo.Chimp.Domain
│ ├── ChimpDbContext.cs
│ ├── Entities
│ │ ├── School.cs
│ │ └── Student.cs
│ ├── Leo.Chimp.Domain.csproj
│ └── Repositories
│ │ ├── SchoolRepository.cs
│ │ └── StudentRepository.cs
└── Leo.Chimp.Example
│ ├── Controllers
│ └── ValuesController.cs
│ ├── Leo.Chimp.Example.csproj
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── Startup.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── sqlscript
├── mssql
│ └── script.sql
└── mysql
│ └── script.sql
├── src
└── Leo.Chimp
│ ├── BaseDbContext.cs
│ ├── DapperAdapter
│ ├── ISqlAdapter.cs
│ ├── MysqlAdapter.cs
│ ├── PagingUtil.cs
│ ├── PartedSql.cs
│ ├── SqlServerAdapter.cs
│ └── StringBuilderCache.cs
│ ├── EfCoreRepository.cs
│ ├── Extensions.Type.cs
│ ├── IEntity.cs
│ ├── IPagedList.cs
│ ├── IRepository.cs
│ ├── IUnitOfWork.cs
│ ├── KingOptions.cs
│ ├── Leo.Chimp.csproj
│ ├── PagedList.cs
│ ├── PagedListExtensions.cs
│ ├── ServiceCollectionExtension.cs
│ └── UnitOfWork.cs
└── test
└── Leo.Chimp.Test
├── ChimpDbContext.cs
├── ChimpDbContextTest.cs
├── DapperTest.cs
├── EfRepositoryTest.cs
├── Entities
├── School.cs
└── Student.cs
├── Leo.Chimp.Test.csproj
└── Repositories
├── SchoolRepository.cs
└── StudentRepository.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/Leo.Chimp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.32228.343
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leo.Chimp", "src\Leo.Chimp\Leo.Chimp.csproj", "{AE57FE9E-110F-4BBD-AF58-243A37E334C3}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A9856CFD-8B10-4C9E-B4B7-E49704894C71}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8DC16EBA-2D9F-473A-820E-5A96C35E44F5}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{98E27906-6784-4538-9CDA-90FDEA7C83EF}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leo.Chimp.Example", "example\Leo.Chimp.Example\Leo.Chimp.Example.csproj", "{22CF40A1-EE7E-4EBA-A502-5C5FC0D0758B}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leo.Chimp.Domain", "example\Leo.Chimp.Domain\Leo.Chimp.Domain.csproj", "{1216801F-2F9A-423D-9460-E3D301496533}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Leo.Chimp.Test", "test\Leo.Chimp.Test\Leo.Chimp.Test.csproj", "{1B160D6D-A698-4465-9F9F-768D7248AB3B}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {AE57FE9E-110F-4BBD-AF58-243A37E334C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {AE57FE9E-110F-4BBD-AF58-243A37E334C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {AE57FE9E-110F-4BBD-AF58-243A37E334C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {AE57FE9E-110F-4BBD-AF58-243A37E334C3}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {22CF40A1-EE7E-4EBA-A502-5C5FC0D0758B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {22CF40A1-EE7E-4EBA-A502-5C5FC0D0758B}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {22CF40A1-EE7E-4EBA-A502-5C5FC0D0758B}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {22CF40A1-EE7E-4EBA-A502-5C5FC0D0758B}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {1216801F-2F9A-423D-9460-E3D301496533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {1216801F-2F9A-423D-9460-E3D301496533}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {1216801F-2F9A-423D-9460-E3D301496533}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {1216801F-2F9A-423D-9460-E3D301496533}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {1B160D6D-A698-4465-9F9F-768D7248AB3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {1B160D6D-A698-4465-9F9F-768D7248AB3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {1B160D6D-A698-4465-9F9F-768D7248AB3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {1B160D6D-A698-4465-9F9F-768D7248AB3B}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(NestedProjects) = preSolution
47 | {AE57FE9E-110F-4BBD-AF58-243A37E334C3} = {A9856CFD-8B10-4C9E-B4B7-E49704894C71}
48 | {22CF40A1-EE7E-4EBA-A502-5C5FC0D0758B} = {98E27906-6784-4538-9CDA-90FDEA7C83EF}
49 | {1216801F-2F9A-423D-9460-E3D301496533} = {98E27906-6784-4538-9CDA-90FDEA7C83EF}
50 | {1B160D6D-A698-4465-9F9F-768D7248AB3B} = {8DC16EBA-2D9F-473A-820E-5A96C35E44F5}
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {D936E593-6A06-4989-8708-382308D741CF}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | #### 更新日历
2 | ###### 2022.5.25
3 | 1. 新增对.netcore3.1和.net6支持。
4 | 2. 删除update对lambda表达式的支持,实际生产并不实用,建议先查询再复制最后savechange的做法。
5 | #### 背景
6 | 17年开始,公司开始向DotNet Core转型,面对ORM工具的选型,当时围绕Dapper和EF发生了激烈的讨论。项目团队更加关注快速交付,他们主张使用EF这种能快速开发的ORM工具;而在线业务团队对性能有更高的要求,他们更希望使用能直接执行Sql语句的Dapper这样可控性更高。而对于架构团队来说,满足开发团队的各种需求,提高他们的开发效率是最核心的价值所在,所以当时决定做一个混合型的既支持EF又支持dapper的数据仓储。
7 |
8 | #### 为什么选择EF+Dapper
9 | 目前来说EF和Dapper是.NET平台最主流的ORM工具,团队成员的接受程度很高,学习成本最低,因为主主流,所以相关的资料非常齐全,各种坑也最少。
10 |
11 | #### 介绍
12 | 1. 它不是一个ORM工具,它不做任何关于数据底层的操作
13 | 2. 它是一个简易封装的数据库仓储和工作单元模型
14 | 3. 能帮助你快速的构建项目的数据访问层
15 | 4. 经过了2年多时间,10个项目组,大小近100多个线上项目的考验
16 | 5. 支持EF和Dapper,可以在项目中随意切换使用
17 | 6. 支持工作单元模式,也支持事务
18 | 7. 支持Mysql和Mssql
19 | 8. 支持同步和异步操作,推荐使用异步
20 | > PS: 简单操作使用EF,复杂sql操作使用Dapper是快速开发的秘诀。
21 |
22 | #### 使用方法
23 | 引入nuget
24 | ```
25 |
26 | ```
27 | 创建实体对象,继承IEntity
28 |
29 | ```
30 | public class School : IEntity
31 | {
32 | public Guid Id { get; set; }
33 | public string Name { get; set; }
34 | }
35 | ```
36 | 创建仓储接口和实现类,分别继承IRepository和EfCoreRepository
37 | ```
38 | public interface ISchoolRepository : IRepository
39 | {
40 | }
41 | public class SchoolRepository: EfCoreRepository,ISchoolRepository
42 | {
43 | public SchoolRepository(DbContext context) : base(context)
44 | {
45 | }
46 | }
47 | ```
48 | 创建上下文,继承BaseDbContext,如果你不需要操作上下文可以不用做这一步
49 | ```
50 | public class ChimpDbContext : BaseDbContext
51 | {
52 | public ChimpDbContext(DbContextOptions options) : base(options)
53 | {
54 | }
55 | protected override void OnModelCreating(ModelBuilder modelBuilder)
56 | {
57 | base.OnModelCreating(modelBuilder);
58 | //your code
59 | }
60 | }
61 | ```
62 | 注入服务
63 | ```
64 | services.AddChimp(
65 | opt =>
66 | opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")
67 | );
68 | ```
69 | 如果你没有创建上下文
70 | ```
71 | services.AddChimp(
72 | opt =>
73 | opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")
74 | );
75 | ```
76 | 在Controller中使用
77 | ```
78 | public class ValuesController : ControllerBase
79 | {
80 | private readonly ISchoolRepository _schoolRepository;
81 | private readonly IUnitOfWork _unitOfWork;
82 | public ValuesController(ISchoolRepository schoolRepository, IUnitOfWork unitOfWork)
83 | {
84 | _schoolRepository = schoolRepository;
85 | _unitOfWork = unitOfWork;
86 | }
87 | }
88 | ```
89 | #### 详细使用说明
90 | 查询
91 | ```
92 | //根据主键查询
93 | _schoolRepository.GetById(Id)
94 | ```
95 | ```
96 | //不带追踪的查询,返回数据不能用于更新或删除操作,性能快
97 | schoolRepository.TableNoTracking.First(x => x.Id == Id);
98 | ```
99 | ```
100 | //带追踪的查询,返回数据可以用于更新或删除操作,性能稍慢
101 | schoolRepository.Table.First(x => x.Id == Id);
102 | ```
103 | ```
104 | //分页查询
105 | _schoolRepository.TableNoTracking.ToPagedList(1,10);
106 | ```
107 | ```
108 | //sql语句查询
109 | _unitOfWork.QueryAsync("select * from school");
110 |
111 | ```
112 | ```
113 | //sql分页查询
114 | _unitOfWork.QueryPagedListAsync(1, 10, "select * from school order by id");
115 | ```
116 | > 关于查询,暴露了返回IQueryable的TableNoTracking、Table这两个属性,让开发人员自己组装Lambda表达式进行查询操作
117 |
118 | 新增
119 | ```
120 | //新增,支持批量新增
121 | _schoolRepository.Insert(school);
122 | _unitOfWork.SaveChanges();
123 | ```
124 | ```
125 | //sql语句新增
126 | _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
127 | school);
128 | ```
129 | 编辑
130 | ```
131 | //编辑,支持批量编辑
132 | var school = _schoolRepository.GetByIdAsync(Id);
133 | school.Name="newschool";
134 | _schoolRepository.Update(school);
135 | _unitOfWork.SaveChanges();
136 | ```
137 |
138 | ```
139 | //sql语句编辑
140 | _unitOfWork.ExecuteAsync("update school set name=@Name where id=@Id",
141 | school);
142 | ```
143 | 删除
144 | ```
145 | //删除,支持批量删除
146 | _schoolRepository.Delete(school);
147 | _unitOfWork.SaveChanges();
148 | ```
149 | ```
150 | //根据lambda删除
151 | _schoolRepository.Delete(x => x.Id == Id);
152 | _unitOfWork.SaveChanges();
153 | ```
154 | 事务
155 | ```
156 | //工作单元模式使用事务
157 | await _schoolRepository.InsertAsync(school1);
158 | await _schoolRepository.InsertAsync(school1);
159 | await _unitOfWork.SaveChangesAsync();
160 | ```
161 | ```
162 | //dapper使用事务
163 | using (var tran = _unitOfWork.BeginTransaction())
164 | {
165 | try
166 | {
167 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
168 | school1,tran);
169 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
170 | school2,tran);
171 | tran.Commit();
172 | }
173 | catch (Exception e)
174 | {
175 | tran.Rollback();
176 | }
177 | }
178 | ```
179 | ```
180 | //dapper+ef混合使用事务
181 | using (var tran = _unitOfWork.BeginTransaction())
182 | {
183 | try
184 | {
185 | await _schoolRepository.InsertAsync(school1);
186 | await _unitOfWork.SaveChangesAsync();
187 |
188 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
189 | school2);
190 | tran.Commit();
191 | }
192 | catch (Exception e)
193 | {
194 | tran.Rollback();
195 | }
196 | }
197 | ```
198 | 高级用法
199 | ```
200 | //通过GetConnection可以使用更多dapper扩展的方法
201 | _unitOfWork.GetConnection().QueryAsync("select * from school");
202 | ```
203 | #### 写在最后
204 | Chimp核心是基于EF和Dapper的,所以EF和Dapper一切功能都可以使用。比如导航属性,字段映射等等。这个库是线上项目核心依赖,会长期更新维护,希望大家能提出更好的意见。
205 |
206 | #### 项目地址
207 | 数据库脚本在根目录的sqlscript文件夹里面
208 | [github地址](https://github.com/longxianghui/chimp.git)
209 |
210 | #### 快速找到作者
211 | QQ:240226543
212 | Emall: 240226543@qq.com
213 | 遇到紧急问题可以联系我
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #### 更新日历
2 | ###### 2022.5.25
3 | 1. 新增对.netcore3.1和.net6支持。
4 | 2. 删除update对lambda表达式的支持,实际生产并不实用,建议先查询再复制最后savechange的做法。
5 | #### 背景
6 | 17年开始,公司开始向DotNet Core转型,面对ORM工具的选型,当时围绕Dapper和EF发生了激烈的讨论。项目团队更加关注快速交付,他们主张使用EF这种能快速开发的ORM工具;而在线业务团队对性能有更高的要求,他们更希望使用能直接执行Sql语句的Dapper这样可控性更高。而对于架构团队来说,满足开发团队的各种需求,提高他们的开发效率是最核心的价值所在,所以当时决定做一个混合型的既支持EF又支持dapper的数据仓储。
7 |
8 | #### 为什么选择EF+Dapper
9 | 目前来说EF和Dapper是.NET平台最主流的ORM工具,团队成员的接受程度很高,学习成本最低,因为主主流,所以相关的资料非常齐全,各种坑也最少。
10 |
11 | #### 介绍
12 | 1. 它不是一个ORM工具,它不做任何关于数据底层的操作
13 | 2. 它是一个简易封装的数据库仓储和工作单元模型
14 | 3. 能帮助你快速的构建项目的数据访问层
15 | 4. 经过了2年多时间,10个项目组,大小近100多个线上项目的考验
16 | 5. 支持EF和Dapper,可以在项目中随意切换使用
17 | 6. 支持工作单元模式,也支持事务
18 | 7. 支持Mysql和Mssql
19 | 8. 支持同步和异步操作,推荐使用异步
20 | > PS: 简单操作使用EF,复杂sql操作使用Dapper是快速开发的秘诀。
21 |
22 | #### 使用方法
23 | 引入nuget
24 | ```
25 |
26 | ```
27 | 创建实体对象,继承IEntity
28 |
29 | ```
30 | public class School : IEntity
31 | {
32 | public Guid Id { get; set; }
33 | public string Name { get; set; }
34 | }
35 | ```
36 | 创建仓储接口和实现类,分别继承IRepository和EfCoreRepository
37 | ```
38 | public interface ISchoolRepository : IRepository
39 | {
40 | }
41 | public class SchoolRepository: EfCoreRepository,ISchoolRepository
42 | {
43 | public SchoolRepository(DbContext context) : base(context)
44 | {
45 | }
46 | }
47 | ```
48 | 创建上下文,继承BaseDbContext,如果你不需要操作上下文可以不用做这一步
49 | ```
50 | public class ChimpDbContext : BaseDbContext
51 | {
52 | public ChimpDbContext(DbContextOptions options) : base(options)
53 | {
54 | }
55 | protected override void OnModelCreating(ModelBuilder modelBuilder)
56 | {
57 | base.OnModelCreating(modelBuilder);
58 | //your code
59 | }
60 | }
61 | ```
62 | 注入服务
63 | ```
64 | services.AddChimp(
65 | opt =>
66 | opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")
67 | );
68 | ```
69 | 如果你没有创建上下文
70 | ```
71 | services.AddChimp(
72 | opt =>
73 | opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123")
74 | );
75 | ```
76 | 在Controller中使用
77 | ```
78 | public class ValuesController : ControllerBase
79 | {
80 | private readonly ISchoolRepository _schoolRepository;
81 | private readonly IUnitOfWork _unitOfWork;
82 | public ValuesController(ISchoolRepository schoolRepository, IUnitOfWork unitOfWork)
83 | {
84 | _schoolRepository = schoolRepository;
85 | _unitOfWork = unitOfWork;
86 | }
87 | }
88 | ```
89 | #### 详细使用说明
90 | 查询
91 | ```
92 | //根据主键查询
93 | _schoolRepository.GetById(Id)
94 | ```
95 | ```
96 | //不带追踪的查询,返回数据不能用于更新或删除操作,性能快
97 | schoolRepository.TableNoTracking.First(x => x.Id == Id);
98 | ```
99 | ```
100 | //带追踪的查询,返回数据可以用于更新或删除操作,性能稍慢
101 | schoolRepository.Table.First(x => x.Id == Id);
102 | ```
103 | ```
104 | //分页查询
105 | _schoolRepository.TableNoTracking.ToPagedList(1,10);
106 | ```
107 | ```
108 | //sql语句查询
109 | _unitOfWork.QueryAsync("select * from school");
110 |
111 | ```
112 | ```
113 | //sql分页查询
114 | _unitOfWork.QueryPagedListAsync(1, 10, "select * from school order by id");
115 | ```
116 | > 关于查询,暴露了返回IQueryable的TableNoTracking、Table这两个属性,让开发人员自己组装Lambda表达式进行查询操作
117 |
118 | 新增
119 | ```
120 | //新增,支持批量新增
121 | _schoolRepository.Insert(school);
122 | _unitOfWork.SaveChanges();
123 | ```
124 | ```
125 | //sql语句新增
126 | _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
127 | school);
128 | ```
129 | 编辑
130 | ```
131 | //编辑,支持批量编辑
132 | var school = _schoolRepository.GetByIdAsync(Id);
133 | school.Name="newschool";
134 | _schoolRepository.Update(school);
135 | _unitOfWork.SaveChanges();
136 | ```
137 | ```
138 | //编辑,不用先查询
139 | var school = new School
140 | {
141 | Id = "xxxxxx",
142 | Name = "newschool"
143 | };
144 | _schoolRepository.Update(school, x => x.Name);
145 | _unitOfWork.SaveChanges();
146 | ```
147 | ```
148 | //sql语句编辑
149 | _unitOfWork.ExecuteAsync("update school set name=@Name where id=@Id",
150 | school);
151 | ```
152 | 删除
153 | ```
154 | //删除,支持批量删除
155 | _schoolRepository.Delete(school);
156 | _unitOfWork.SaveChanges();
157 | ```
158 | ```
159 | //根据lambda删除
160 | _schoolRepository.Delete(x => x.Id == Id);
161 | _unitOfWork.SaveChanges();
162 | ```
163 | 事务
164 | ```
165 | //工作单元模式使用事务
166 | await _schoolRepository.InsertAsync(school1);
167 | await _schoolRepository.InsertAsync(school1);
168 | await _unitOfWork.SaveChangesAsync();
169 | ```
170 | ```
171 | //dapper使用事务
172 | using (var tran = _unitOfWork.BeginTransaction())
173 | {
174 | try
175 | {
176 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
177 | school1,tran);
178 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
179 | school2,tran);
180 | tran.Commit();
181 | }
182 | catch (Exception e)
183 | {
184 | tran.Rollback();
185 | }
186 | }
187 | ```
188 | ```
189 | //dapper+ef混合使用事务
190 | using (var tran = _unitOfWork.BeginTransaction())
191 | {
192 | try
193 | {
194 | await _schoolRepository.InsertAsync(school1);
195 | await _unitOfWork.SaveChangesAsync();
196 |
197 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
198 | school2);
199 | tran.Commit();
200 | }
201 | catch (Exception e)
202 | {
203 | tran.Rollback();
204 | }
205 | }
206 | ```
207 | 高级用法
208 | ```
209 | //通过GetConnection可以使用更多dapper扩展的方法
210 | _unitOfWork.GetConnection().QueryAsync("select * from school");
211 | ```
212 | #### 写在最后
213 | Chimp核心是基于EF和Dapper的,所以EF和Dapper一切功能都可以使用。比如导航属性,字段映射等等。这个库是线上项目核心依赖,会长期更新维护,希望大家能提出更好的意见。
214 |
215 | #### 项目地址
216 | 数据库脚本在根目录的sqlscript文件夹里面
217 | [github地址](https://github.com/longxianghui/chimp.git)
218 |
219 | #### 快速找到作者
220 | QQ:240226543
221 | Emall: 240226543@qq.com
222 | 遇到紧急问题可以联系我
--------------------------------------------------------------------------------
/example/Leo.Chimp.Domain/ChimpDbContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace Leo.Chimp.Domain
7 | {
8 | public class ChimpDbContext : BaseDbContext
9 | {
10 | public ChimpDbContext(DbContextOptions options) : base(options)
11 | {
12 | }
13 | protected override void OnModelCreating(ModelBuilder modelBuilder)
14 | {
15 | base.OnModelCreating(modelBuilder);
16 | //your code
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Domain/Entities/School.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.Domain.Entities
6 | {
7 | public class School
8 | {
9 | public School()
10 | {
11 | Students = new List();
12 | }
13 |
14 | public Guid Id { get; set; }
15 | public string Name { get; set; }
16 |
17 | public List Students { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Domain/Entities/Student.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace Leo.Chimp.Domain.Entities
7 | {
8 | public class Student : IEntity
9 | {
10 | public Guid Id { get; set; }
11 | public string Name { get; set; }
12 | public Guid SchoolId { get; set; }
13 | public DateTime Birthday { get; set; }
14 |
15 | public School MySchool { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Domain/Leo.Chimp.Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Domain/Repositories/SchoolRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Leo.Chimp.Domain.Entities;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace Leo.Chimp.Domain.Repositories
8 | {
9 | public class SchoolRepository: EfCoreRepository,ISchoolRepository
10 | {
11 | public SchoolRepository(DbContext context) : base(context)
12 | {
13 | }
14 | }
15 | public interface ISchoolRepository : IRepository
16 | {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Domain/Repositories/StudentRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Leo.Chimp.Domain.Entities;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace Leo.Chimp.Domain.Repositories
8 | {
9 | public class StudentRepository: EfCoreRepository,IStudentRepository
10 | {
11 | public StudentRepository(DbContext context) : base(context)
12 | {
13 | }
14 | }
15 |
16 | public interface IStudentRepository : IRepository
17 | {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/Controllers/ValuesController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Leo.Chimp.Domain.Repositories;
6 | using Microsoft.AspNetCore.Mvc;
7 |
8 | namespace Leo.Chimp.Example.Controllers
9 | {
10 | [Route("api/[controller]")]
11 | [ApiController]
12 | public class ValuesController : ControllerBase
13 | {
14 | private readonly ISchoolRepository _schoolRepository;
15 | private readonly IUnitOfWork _unitOfWork;
16 | public ValuesController(ISchoolRepository schoolRepository, IUnitOfWork unitOfWork)
17 | {
18 | _schoolRepository = schoolRepository;
19 | _unitOfWork = unitOfWork;
20 | }
21 | [HttpGet]
22 | public async Task Get()
23 | {
24 | var schools = await _schoolRepository.TableNoTracking.ToPagedListAsync(1, 10);
25 | return Ok(schools);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/Leo.Chimp.Example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace Leo.Chimp.Example
12 | {
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | CreateWebHostBuilder(args).Build().Run();
18 | }
19 |
20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
21 | WebHost.CreateDefaultBuilder(args)
22 | .UseStartup();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:51399",
8 | "sslPort": 0
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "api/values",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "Leo.Chimp.Example": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "api/values",
24 | "applicationUrl": "http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Leo.Chimp.Domain;
6 | using Microsoft.AspNetCore.Builder;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.Extensions.Configuration;
11 | using Microsoft.Extensions.DependencyInjection;
12 | using Microsoft.Extensions.Logging;
13 | using Microsoft.Extensions.Options;
14 |
15 | namespace Leo.Chimp.Example
16 | {
17 | public class Startup
18 | {
19 | public Startup(IConfiguration configuration)
20 | {
21 | Configuration = configuration;
22 | }
23 |
24 | public IConfiguration Configuration { get; }
25 |
26 | // This method gets called by the runtime. Use this method to add services to the container.
27 | public void ConfigureServices(IServiceCollection services)
28 | {
29 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
30 | services.AddChimp(
31 | opt => opt.UseSqlServer("Server=10.0.0.99;Database=Chimp;Uid=sa;Pwd=Fuluerp123")
32 | );
33 | }
34 |
35 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
36 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
37 | {
38 | if (env.IsDevelopment())
39 | {
40 | app.UseDeveloperExceptionPage();
41 | }
42 |
43 | app.UseMvc();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/Leo.Chimp.Example/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/sqlscript/mssql/script.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/longxianghui/chimp/7c3c4f0878c9b780ad56ba82475c299d76e9801e/sqlscript/mssql/script.sql
--------------------------------------------------------------------------------
/sqlscript/mysql/script.sql:
--------------------------------------------------------------------------------
1 |
2 | -- ----------------------------
3 | -- Table structure for school
4 | -- ----------------------------
5 | DROP TABLE IF EXISTS `school`;
6 | CREATE TABLE `school` (
7 | `Id` char(36) NOT NULL,
8 | `Name` varchar(255) NOT NULL,
9 | PRIMARY KEY (`Id`)
10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
11 |
12 | -- ----------------------------
13 | -- Table structure for student
14 | -- ----------------------------
15 | DROP TABLE IF EXISTS `student`;
16 | CREATE TABLE `student` (
17 | `Id` char(36) NOT NULL,
18 | `Name` varchar(255) DEFAULT NULL,
19 | `SchoolId` char(36) DEFAULT NULL,
20 | `Birthday` datetime DEFAULT NULL,
21 | PRIMARY KEY (`Id`) USING BTREE
22 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
23 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/BaseDbContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.DependencyModel;
7 |
8 | namespace Leo.Chimp
9 | {
10 | ///
11 | /// default DbContext
12 | ///
13 | public class BaseDbContext : DbContext
14 | {
15 | protected BaseDbContext()
16 | {
17 | }
18 |
19 | public BaseDbContext(DbContextOptions options) : base(options)
20 | {
21 | }
22 |
23 | protected override void OnModelCreating(ModelBuilder modelBuilder)
24 | {
25 | var assemblies = GetCurrentPathAssembly();
26 | foreach (var assembly in assemblies)
27 | {
28 | var entityTypes = assembly.GetTypes()
29 | .Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
30 | .Where(type => type.IsClass)
31 | .Where(type => type.BaseType != null)
32 | .Where(type => typeof(IEntity).IsAssignableFrom(type));
33 |
34 | foreach (var entityType in entityTypes)
35 | {
36 | if (modelBuilder.Model.FindEntityType(entityType) != null)
37 | continue;
38 | modelBuilder.Model.AddEntityType(entityType);
39 | }
40 | }
41 | base.OnModelCreating(modelBuilder);
42 | }
43 |
44 |
45 | private List GetCurrentPathAssembly()
46 | {
47 | var dlls = DependencyContext.Default.CompileLibraries
48 | .Where(x => !x.Name.StartsWith("Microsoft") && !x.Name.StartsWith("System"))
49 | .ToList();
50 | var list = new List();
51 | if (dlls.Any())
52 | {
53 | foreach (var dll in dlls)
54 | {
55 | if (dll.Type == "project")
56 | {
57 | list.Add(Assembly.Load(dll.Name));
58 | }
59 | }
60 | }
61 | return list;
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/src/Leo.Chimp/DapperAdapter/ISqlAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.DapperAdapter
6 | {
7 | public interface ISqlAdapter
8 | {
9 | ///
10 | /// Builds an SQL query suitable for performing page based queries to the database
11 | ///
12 | /// partedSql
13 | /// Arguments to any embedded parameters in the SQL query
14 | /// The number of rows that should be skipped by the query
15 | /// The number of rows that should be retruend by the query
16 | string PagingBuild(ref PartedSql partedSql, object sqlArgs, long skip, long take);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/DapperAdapter/MysqlAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.DapperAdapter
6 | {
7 | public class MysqlAdapter : ISqlAdapter
8 | {
9 | public virtual string PagingBuild(ref PartedSql partedSql, object args, long skip, long take)
10 | {
11 | var pageSql = $"{partedSql.Raw} LIMIT {take} OFFSET {skip}";
12 | return pageSql;
13 | }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/DapperAdapter/PagingUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace Leo.Chimp.DapperAdapter
7 | {
8 | public class PagingUtil
9 | {
10 | //private static readonly Regex _rexSelect = new Regex(@"^\s*SELECT\s+(.+?)\sFROM\s", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
11 | private static readonly Regex _rexSelect = new Regex(@"^\s*SELECT\s+(.+?)\sFROM\s", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
12 |
13 | private static readonly Regex _rexSelect1 = new Regex(@"^\s*SELECT\s+(.+?)\sFROM\s*\(+\s*", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
14 | private static readonly Regex _rexOrderBy = new Regex(@"\s+ORDER\s+BY\s+(([^\s]+(?:\s+ASC|\s+DESC)?)+)\s*$", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
15 |
16 | ///
17 | /// 分割SQL
18 | ///
19 | public static PartedSql SplitSql(string sql)
20 | {
21 | var parts = new PartedSql { Raw = sql };
22 |
23 | // Extract the sql from "SELECT FROM"
24 | var s = _rexSelect1.Match(sql);
25 | if (s.Success)
26 | {
27 | parts.Select = s.Groups[1].Value;
28 |
29 |
30 | sql = sql.Substring(s.Length);
31 | s = _rexOrderBy.Match(sql);
32 | if (s.Success)
33 | {
34 | sql = sql.Substring(0, s.Index);
35 | parts.OrderBy = s.Groups[1].Value;
36 | }
37 | parts.Body = "("+sql;
38 |
39 | return parts;
40 | }
41 |
42 | var m = _rexSelect.Match(sql);
43 | if (!m.Success)
44 | throw new ArgumentException("Unable to parse SQL statement for select");
45 | parts.Select = m.Groups[1].Value;
46 |
47 |
48 | sql = sql.Substring(m.Length);
49 | m = _rexOrderBy.Match(sql);
50 | if (m.Success)
51 | {
52 | sql = sql.Substring(0, m.Index);
53 | parts.OrderBy = m.Groups[1].Value;
54 | }
55 | parts.Body = sql;
56 |
57 | return parts;
58 | }
59 |
60 | public static string GetCountSql(PartedSql sql)
61 | {
62 | return $"SELECT COUNT(*) FROM {sql.Body}";
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/DapperAdapter/PartedSql.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.DapperAdapter
6 | {
7 | ///
8 | /// sql info
9 | ///
10 | public struct PartedSql
11 | {
12 | public string Raw;
13 | ///
14 | /// select eg:distinct Id, name
15 | ///
16 | public string Select;
17 |
18 | ///
19 | /// body eg:tabName where Id = 123
20 | ///
21 | public string Body;
22 |
23 | ///
24 | /// order by eg: Id Asc
25 | ///
26 | public string OrderBy;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/DapperAdapter/SqlServerAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.DapperAdapter
6 | {
7 | public class SqlServerAdapter : ISqlAdapter
8 | {
9 | public virtual string PagingBuild(ref PartedSql partedSql, object args, long skip, long take)
10 | {
11 | if (string.IsNullOrEmpty(partedSql.OrderBy))
12 | throw new InvalidOperationException("miss order by");
13 |
14 | var hasDistinct = partedSql.Select.IndexOf("DISTINCT", StringComparison.OrdinalIgnoreCase) == 0;
15 | var select = "SELECT";
16 | if (hasDistinct)
17 | {
18 | partedSql.Select = partedSql.Select.Substring("DISTINCT".Length);
19 | select = "SELECT DISTINCT";
20 | }
21 | if (skip <= 0)
22 | {
23 | var sbSql = StringBuilderCache.Allocate().AppendFormat("{0} TOP {1} {2}", select, take, partedSql.Select)
24 | .Append(" FROM ").Append(partedSql.Body).Append(" order by ").Append(partedSql.OrderBy);
25 | return StringBuilderCache.ReturnAndFree(sbSql);
26 | }
27 | else
28 | {
29 | var sbSql = StringBuilderCache.Allocate()
30 | .AppendFormat("SELECT * FROM (SELECT {0}, ROW_NUMBER() OVER " +
31 | "(order by {1}) As RowNum FROM {2}) AS RowConstrainedResult " +
32 | "WHERE RowNum > {3} AND RowNum <= {4}",
33 | partedSql.Select, partedSql.OrderBy, partedSql.Body, skip, skip + take);
34 |
35 | return StringBuilderCache.ReturnAndFree(sbSql);
36 | }
37 | }
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/DapperAdapter/StringBuilderCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.DapperAdapter
6 | {
7 | public static class StringBuilderCache
8 | {
9 | [ThreadStatic]
10 | private static StringBuilder _cache;
11 |
12 | public static StringBuilder Allocate()
13 | {
14 | var sb = _cache;
15 | if (sb == null)
16 | return new StringBuilder();
17 |
18 | sb.Length = 0;
19 | _cache = null;
20 | return sb;
21 | }
22 |
23 | public static void Free(StringBuilder sb)
24 | {
25 | _cache = sb;
26 | }
27 |
28 | public static string ReturnAndFree(StringBuilder sb)
29 | {
30 | var str = sb.ToString();
31 | _cache = sb;
32 | return str;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/EfCoreRepository.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.ChangeTracking;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Data;
6 | using System.Linq;
7 | using System.Linq.Expressions;
8 | using System.Threading.Tasks;
9 |
10 | namespace Leo.Chimp
11 | {
12 |
13 |
14 | ///
15 | /// EfCoreRepository
16 | ///
17 | ///
18 | public class EfCoreRepository : IRepository
19 | where TEntity : class
20 | {
21 | private readonly DbContext _context;
22 | private DbSet _entities;
23 | public EfCoreRepository(DbContext context)
24 | {
25 | _context = context;
26 | }
27 |
28 | public TEntity GetById(object id)
29 | {
30 | if (id == null)
31 | throw new ArgumentNullException("id");
32 |
33 | return Entities.Find(id);
34 | }
35 |
36 | #if NETSTANDARD2_0
37 | public Task GetByIdAsync(object id)
38 | {
39 | if (id == null)
40 | throw new ArgumentNullException("id");
41 |
42 | return Entities.FindAsync(id);
43 | }
44 | #endif
45 | #if NETSTANDARD2_1 || NET6_0
46 | public Task GetByIdAsync(object id)
47 | {
48 | if (id == null)
49 | throw new ArgumentNullException("id");
50 |
51 | return Entities.FindAsync(id).AsTask();
52 | }
53 | #endif
54 |
55 | public void Insert(TEntity entity)
56 | {
57 | if (entity == null)
58 | throw new ArgumentNullException("entity");
59 |
60 | Entities.Add(entity);
61 | }
62 |
63 |
64 | public void Insert(IEnumerable entities)
65 | {
66 | if (!entities.Any())
67 | throw new ArgumentNullException("entities");
68 |
69 | Entities.AddRange(entities);
70 | }
71 | #if NETSTANDARD2_0
72 | public Task InsertAsync(TEntity entity)
73 | {
74 | if (entity == null)
75 | throw new ArgumentNullException("entity");
76 |
77 | return Entities.AddAsync(entity);
78 | }
79 | #endif
80 | #if NETSTANDARD2_1 || NET6_0
81 | public Task InsertAsync(TEntity entity)
82 | {
83 | if (entity == null)
84 | throw new ArgumentNullException("entity");
85 |
86 | return Entities.AddAsync(entity).AsTask();
87 |
88 | }
89 | #endif
90 |
91 | public Task InsertAsync(IEnumerable entities)
92 | {
93 | if (!entities.Any())
94 | throw new ArgumentNullException("entities");
95 |
96 | return Entities.AddRangeAsync(entities);
97 | }
98 |
99 | public void Update(TEntity entity)
100 | {
101 | if (entity == null)
102 | throw new ArgumentNullException("entity");
103 |
104 | Entities.Attach(entity);
105 | _context.Update(entity);
106 | }
107 |
108 | public void Update(IEnumerable entities)
109 | {
110 | if (!entities.Any())
111 | throw new ArgumentNullException("entities");
112 |
113 | _context.UpdateRange(entities);
114 |
115 | }
116 |
117 |
118 | string GetPropertyName(string str)
119 | {
120 | return str.Split(',')[0].Split('.')[1];
121 | }
122 |
123 | public void Delete(TEntity entity)
124 | {
125 | if (entity == null)
126 | throw new ArgumentNullException("entity");
127 |
128 | _context.Remove(entity);
129 | }
130 |
131 |
132 | public void Delete(IEnumerable entities)
133 | {
134 | if (!entities.Any())
135 | throw new ArgumentNullException("entities");
136 |
137 | _context.RemoveRange(entities);
138 |
139 | }
140 |
141 | public void Delete(Expression> predicate)
142 | {
143 | if (predicate == null)
144 | throw new ArgumentNullException("predicate");
145 | _context.RemoveRange(Entities.Where(predicate));
146 | }
147 |
148 | #region Properties
149 |
150 | ///
151 | /// Gets a table
152 | ///
153 | public virtual IQueryable Table => Entities;
154 |
155 | ///
156 | /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
157 | ///
158 | public virtual IQueryable TableNoTracking => Entities.AsNoTracking();
159 |
160 | ///
161 | /// Gets an entity set
162 | ///
163 | protected virtual DbSet Entities => _entities ?? (_entities = _context.Set());
164 |
165 | #endregion
166 | private void AttachIfNot(TEntity entity)
167 | {
168 | var entry = _context.ChangeTracker.Entries().FirstOrDefault(ent => ent.Entity == entity);
169 | if (entry != null)
170 | {
171 | return;
172 | }
173 | _context.Attach(entity);
174 | }
175 |
176 |
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/Extensions.Type.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using Microsoft.Extensions.DependencyModel;
7 |
8 | namespace Leo.Chimp
9 | {
10 | public static class TypeExtensions
11 | {
12 | ///
13 | /// GetNonSystemAssembly
14 | ///
15 | ///
16 | ///
17 | public static List GetNonSystemAssembly(this AppDomain domain)
18 | {
19 | var allAssemblies = domain.GetAssemblies();
20 | return allAssemblies.Where(x => !x.FullName.StartsWith("Microsoft") && !x.FullName.StartsWith("System"))
21 | .ToList();
22 | }
23 |
24 | public static List GetCurrentPathAssembly(this AppDomain domain)
25 | {
26 | var dlls = DependencyContext.Default.CompileLibraries
27 | .Where(x => !x.Name.StartsWith("Microsoft") && !x.Name.StartsWith("System"))
28 | .ToList();
29 | var list = new List();
30 | if (dlls.Any())
31 | {
32 | foreach (var dll in dlls)
33 | {
34 | if (dll.Type == "project")
35 | {
36 | list.Add(Assembly.Load(dll.Name));
37 | }
38 | }
39 | }
40 | return list;
41 | }
42 |
43 |
44 | public static bool HasImplementedRawGeneric(this Type type, Type generic)
45 | {
46 | if (type == null) throw new ArgumentNullException(nameof(type));
47 | if (generic == null) throw new ArgumentNullException(nameof(generic));
48 | var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
49 | if (isTheRawGenericType) return true;
50 | while (type != null && type != typeof(object))
51 | {
52 | isTheRawGenericType = IsTheRawGenericType(type);
53 | if (isTheRawGenericType) return true;
54 | type = type.BaseType;
55 | }
56 | return false;
57 |
58 | bool IsTheRawGenericType(Type test)
59 | => generic == (test.IsGenericType ? test.GetGenericTypeDefinition() : test);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/IEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Leo.Chimp
2 | {
3 | ///
4 | /// IEntity
5 | ///
6 | public interface IEntity
7 | {
8 |
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Leo.Chimp/IPagedList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp
6 | {
7 | public interface IPagedList
8 | {
9 | int Current { get; }
10 |
11 | int PageSize { get; }
12 |
13 | int Total { get; }
14 |
15 | int PageTotal { get; }
16 |
17 | IEnumerable Item { get; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/IRepository.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Linq.Expressions;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Microsoft.EntityFrameworkCore.Query;
10 | using Microsoft.EntityFrameworkCore.ChangeTracking;
11 |
12 | namespace Leo.Chimp
13 | {
14 | ///
15 | /// This interface is implemented by all repositories to ensure implementation of fixed methods.
16 | ///
17 | /// Main Entity type this repository works on
18 | public interface IRepository where TEntity : class
19 | {
20 |
21 | #region Select/Get/Query
22 | ///
23 | /// GetById
24 | ///
25 | /// 主键
26 | /// Entity
27 | TEntity GetById(object id);
28 |
29 | ///
30 | /// GetByIdAsync
31 | ///
32 | /// 主键
33 | /// Entity
34 | Task GetByIdAsync(object id);
35 |
36 | ///
37 | /// Table Tracking
38 | ///
39 | IQueryable Table { get; }
40 |
41 | ///
42 | /// TableNoTracking
43 | ///
44 | IQueryable TableNoTracking { get; }
45 |
46 | #endregion
47 |
48 | #region Insert
49 | ///
50 | /// Insert
51 | ///
52 | /// Inserted entity
53 | void Insert(TEntity entity);
54 |
55 | ///
56 | /// Inserts
57 | ///
58 | /// Inserted entity
59 | void Insert(IEnumerable entities);
60 |
61 | ///
62 | /// 添加实体
63 | ///
64 | /// Inserted entity
65 | Task InsertAsync(TEntity entity);
66 |
67 | ///
68 | /// InsertAsync
69 | ///
70 | /// Inserted entity
71 | Task InsertAsync(IEnumerable entities);
72 | #endregion
73 |
74 | #region Update
75 | ///
76 | /// Update
77 | ///
78 | /// Entity
79 | void Update(TEntity entity);
80 |
81 | ///
82 | /// Updates
83 | ///
84 | ///
85 | void Update(IEnumerable entities);
86 |
87 | #endregion
88 |
89 | #region Delete
90 | ///
91 | /// Delete
92 | ///
93 | /// Entity to be deleted
94 | void Delete(TEntity entity);
95 |
96 |
97 | ///
98 | /// Deletes
99 | ///
100 | ///
101 | void Delete(IEnumerable entities);
102 |
103 |
104 | ///
105 | /// Delete by lambda
106 | ///
107 | /// A condition to filter entities
108 | void Delete(Expression> predicate);
109 | #endregion
110 |
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/IUnitOfWork.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Threading.Tasks;
5 | using Microsoft.EntityFrameworkCore.Storage;
6 |
7 | namespace Leo.Chimp
8 | {
9 | ///
10 | /// Defines the interface(s) for unit of work.
11 | ///
12 | public interface IUnitOfWork : IDisposable
13 | {
14 | ///
15 | /// Saves all changes made in this context to the database.
16 | ///
17 | /// The number of state entries written to the database.
18 | int SaveChanges();
19 |
20 | ///
21 | /// Asynchronously saves all changes made in this unit of work to the database.
22 | ///
23 | /// A that represents the asynchronous save operation. The task result contains the number of state entities written to database.
24 | Task SaveChangesAsync();
25 |
26 | #region command sql
27 |
28 | ///
29 | /// QueryAsync
30 | /// ag:await _unitOfWork.QueryAsync`Demo`("select id,name from school where id = @id", new { id = 1 });
31 | ///
32 | ///
33 | /// sql语句
34 | /// 参数
35 | ///
36 | ///
37 | Task> QueryAsync(string sql, object param = null, IDbContextTransaction trans = null) where TEntity : class;
38 |
39 | ///
40 | /// ExecuteAsync
41 | /// ag:await _unitOfWork.ExecuteAsync("update school set name =@name where id =@id", new { name = "", id=1 });
42 | ///
43 | ///
44 | ///
45 | ///
46 | ///
47 | Task ExecuteAsync(string sql, object param, IDbContextTransaction trans = null);
48 |
49 | ///
50 | /// QueryPagedListAsync, complex sql, use "select * from (your sql) b"
51 | ///
52 | ///
53 | ///
54 | ///
55 | ///
56 | ///
57 | ///
58 | Task> QueryPagedListAsync(int pageIndex, int pageSize, string pageSql, object pageSqlArgs = null)
59 | where TEntity : class;
60 | #endregion
61 |
62 | #region Transaction
63 | ///
64 | /// BeginTransaction
65 | ///
66 | ///
67 | IDbContextTransaction BeginTransaction();
68 |
69 | #endregion
70 | ///
71 | /// get connection
72 | ///
73 | ///
74 | IDbConnection GetConnection();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/KingOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp
6 | {
7 | public class KingOptions
8 | {
9 | ///
10 | /// 数据库类型,目前只支持mssql和mysql
11 | ///
12 | public DbType DbType { get; set; }
13 | ///
14 | /// 链接字符串
15 | ///
16 | public string ConnectionString { get; set; }
17 | }
18 |
19 | public enum DbType
20 | {
21 | MSSQL,
22 | MYSQL
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/Leo.Chimp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;netstandard2.1;net6.0;
5 | https://github.com/longxianghui/chimp
6 | 7.3
7 | 2.1.2
8 | longxianghui
9 | ef and dapper repository.
10 | repository;unit of work;ef;dapper;
11 | https://github.com/longxianghui/chimp
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/PagedList.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace Leo.Chimp
7 | {
8 | ///
9 | /// 分页列表
10 | ///
11 | /// The type of the data to page
12 | public class PagedList: IPagedList
13 | {
14 | public PagedList()
15 | {
16 |
17 | }
18 | public PagedList(IList items, int pageIndex, int pageSize, int totalCount)
19 | {
20 | Current = pageIndex;
21 | PageSize = pageSize;
22 | Total = totalCount;
23 | PageTotal = (int)Math.Ceiling(totalCount / (double)pageSize);
24 | Item = items;
25 | }
26 |
27 | //internal PagedList() { }
28 |
29 | public int Current { get; set; }
30 |
31 | public int PageSize { get; set; }
32 |
33 | public int Total { get; set; }
34 |
35 | public int PageTotal { get; set; }
36 |
37 | public IEnumerable Item { get; set; }
38 |
39 | public static PagedList Create(IPagedList source)
40 | {
41 | if (source is PagedList same)
42 | return same;
43 | return new PagedList(source.Item.ToList(), source.Current, source.PageSize, source.Total);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/PagedListExtensions.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace Leo.Chimp
10 | {
11 |
12 | public static class PagedListExtensions
13 | {
14 | ///
15 | /// PagedList
16 | ///
17 | ///
18 | ///
19 | /// 1为起始页
20 | ///
21 | ///
22 | ///
23 | public static async Task> ToPagedListAsync(
24 | this IQueryable query,
25 | int pageIndex,
26 | int pageSize,
27 | CancellationToken cancellationToken = default)
28 | {
29 | if (pageIndex < 1)
30 | {
31 | throw new ArgumentOutOfRangeException(nameof(pageIndex));
32 | }
33 |
34 | int realIndex = pageIndex - 1;
35 |
36 | int count = await query.CountAsync(cancellationToken).ConfigureAwait(false);
37 | List items = await query.Skip(realIndex * pageSize)
38 | .Take(pageSize).ToListAsync(cancellationToken).ConfigureAwait(false);
39 |
40 | return new PagedList(items, pageIndex, pageSize, count);
41 | }
42 | public static PagedList ToPagedList(
43 | this IQueryable query,
44 | int pageIndex,
45 | int pageSize,
46 | CancellationToken cancellationToken = default)
47 | {
48 | if (pageIndex < 1)
49 | {
50 | throw new ArgumentOutOfRangeException(nameof(pageIndex));
51 | }
52 | int realIndex = pageIndex - 1;
53 | int count = query.Count();
54 | List items = query.Skip(realIndex * pageSize)
55 | .Take(pageSize).ToList();
56 |
57 | return new PagedList(items, pageIndex, pageSize, count);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Leo.Chimp/ServiceCollectionExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using Leo.Chimp;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 |
10 |
11 | namespace Microsoft.Extensions.DependencyInjection
12 | {
13 | public static class ServiceCollectionExtension
14 | {
15 | public static IServiceCollection AddChimp(this IServiceCollection services,
16 | Action options)
17 | {
18 | services.AddDbContext(options);
19 | services.AddScoped();
20 | AddDefault(services);
21 | return services;
22 | }
23 | public static IServiceCollection AddChimp(this IServiceCollection services,
24 | Action options) where T : BaseDbContext
25 | {
26 | services.AddDbContext(options);
27 | services.AddScoped();
28 | AddDefault(services);
29 | return services;
30 | }
31 |
32 |
33 | #if NETSTANDARD2_1 || NET6_0
34 | public static DbContextOptionsBuilder UseMySql(this DbContextOptionsBuilder optionsBuilder, string connectionString)
35 | {
36 | optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
37 | return optionsBuilder;
38 | }
39 | #endif
40 |
41 | #region private function
42 | private static void AddDefault(IServiceCollection services)
43 | {
44 | services.AddScoped();
45 | services.AutoDi(typeof(IRepository<>));
46 | services.AddScoped(typeof(IRepository<>), typeof(EfCoreRepository<>));
47 | }
48 | //auto di
49 | private static IServiceCollection AutoDi(this IServiceCollection services, Type baseType)
50 | {
51 | var allAssemblies = AppDomain.CurrentDomain.GetCurrentPathAssembly();
52 | foreach (var assembly in allAssemblies)
53 | {
54 | var types = assembly.GetTypes()
55 | .Where(type => type.IsClass
56 | && type.BaseType != null
57 | && type.HasImplementedRawGeneric(baseType));
58 | foreach (var type in types)
59 | {
60 | var interfaces = type.GetInterfaces();
61 |
62 | var interfaceType = interfaces.FirstOrDefault(x => x.Name == $"I{type.Name}");
63 | if (interfaceType == null)
64 | {
65 | interfaceType = type;
66 | }
67 | ServiceDescriptor serviceDescriptor =
68 | new ServiceDescriptor(interfaceType, type, ServiceLifetime.Scoped);
69 | if (!services.Contains(serviceDescriptor))
70 | {
71 | services.Add(serviceDescriptor);
72 | }
73 | }
74 | }
75 |
76 | return services;
77 | }
78 |
79 |
80 |
81 | }
82 | #endregion
83 | }
--------------------------------------------------------------------------------
/src/Leo.Chimp/UnitOfWork.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using Dapper;
8 | using Leo.Chimp.DapperAdapter;
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.EntityFrameworkCore.Storage;
11 | using Microsoft.Extensions.Options;
12 |
13 | namespace Leo.Chimp
14 | {
15 | ///
16 | /// Represents the default implementation of the
17 | ///
18 | public class UnitOfWork : IUnitOfWork
19 | {
20 | private readonly DbContext _context;
21 | private bool _disposed = false;
22 |
23 | ///
24 | /// Initializes a new instance of the
25 | ///
26 | /// The context.
27 | public UnitOfWork(DbContext context)
28 | {
29 | _context = context;
30 | }
31 |
32 |
33 |
34 | public int SaveChanges()
35 | {
36 | return _context.SaveChanges();
37 | }
38 |
39 |
40 | public Task SaveChangesAsync()
41 | {
42 | return _context.SaveChangesAsync();
43 | }
44 |
45 |
46 | public Task> QueryAsync(string sql, object param = null, IDbContextTransaction trans = null) where TEntity : class
47 | {
48 | var conn = GetConnection();
49 | return conn.QueryAsync(sql, param, trans?.GetDbTransaction());
50 |
51 | }
52 |
53 |
54 | public async Task ExecuteAsync(string sql, object param, IDbContextTransaction trans = null)
55 | {
56 | var conn = GetConnection();
57 | return await conn.ExecuteAsync(sql, param, trans?.GetDbTransaction());
58 |
59 | }
60 |
61 |
62 |
63 | public async Task> QueryPagedListAsync(int pageIndex, int pageSize, string pageSql, object pageSqlArgs = null) where TEntity : class
64 | {
65 | if (pageSize < 1 || pageSize > 5000)
66 | throw new ArgumentOutOfRangeException(nameof(pageSize));
67 | if (pageIndex < 1)
68 | throw new ArgumentOutOfRangeException(nameof(pageIndex));
69 |
70 | var partedSql = PagingUtil.SplitSql(pageSql);
71 | ISqlAdapter sqlAdapter = null;
72 | if (_context.Database.IsMySql())
73 | sqlAdapter = new MysqlAdapter();
74 | if (_context.Database.IsSqlServer())
75 | sqlAdapter = new SqlServerAdapter();
76 | if (sqlAdapter == null)
77 | throw new Exception("Unsupported database type");
78 | pageSql = sqlAdapter.PagingBuild(ref partedSql, pageSqlArgs, (pageIndex - 1) * pageSize, pageSize);
79 | var sqlCount = PagingUtil.GetCountSql(partedSql);
80 | var conn = GetConnection();
81 | var totalCount = await conn.ExecuteScalarAsync(sqlCount, pageSqlArgs);
82 | var items = await conn.QueryAsync(pageSql, pageSqlArgs);
83 | var pagedList = new PagedList(items.ToList(), pageIndex - 1, pageSize, totalCount);
84 | return pagedList;
85 | }
86 |
87 |
88 | public IDbContextTransaction BeginTransaction()
89 | {
90 | return _context.Database.BeginTransaction();
91 | }
92 |
93 |
94 |
95 | ///
96 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
97 | ///
98 | public void Dispose()
99 | {
100 | Dispose(true);
101 | GC.SuppressFinalize(this);
102 | }
103 |
104 |
105 | protected virtual void Dispose(bool disposing)
106 | {
107 | if (!_disposed)
108 | {
109 | if (disposing)
110 | {
111 | _context.Dispose();
112 | }
113 | }
114 | _disposed = true;
115 | }
116 |
117 | public IDbConnection GetConnection()
118 | {
119 | return _context.Database.GetDbConnection();
120 | }
121 | }
122 |
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/ChimpDbContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace Leo.Chimp.Test
7 | {
8 | public class ChimpDbContext : BaseDbContext
9 | {
10 | public ChimpDbContext(DbContextOptions options) : base(options)
11 | {
12 | }
13 |
14 | protected override void OnModelCreating(ModelBuilder modelBuilder)
15 | {
16 | base.OnModelCreating(modelBuilder);
17 | //your code
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/ChimpDbContextTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Leo.Chimp.Test.Entities;
5 | using Leo.Chimp.Test.Repositories;
6 | using Microsoft.EntityFrameworkCore;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Xunit;
9 |
10 | namespace Leo.Chimp.Test
11 | {
12 |
13 | public class ChimpDbContextTest
14 | {
15 | private readonly IUnitOfWork _unitOfWork;
16 | private readonly ISchoolRepository _schoolRepository;
17 | public ChimpDbContextTest()
18 | {
19 | var services = new ServiceCollection();
20 |
21 | services.AddChimp(opt =>
22 | {
23 | opt.UseMySql("server = 10.0.0.146;database=chimp;uid=root;password=123456;");
24 | });
25 | var sp = services.BuildServiceProvider();
26 | _unitOfWork = sp.GetRequiredService();
27 | _schoolRepository = sp.GetRequiredService();
28 | }
29 |
30 | [Fact]
31 | public School Insert()
32 | {
33 | var school = new School
34 | {
35 | Name = Guid.NewGuid().ToString(),
36 | Id = Guid.NewGuid()
37 | };
38 | _schoolRepository.Insert(school);
39 | _unitOfWork.SaveChanges();
40 | return school;
41 | }
42 |
43 | [Fact]
44 | public void GetById()
45 | {
46 | var newSchool = Insert();
47 | var school = _schoolRepository.GetById(newSchool.Id);
48 | Assert.True(school != null);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/DapperTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Dapper;
7 | using Leo.Chimp.Test.Entities;
8 | using Leo.Chimp.Test.Repositories;
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.Extensions.DependencyInjection;
11 | using Xunit;
12 |
13 | namespace Leo.Chimp.Test
14 | {
15 | public class DapperTest
16 | {
17 | private readonly IUnitOfWork _unitOfWork;
18 | private readonly ISchoolRepository _schoolRepository;
19 |
20 | public DapperTest()
21 | {
22 | var services = new ServiceCollection();
23 | services.AddChimp(opt => { opt.UseMySql("server = 10.0.0.146;database=chimp;uid=root;password=123456;SslMode=none;"); });
24 | //services.AddChimp(opt => { opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123"); });
25 | var sp = services.BuildServiceProvider();
26 | _unitOfWork = sp.GetRequiredService();
27 | _schoolRepository = sp.GetRequiredService();
28 | }
29 |
30 | [Fact]
31 | public async Task QueryAsync()
32 | {
33 | var schools = await _unitOfWork.QueryAsync("select * from school");
34 | Assert.True(schools.Any());
35 | }
36 |
37 | [Fact]
38 | public async Task QueryPagedListAsync()
39 | {
40 | var schools = await _unitOfWork.QueryPagedListAsync(1, 10, "select * from school order by id");
41 | Assert.True(schools.Item.Any());
42 | }
43 |
44 | [Fact]
45 | public async Task ExecuteAsync()
46 | {
47 | var school = new School
48 | {
49 | Id = Guid.NewGuid(),
50 | Name = "school"
51 | };
52 |
53 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
54 | school);
55 |
56 | var newSchool = await _unitOfWork.QueryAsync("select * from school where id =@id",
57 | new { Id = school.Id });
58 |
59 | Assert.True(school.Name == newSchool.First().Name);
60 | }
61 |
62 | [Fact]
63 | public async Task GetConnection()
64 | {
65 | //可以直接使用dapper
66 | var schools = await _unitOfWork.GetConnection().QueryAsync("select * from school");
67 | Assert.True(schools.Any());
68 | }
69 |
70 | [Fact]
71 | public async Task TransactionUseSaveChange()
72 | {
73 | var school1 = new School
74 | {
75 | Id = Guid.NewGuid(),
76 | Name = "school1"
77 | };
78 |
79 | var school2 = new School
80 | {
81 | Id = Guid.NewGuid(),
82 | Name = "school2"
83 | };
84 |
85 | await _schoolRepository.InsertAsync(school1);
86 | await _schoolRepository.InsertAsync(school2);
87 | await _unitOfWork.SaveChangesAsync();
88 |
89 | var newSchool1 = await _unitOfWork.QueryAsync("select * from school where id =@Id",
90 | new { Id = school1.Id });
91 | var newSchool2 = await _unitOfWork.QueryAsync("select * from school where id =@Id",
92 | new { Id = school2.Id });
93 | Assert.True(newSchool1.Any() && newSchool2.Any());
94 |
95 | }
96 |
97 | [Fact]
98 | public async Task Transaction()
99 | {
100 | var school1 = new School
101 | {
102 | Id = Guid.NewGuid(),
103 | Name = "school1"
104 | };
105 |
106 | var school2 = new School
107 | {
108 | Id = Guid.NewGuid(),
109 | Name = "school2"
110 | };
111 |
112 | using (var tran = _unitOfWork.BeginTransaction())
113 | {
114 | try
115 | {
116 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
117 | school1, tran);
118 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
119 | school2, tran);
120 | throw new Exception();
121 | tran.Commit();
122 | }
123 | catch (Exception e)
124 | {
125 | tran.Rollback();
126 | }
127 | }
128 | var newSchool1 = await _unitOfWork.QueryAsync("select * from school where id =@Id",
129 | new { Id = school1.Id });
130 | var newSchool2 = await _unitOfWork.QueryAsync("select * from school where id =@Id",
131 | new { Id = school2.Id });
132 | Assert.False(newSchool1.Any() || newSchool2.Any());
133 |
134 | }
135 |
136 | [Fact]
137 | public async Task HybridTransaction()
138 | {
139 | var school1 = new School
140 | {
141 | Id = Guid.NewGuid(),
142 | Name = "school1"
143 | };
144 |
145 | var school2 = new School
146 | {
147 | Id = Guid.NewGuid(),
148 | Name = "school2"
149 | };
150 | using (var tran = _unitOfWork.BeginTransaction())
151 | {
152 | try
153 | {
154 | await _schoolRepository.InsertAsync(school1);
155 | await _unitOfWork.SaveChangesAsync();
156 |
157 | await _unitOfWork.ExecuteAsync("insert school(id,name) values(@Id,@Name)",
158 | school2, tran);
159 | throw new Exception();
160 | tran.Commit();
161 | }
162 | catch (Exception e)
163 | {
164 | tran.Rollback();
165 | }
166 | }
167 | var newSchool1 = await _unitOfWork.QueryAsync("select * from school where id =@id",
168 | new { Id = school1.Id });
169 | var newSchool2 = await _unitOfWork.QueryAsync("select * from school where id =@id",
170 | new { Id = school2.Id });
171 | Assert.False(newSchool1.Any() || newSchool2.Any());
172 | }
173 |
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/EfRepositoryTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Leo.Chimp.Test.Entities;
8 | using Leo.Chimp.Test.Repositories;
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.Extensions.DependencyInjection;
11 | using Xunit;
12 |
13 | namespace Leo.Chimp.Test
14 | {
15 | public class EfRepositoryTest
16 | {
17 |
18 | private readonly IUnitOfWork _unitOfWork;
19 | private readonly ISchoolRepository _schoolRepository;
20 |
21 | public EfRepositoryTest()
22 | {
23 | var services = new ServiceCollection();
24 |
25 | services.AddChimp(opt =>
26 | {
27 | opt.UseMySql("server = 10.0.0.146;database=chimp;uid=root;password=123456;");
28 | });
29 |
30 | //services.AddChimp(opt =>
31 | //{
32 | // opt.UseSqlServer("Server=10.0.0.99;Database=chimp;Uid=sa;Pwd=Fuluerp123");
33 | //});
34 | var sp = services.BuildServiceProvider();
35 | _unitOfWork = sp.GetRequiredService();
36 | _schoolRepository = sp.GetRequiredService();
37 | }
38 |
39 |
40 | [Fact]
41 | public School Insert()
42 | {
43 | var school = new School
44 | {
45 | Name = Guid.NewGuid().ToString(),
46 | Id = Guid.NewGuid()
47 | };
48 | _schoolRepository.Insert(school);
49 | _unitOfWork.SaveChanges();
50 | return school;
51 | }
52 |
53 | [Fact]
54 | public void GetById()
55 | {
56 | var newSchool = Insert();
57 | var school = _schoolRepository.GetById(newSchool.Id);
58 | Assert.True(school != null);
59 | }
60 |
61 | [Fact]
62 | public void PagedList()
63 | {
64 | var schools = _schoolRepository.TableNoTracking.ToPagedList(1, 10);
65 | Assert.True(schools != null);
66 | }
67 |
68 | [Fact]
69 | public async Task PagedListAsync()
70 | {
71 | var schools = await _schoolRepository.TableNoTracking.ToPagedListAsync(1, 10);
72 | Assert.True(schools != null);
73 | }
74 |
75 | [Fact]
76 | public List Inserts()
77 | {
78 | var schools = new List();
79 | for (int i = 0; i < 100; i++)
80 | {
81 | var school = new School
82 | {
83 | Id = Guid.NewGuid(),
84 | Name = Guid.NewGuid().ToString()
85 | };
86 | schools.Add(school);
87 | }
88 | _schoolRepository.Insert(schools);
89 | _unitOfWork.SaveChanges();
90 |
91 | foreach (var item in schools)
92 | {
93 | var school = _schoolRepository.GetById(item.Id);
94 | if (school == null)
95 | {
96 | Assert.True(false);
97 | }
98 | }
99 | return schools;
100 | }
101 | [Fact]
102 | public async Task InsertAsync()
103 | {
104 | var school = new School
105 | {
106 | Id = Guid.NewGuid(),
107 | Name = Guid.NewGuid().ToString()
108 | };
109 | await _schoolRepository.InsertAsync(school);
110 | await _unitOfWork.SaveChangesAsync();
111 | }
112 |
113 |
114 | [Fact]
115 | public async Task GetByIdAsync()
116 | {
117 | var newSchool = Insert();
118 |
119 | var school = await _schoolRepository.GetByIdAsync(newSchool.Id);
120 | Assert.True(school != null);
121 | }
122 |
123 | [Fact]
124 | public async Task InsertsAsync()
125 | {
126 | var schools = new List();
127 | for (int i = 0; i < 100; i++)
128 | {
129 | var school = new School
130 | {
131 | Id = Guid.NewGuid(),
132 | Name = Guid.NewGuid().ToString()
133 | };
134 | schools.Add(school);
135 | }
136 | await _schoolRepository.InsertAsync(schools);
137 | await _unitOfWork.SaveChangesAsync();
138 |
139 | foreach (var item in schools)
140 | {
141 | var school = await _schoolRepository.GetByIdAsync(item.Id);
142 | if (school == null)
143 | {
144 | Assert.True(false);
145 | }
146 | }
147 | }
148 | [Fact]
149 | public void Update()
150 | {
151 | var school = Insert();
152 | school.Name = Guid.NewGuid().ToString();
153 | _schoolRepository.Update(school);
154 | _unitOfWork.SaveChanges();
155 | }
156 | [Fact]
157 | public void Updates()
158 | {
159 | var schools = Inserts();
160 |
161 | schools.ForEach(x => x.Name = Guid.NewGuid().ToString());
162 | _schoolRepository.Update(schools);
163 | _unitOfWork.SaveChanges();
164 |
165 | foreach (var item in schools)
166 | {
167 | var school = _schoolRepository.GetById(item.Id);
168 | if (school.Name != item.Name)
169 | {
170 | Assert.True(false);
171 | }
172 | }
173 | }
174 |
175 |
176 | [Fact]
177 | public void Delete()
178 | {
179 | var school = Insert();
180 |
181 | _schoolRepository.Delete(school);
182 | _unitOfWork.SaveChanges();
183 |
184 | var newSchool = _schoolRepository.Table.FirstOrDefault(x => x.Id == school.Id);
185 | Assert.True(newSchool == null);
186 | }
187 |
188 |
189 |
190 | [Fact]
191 | public void Deletes()
192 | {
193 | var schools = new List();
194 | for (int i = 0; i < 100; i++)
195 | {
196 | var school = new School
197 | {
198 | Id = Guid.NewGuid(),
199 | Name = Guid.NewGuid().ToString()
200 | };
201 | schools.Add(school);
202 | }
203 | _schoolRepository.Insert(schools);
204 | _unitOfWork.SaveChanges();
205 |
206 | _schoolRepository.Delete(schools);
207 | _unitOfWork.SaveChanges();
208 |
209 | foreach (var item in schools)
210 | {
211 | var school = _schoolRepository.GetById(item.Id);
212 | if (school != null)
213 | {
214 | Assert.True(false);
215 | }
216 | }
217 | }
218 |
219 |
220 |
221 | [Fact]
222 | public void DeleteByLambda()
223 | {
224 | var data = Insert();
225 |
226 | _schoolRepository.Delete(x => x.Id == data.Id);
227 | _unitOfWork.SaveChanges();
228 |
229 | var newSchool = _schoolRepository.Table.FirstOrDefault(x => x.Id == data.Id);
230 | Assert.True(newSchool == null);
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/Entities/School.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Leo.Chimp.Test.Entities
6 | {
7 | public class School : IEntity
8 | {
9 | public Guid Id { get; set; }
10 | public string Name { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/Entities/Student.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace Leo.Chimp.Test.Entities
7 | {
8 | public class Student : IEntity
9 | {
10 | public Guid Id { get; set; }
11 | public string Name { get; set; }
12 | public Guid SchoolId { get; set; }
13 | public DateTime Birthday { get; set; }
14 |
15 | public School MySchool { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/Leo.Chimp.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6
5 |
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/Repositories/SchoolRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Leo.Chimp.Test.Entities;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace Leo.Chimp.Test.Repositories
8 | {
9 | public class SchoolRepository: EfCoreRepository,ISchoolRepository
10 | {
11 | public SchoolRepository(DbContext context) : base(context)
12 | {
13 | }
14 | }
15 |
16 | public interface ISchoolRepository : IRepository
17 | {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/Leo.Chimp.Test/Repositories/StudentRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Leo.Chimp.Test.Entities;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace Leo.Chimp.Test.Repositories
8 | {
9 | public class StudentRepository: EfCoreRepository,IStudentRepository
10 | {
11 | public StudentRepository(DbContext context) : base(context)
12 | {
13 | }
14 | }
15 |
16 | public interface IStudentRepository : IRepository
17 | {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------