├── .gitignore
├── EFCoreEleganceUse.Core
└── EFCoreEleganceUse.Core.csproj
├── EFCoreEleganceUse.Domain
├── EFCoreEleganceUse.Domain.csproj
├── EFCoreEleganceUseDomainModule.cs
├── Entities
│ ├── AggregateRoot.cs
│ ├── Book.cs
│ ├── Config
│ │ ├── BookConfig.cs
│ │ ├── EntityTypeConfiguration.cs
│ │ └── UserConfig.cs
│ ├── EFEntityInfo.cs
│ ├── IEFEntity.cs
│ ├── ISoftDelete.cs
│ └── User.cs
└── Repository
│ ├── IAsyncRepository.cs
│ └── IWorkUnit.cs
├── EFCoreEleganceUse.EF.Migrations
├── EFCoreEleganceUse.EF.Migrations.csproj
└── Migrations
│ ├── 20221128052101_InitializeDatabase.Designer.cs
│ ├── 20221128052101_InitializeDatabase.cs
│ └── LibraryDbContextModelSnapshot.cs
├── EFCoreEleganceUse.EF.Mysql
├── EFCoreEleganceUse.EF.Mysql.csproj
├── EFCoreEleganceUseEFCoreModule.cs
├── LibraryDbContext.cs
├── MysqlOptions.cs
└── Repository
│ ├── GenericRepository.cs
│ └── WorkUnit.cs
├── EFCoreEleganceUse.sln
├── EFCoreEleganceUse
├── EFCoreEleganceUse.csproj
├── Program.cs
├── UsersController.cs
└── appsettings.Development.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Coverlet is a free, cross platform Code Coverage Tool
141 | coverage*[.json, .xml, .info]
142 |
143 | # Visual Studio code coverage results
144 | *.coverage
145 | *.coveragexml
146 |
147 | # NCrunch
148 | _NCrunch_*
149 | .*crunch*.local.xml
150 | nCrunchTemp_*
151 |
152 | # MightyMoose
153 | *.mm.*
154 | AutoTest.Net/
155 |
156 | # Web workbench (sass)
157 | .sass-cache/
158 |
159 | # Installshield output folder
160 | [Ee]xpress/
161 |
162 | # DocProject is a documentation generator add-in
163 | DocProject/buildhelp/
164 | DocProject/Help/*.HxT
165 | DocProject/Help/*.HxC
166 | DocProject/Help/*.hhc
167 | DocProject/Help/*.hhk
168 | DocProject/Help/*.hhp
169 | DocProject/Help/Html2
170 | DocProject/Help/html
171 |
172 | # Click-Once directory
173 | publish/
174 |
175 | # Publish Web Output
176 | *.[Pp]ublish.xml
177 | *.azurePubxml
178 | # Note: Comment the next line if you want to checkin your web deploy settings,
179 | # but database connection strings (with potential passwords) will be unencrypted
180 | *.pubxml
181 | *.publishproj
182 |
183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
184 | # checkin your Azure Web App publish settings, but sensitive information contained
185 | # in these scripts will be unencrypted
186 | PublishScripts/
187 |
188 | # NuGet Packages
189 | *.nupkg
190 | # NuGet Symbol Packages
191 | *.snupkg
192 | # The packages folder can be ignored because of Package Restore
193 | **/[Pp]ackages/*
194 | # except build/, which is used as an MSBuild target.
195 | !**/[Pp]ackages/build/
196 | # Uncomment if necessary however generally it will be regenerated when needed
197 | #!**/[Pp]ackages/repositories.config
198 | # NuGet v3's project.json files produces more ignorable files
199 | *.nuget.props
200 | *.nuget.targets
201 |
202 | # Microsoft Azure Build Output
203 | csx/
204 | *.build.csdef
205 |
206 | # Microsoft Azure Emulator
207 | ecf/
208 | rcf/
209 |
210 | # Windows Store app package directories and files
211 | AppPackages/
212 | BundleArtifacts/
213 | Package.StoreAssociation.xml
214 | _pkginfo.txt
215 | *.appx
216 | *.appxbundle
217 | *.appxupload
218 |
219 | # Visual Studio cache files
220 | # files ending in .cache can be ignored
221 | *.[Cc]ache
222 | # but keep track of directories ending in .cache
223 | !?*.[Cc]ache/
224 |
225 | # Others
226 | ClientBin/
227 | ~$*
228 | *~
229 | *.dbmdl
230 | *.dbproj.schemaview
231 | *.jfm
232 | *.pfx
233 | *.publishsettings
234 | orleans.codegen.cs
235 |
236 | # Including strong name files can present a security risk
237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
238 | #*.snk
239 |
240 | # Since there are multiple workflows, uncomment next line to ignore bower_components
241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
242 | #bower_components/
243 |
244 | # RIA/Silverlight projects
245 | Generated_Code/
246 |
247 | # Backup & report files from converting an old project file
248 | # to a newer Visual Studio version. Backup files are not needed,
249 | # because we have git ;-)
250 | _UpgradeReport_Files/
251 | Backup*/
252 | UpgradeLog*.XML
253 | UpgradeLog*.htm
254 | ServiceFabricBackup/
255 | *.rptproj.bak
256 |
257 | # SQL Server files
258 | *.mdf
259 | *.ldf
260 | *.ndf
261 |
262 | # Business Intelligence projects
263 | *.rdl.data
264 | *.bim.layout
265 | *.bim_*.settings
266 | *.rptproj.rsuser
267 | *- [Bb]ackup.rdl
268 | *- [Bb]ackup ([0-9]).rdl
269 | *- [Bb]ackup ([0-9][0-9]).rdl
270 |
271 | # Microsoft Fakes
272 | FakesAssemblies/
273 |
274 | # GhostDoc plugin setting file
275 | *.GhostDoc.xml
276 |
277 | # Node.js Tools for Visual Studio
278 | .ntvs_analysis.dat
279 | node_modules/
280 |
281 | # Visual Studio 6 build log
282 | *.plg
283 |
284 | # Visual Studio 6 workspace options file
285 | *.opt
286 |
287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
288 | *.vbw
289 |
290 | # Visual Studio LightSwitch build output
291 | **/*.HTMLClient/GeneratedArtifacts
292 | **/*.DesktopClient/GeneratedArtifacts
293 | **/*.DesktopClient/ModelManifest.xml
294 | **/*.Server/GeneratedArtifacts
295 | **/*.Server/ModelManifest.xml
296 | _Pvt_Extensions
297 |
298 | # Paket dependency manager
299 | .paket/paket.exe
300 | paket-files/
301 |
302 | # FAKE - F# Make
303 | .fake/
304 |
305 | # CodeRush personal settings
306 | .cr/personal
307 |
308 | # Python Tools for Visual Studio (PTVS)
309 | __pycache__/
310 | *.pyc
311 |
312 | # Cake - Uncomment if you are using it
313 | # tools/**
314 | # !tools/packages.config
315 |
316 | # Tabs Studio
317 | *.tss
318 |
319 | # Telerik's JustMock configuration file
320 | *.jmconfig
321 |
322 | # BizTalk build output
323 | *.btp.cs
324 | *.btm.cs
325 | *.odx.cs
326 | *.xsd.cs
327 |
328 | # OpenCover UI analysis results
329 | OpenCover/
330 |
331 | # Azure Stream Analytics local run output
332 | ASALocalRun/
333 |
334 | # MSBuild Binary and Structured Log
335 | *.binlog
336 |
337 | # NVidia Nsight GPU debugger configuration file
338 | *.nvuser
339 |
340 | # MFractors (Xamarin productivity tool) working folder
341 | .mfractor/
342 |
343 | # Local History for Visual Studio
344 | .localhistory/
345 |
346 | # BeatPulse healthcheck temp database
347 | healthchecksdb
348 |
349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
350 | MigrationBackup/
351 |
352 | # Ionide (cross platform F# VS Code tools) working folder
353 | .ionide/
354 |
355 | ##
356 | ## Visual studio for Mac
357 | ##
358 |
359 |
360 | # globs
361 | Makefile.in
362 | *.userprefs
363 | *.usertasks
364 | config.make
365 | config.status
366 | aclocal.m4
367 | install-sh
368 | autom4te.cache/
369 | *.tar.gz
370 | tarballs/
371 | test-results/
372 |
373 | # Mac bundle stuff
374 | *.dmg
375 | *.app
376 |
377 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
378 | # General
379 | .DS_Store
380 | .AppleDouble
381 | .LSOverride
382 |
383 | # Icon must end with two \r
384 | Icon
385 |
386 |
387 | # Thumbnails
388 | ._*
389 |
390 | # Files that might appear in the root of a volume
391 | .DocumentRevisions-V100
392 | .fseventsd
393 | .Spotlight-V100
394 | .TemporaryItems
395 | .Trashes
396 | .VolumeIcon.icns
397 | .com.apple.timemachine.donotpresent
398 |
399 | # Directories potentially created on remote AFP share
400 | .AppleDB
401 | .AppleDesktop
402 | Network Trash Folder
403 | Temporary Items
404 | .apdisk
405 |
406 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
407 | # Windows thumbnail cache files
408 | Thumbs.db
409 | ehthumbs.db
410 | ehthumbs_vista.db
411 |
412 | # Dump file
413 | *.stackdump
414 |
415 | # Folder config file
416 | [Dd]esktop.ini
417 |
418 | # Recycle Bin used on file shares
419 | $RECYCLE.BIN/
420 |
421 | # Windows Installer files
422 | *.cab
423 | *.msi
424 | *.msix
425 | *.msm
426 | *.msp
427 |
428 | # Windows shortcuts
429 | *.lnk
430 |
431 | # JetBrains Rider
432 | .idea/
433 | *.sln.iml
434 |
435 | ##
436 | ## Visual Studio Code
437 | ##
438 | .vscode
439 | .vscode/*
440 | !.vscode/settings.json
441 | !.vscode/tasks.json
442 | !.vscode/launch.json
443 | !.vscode/extensions.json
444 |
445 | *.pro.user
446 | /server/business/plugins/mfoht030v2.common/main/vehicle/telegrams/InstallCommandResponse.cs
447 | /server/business/plugins/mfoht030v2.common/main/vehicle/telegrams/RemoveCommandRequest.cs
448 | /server/business/plugins/mfoht030v2.common/main/vehicle/telegrams/RemoveCommandResponse.cs
449 | /server/business/share/apibase/main/data/order/MSTCRemoveVehicleCommand.cs
450 | /server/business/share/apibase/main/data/order/MSTCInstallVehicleCommand.cs
451 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Core/EFCoreEleganceUse.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/EFCoreEleganceUse.Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/EFCoreEleganceUseDomainModule.cs:
--------------------------------------------------------------------------------
1 | using Autofac;
2 | using EFCoreEleganceUse.Domain.Entities;
3 |
4 | namespace EFCoreEleganceUse.Domain
5 | {
6 | public class EFCoreEleganceUseDomainModule : Module
7 | {
8 | protected override void Load(ContainerBuilder builder)
9 | {
10 | builder.RegisterType().SingleInstance();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/AggregateRoot.cs:
--------------------------------------------------------------------------------
1 | namespace EFCoreEleganceUse.Domain.Entities
2 | {
3 | public abstract class AggregateRoot : IEFEntity
4 | {
5 | public TKey Id { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/Book.cs:
--------------------------------------------------------------------------------
1 | namespace EFCoreEleganceUse.Domain.Entities
2 | {
3 | public class Book : AggregateRoot
4 | {
5 | public string BookName { get; set; }
6 | public string Author { get; set; }
7 | public double Price { get; set; }
8 | public string Publisher { get; set; }
9 | public string SN { get; set; } //图书序列号
10 | public string? UserId { get; set; }
11 | public virtual User? User { get; set; } //租借该书的用户
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/Config/BookConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
2 |
3 | namespace EFCoreEleganceUse.Domain.Entities.Config
4 | {
5 | public class BookConfig : EntityTypeConfiguration
6 | {
7 | public override void Configure(EntityTypeBuilder builder)
8 | {
9 | base.Configure(builder);
10 |
11 | builder.Property(x => x.Id).ValueGeneratedOnAdd(); //设置book的id自增
12 | builder.Property(x => x.BookName).HasMaxLength(500).IsRequired();
13 | builder.HasIndex(x => x.Author);//作者添加索引
14 | builder.HasIndex(x => x.SN).IsUnique();//序列号添加唯一索引
15 | builder.HasOne(r => r.User).WithMany(x => x.Books)
16 | .HasForeignKey(r => r.UserId).IsRequired(false);//导航属性,本质就是创建外键,虽然查询很方便,生产中不建议使用!!!
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/Config/EntityTypeConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace EFCoreEleganceUse.Domain.Entities.Config
5 | {
6 | public abstract class EntityTypeConfiguration : IEntityTypeConfiguration
7 | where TEntity : AggregateRoot
8 | {
9 | public virtual void Configure(EntityTypeBuilder builder)
10 | {
11 | var entityType = typeof(TEntity);
12 |
13 | builder.HasKey(x => x.Id);
14 |
15 | if (typeof(ISoftDelete).IsAssignableFrom(entityType))
16 | {
17 | builder.HasQueryFilter(d => EF.Property(d, "IsDeleted") == false);
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/Config/UserConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
2 |
3 | namespace EFCoreEleganceUse.Domain.Entities.Config
4 | {
5 | public class UserConfig : EntityTypeConfiguration
6 | {
7 | public override void Configure(EntityTypeBuilder builder)
8 | {
9 | base.Configure(builder);
10 |
11 | builder.Property(x => x.UserName).HasMaxLength(50);
12 | //mock一条数据
13 | builder.HasData(new User()
14 | {
15 | Id = "090213204",
16 | UserName = "Bruce",
17 | Birthday = DateTime.Parse("1996-08-24")
18 | });
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/EFEntityInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace EFCoreEleganceUse.Domain.Entities
4 | {
5 | public class EFEntityInfo
6 | {
7 | public (Assembly Assembly, IEnumerable Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly));
8 | private IEnumerable GetEntityTypes(Assembly assembly)
9 | {
10 | var efEntities = assembly.GetTypes().Where(m => m.FullName != null
11 | && Array.Exists(m.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEFEntity<>))
12 | && !m.IsAbstract && !m.IsInterface).ToArray();
13 |
14 | return efEntities;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/IEFEntity.cs:
--------------------------------------------------------------------------------
1 | namespace EFCoreEleganceUse.Domain.Entities
2 | {
3 | public interface IEFEntity
4 | {
5 | public TKey Id { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/ISoftDelete.cs:
--------------------------------------------------------------------------------
1 | namespace EFCoreEleganceUse.Domain.Entities
2 | {
3 | public interface ISoftDelete
4 | {
5 | bool IsDeleted { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Entities/User.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace EFCoreEleganceUse.Domain.Entities
4 | {
5 | public class User : AggregateRoot
6 | {
7 | public string UserName { get; set; }
8 | public DateTime Birthday { get; set; }
9 | public virtual ICollection Books { get; set; } //该用户所借的书
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Repository/IAsyncRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 |
3 | namespace EFCoreEleganceUse.Domain.Repository
4 | {
5 | public interface IAsyncRepository where TEntity : class
6 | {
7 | // query
8 | IQueryable All();
9 | IQueryable All(string[] propertiesToInclude);
10 | IQueryable Where(Expression> filter);
11 | IQueryable Where(Expression> filter, string[] propertiesToInclude);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.Domain/Repository/IWorkUnit.cs:
--------------------------------------------------------------------------------
1 | namespace EFCoreEleganceUse.Domain.Repository
2 | {
3 | public interface IWorkUnit
4 | {
5 | Task SaveAsync();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Migrations/EFCoreEleganceUse.EF.Migrations.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Migrations/Migrations/20221128052101_InitializeDatabase.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using EFCoreEleganceUse.EF.Mysql;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Migrations;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | #nullable disable
10 |
11 | namespace EFCoreEleganceUse.EF.Migrations.Migrations
12 | {
13 | [DbContext(typeof(LibraryDbContext))]
14 | [Migration("20221128052101_InitializeDatabase")]
15 | partial class InitializeDatabase
16 | {
17 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
18 | {
19 | #pragma warning disable 612, 618
20 | modelBuilder
21 | .HasAnnotation("ProductVersion", "6.0.5")
22 | .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 |
24 | MySqlModelBuilderExtensions.HasCharSet(modelBuilder, "utf8mb4 ");
25 |
26 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.Book", b =>
27 | {
28 | b.Property("Id")
29 | .ValueGeneratedOnAdd()
30 | .HasColumnType("bigint");
31 |
32 | b.Property("Author")
33 | .IsRequired()
34 | .HasColumnType("varchar(95)");
35 |
36 | b.Property("BookName")
37 | .IsRequired()
38 | .HasMaxLength(500)
39 | .HasColumnType("varchar(500)");
40 |
41 | b.Property("Price")
42 | .HasColumnType("double");
43 |
44 | b.Property("Publisher")
45 | .IsRequired()
46 | .HasColumnType("longtext");
47 |
48 | b.Property("SN")
49 | .IsRequired()
50 | .HasColumnType("varchar(95)");
51 |
52 | b.Property("UserId")
53 | .IsRequired()
54 | .HasColumnType("varchar(95)");
55 |
56 | b.HasKey("Id");
57 |
58 | b.HasIndex("Author");
59 |
60 | b.HasIndex("SN")
61 | .IsUnique();
62 |
63 | b.HasIndex("UserId");
64 |
65 | b.ToTable("Book");
66 | });
67 |
68 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.User", b =>
69 | {
70 | b.Property("Id")
71 | .HasColumnType("varchar(95)");
72 |
73 | b.Property("Birthday")
74 | .HasColumnType("datetime");
75 |
76 | b.Property("UserName")
77 | .IsRequired()
78 | .HasMaxLength(50)
79 | .HasColumnType("varchar(50)");
80 |
81 | b.HasKey("Id");
82 |
83 | b.ToTable("User");
84 |
85 | b.HasData(
86 | new
87 | {
88 | Id = "090213204",
89 | Birthday = new DateTime(1996, 8, 24, 0, 0, 0, 0, DateTimeKind.Unspecified),
90 | UserName = "Bruce"
91 | });
92 | });
93 |
94 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.Book", b =>
95 | {
96 | b.HasOne("EFCoreEleganceUse.Domain.Entities.User", "User")
97 | .WithMany("Books")
98 | .HasForeignKey("UserId")
99 | .OnDelete(DeleteBehavior.Cascade)
100 | .IsRequired();
101 |
102 | b.Navigation("User");
103 | });
104 |
105 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.User", b =>
106 | {
107 | b.Navigation("Books");
108 | });
109 | #pragma warning restore 612, 618
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Migrations/Migrations/20221128052101_InitializeDatabase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Metadata;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 |
5 | #nullable disable
6 |
7 | namespace EFCoreEleganceUse.EF.Migrations.Migrations
8 | {
9 | public partial class InitializeDatabase : Migration
10 | {
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AlterDatabase()
14 | .Annotation("MySql:CharSet", "utf8mb4 ");
15 |
16 | migrationBuilder.CreateTable(
17 | name: "User",
18 | columns: table => new
19 | {
20 | Id = table.Column(type: "varchar(95)", nullable: false)
21 | .Annotation("MySql:CharSet", "utf8mb4 "),
22 | UserName = table.Column(type: "varchar(50)", maxLength: 50, nullable: false)
23 | .Annotation("MySql:CharSet", "utf8mb4 "),
24 | Birthday = table.Column(type: "datetime", nullable: false)
25 | },
26 | constraints: table =>
27 | {
28 | table.PrimaryKey("PK_User", x => x.Id);
29 | })
30 | .Annotation("MySql:CharSet", "utf8mb4 ");
31 |
32 | migrationBuilder.CreateTable(
33 | name: "Book",
34 | columns: table => new
35 | {
36 | Id = table.Column(type: "bigint", nullable: false)
37 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
38 | BookName = table.Column(type: "varchar(500)", maxLength: 500, nullable: false)
39 | .Annotation("MySql:CharSet", "utf8mb4 "),
40 | Author = table.Column(type: "varchar(95)", nullable: false)
41 | .Annotation("MySql:CharSet", "utf8mb4 "),
42 | Price = table.Column(type: "double", nullable: false),
43 | Publisher = table.Column(type: "longtext", nullable: false)
44 | .Annotation("MySql:CharSet", "utf8mb4 "),
45 | SN = table.Column(type: "varchar(95)", nullable: false)
46 | .Annotation("MySql:CharSet", "utf8mb4 "),
47 | UserId = table.Column(type: "varchar(95)", nullable: false)
48 | .Annotation("MySql:CharSet", "utf8mb4 ")
49 | },
50 | constraints: table =>
51 | {
52 | table.PrimaryKey("PK_Book", x => x.Id);
53 | table.ForeignKey(
54 | name: "FK_Book_User_UserId",
55 | column: x => x.UserId,
56 | principalTable: "User",
57 | principalColumn: "Id",
58 | onDelete: ReferentialAction.Cascade);
59 | })
60 | .Annotation("MySql:CharSet", "utf8mb4 ");
61 |
62 | migrationBuilder.InsertData(
63 | table: "User",
64 | columns: new[] { "Id", "Birthday", "UserName" },
65 | values: new object[] { "090213204", new DateTime(1996, 8, 24, 0, 0, 0, 0, DateTimeKind.Unspecified), "Bruce" });
66 |
67 | migrationBuilder.CreateIndex(
68 | name: "IX_Book_Author",
69 | table: "Book",
70 | column: "Author");
71 |
72 | migrationBuilder.CreateIndex(
73 | name: "IX_Book_SN",
74 | table: "Book",
75 | column: "SN",
76 | unique: true);
77 |
78 | migrationBuilder.CreateIndex(
79 | name: "IX_Book_UserId",
80 | table: "Book",
81 | column: "UserId");
82 | }
83 |
84 | protected override void Down(MigrationBuilder migrationBuilder)
85 | {
86 | migrationBuilder.DropTable(
87 | name: "Book");
88 |
89 | migrationBuilder.DropTable(
90 | name: "User");
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Migrations/Migrations/LibraryDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using EFCoreEleganceUse.EF.Mysql;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 |
8 | #nullable disable
9 |
10 | namespace EFCoreEleganceUse.EF.Migrations.Migrations
11 | {
12 | [DbContext(typeof(LibraryDbContext))]
13 | partial class LibraryDbContextModelSnapshot : ModelSnapshot
14 | {
15 | protected override void BuildModel(ModelBuilder modelBuilder)
16 | {
17 | #pragma warning disable 612, 618
18 | modelBuilder
19 | .HasAnnotation("ProductVersion", "6.0.5")
20 | .HasAnnotation("Relational:MaxIdentifierLength", 64);
21 |
22 | MySqlModelBuilderExtensions.HasCharSet(modelBuilder, "utf8mb4 ");
23 |
24 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.Book", b =>
25 | {
26 | b.Property("Id")
27 | .ValueGeneratedOnAdd()
28 | .HasColumnType("bigint");
29 |
30 | b.Property("Author")
31 | .IsRequired()
32 | .HasColumnType("varchar(95)");
33 |
34 | b.Property("BookName")
35 | .IsRequired()
36 | .HasMaxLength(500)
37 | .HasColumnType("varchar(500)");
38 |
39 | b.Property("Price")
40 | .HasColumnType("double");
41 |
42 | b.Property("Publisher")
43 | .IsRequired()
44 | .HasColumnType("longtext");
45 |
46 | b.Property("SN")
47 | .IsRequired()
48 | .HasColumnType("varchar(95)");
49 |
50 | b.Property("UserId")
51 | .IsRequired()
52 | .HasColumnType("varchar(95)");
53 |
54 | b.HasKey("Id");
55 |
56 | b.HasIndex("Author");
57 |
58 | b.HasIndex("SN")
59 | .IsUnique();
60 |
61 | b.HasIndex("UserId");
62 |
63 | b.ToTable("Book");
64 | });
65 |
66 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.User", b =>
67 | {
68 | b.Property("Id")
69 | .HasColumnType("varchar(95)");
70 |
71 | b.Property("Birthday")
72 | .HasColumnType("datetime");
73 |
74 | b.Property("UserName")
75 | .IsRequired()
76 | .HasMaxLength(50)
77 | .HasColumnType("varchar(50)");
78 |
79 | b.HasKey("Id");
80 |
81 | b.ToTable("User");
82 |
83 | b.HasData(
84 | new
85 | {
86 | Id = "090213204",
87 | Birthday = new DateTime(1996, 8, 24, 0, 0, 0, 0, DateTimeKind.Unspecified),
88 | UserName = "Bruce"
89 | });
90 | });
91 |
92 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.Book", b =>
93 | {
94 | b.HasOne("EFCoreEleganceUse.Domain.Entities.User", "User")
95 | .WithMany("Books")
96 | .HasForeignKey("UserId")
97 | .OnDelete(DeleteBehavior.Cascade)
98 | .IsRequired();
99 |
100 | b.Navigation("User");
101 | });
102 |
103 | modelBuilder.Entity("EFCoreEleganceUse.Domain.Entities.User", b =>
104 | {
105 | b.Navigation("Books");
106 | });
107 | #pragma warning restore 612, 618
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Mysql/EFCoreEleganceUse.EF.Mysql.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Mysql/EFCoreEleganceUseEFCoreModule.cs:
--------------------------------------------------------------------------------
1 | using Autofac;
2 | using EFCoreEleganceUse.Domain;
3 | using EFCoreEleganceUse.Domain.Repository;
4 | using EFCoreEleganceUse.EF.Mysql.Repository;
5 |
6 | namespace EFCoreEleganceUse.EF.Mysql
7 | {
8 | public class EFCoreEleganceUseEFCoreModule : Module
9 | {
10 | protected override void Load(ContainerBuilder builder)
11 | {
12 | base.Load(builder);
13 |
14 | builder.RegisterModule(); //注入domain模块
15 | builder.RegisterGeneric(typeof(GenericRepository<,>))//将dbcontext注入到仓储的构造中
16 | .UsingConstructor(typeof(LibraryDbContext))
17 | .AsImplementedInterfaces()
18 | .InstancePerDependency();
19 |
20 | builder.RegisterType().As().InstancePerDependency();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Mysql/LibraryDbContext.cs:
--------------------------------------------------------------------------------
1 | using EFCoreEleganceUse.Domain.Entities;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace EFCoreEleganceUse.EF.Mysql
5 | {
6 | ///
7 | /// 图书馆数据库上下文
8 | ///
9 | public class LibraryDbContext : DbContext
10 | {
11 | private readonly EFEntityInfo _efEntitysInfo;
12 |
13 | public LibraryDbContext(DbContextOptions options, EFEntityInfo efEntityInfo) : base(options)
14 | {
15 | _efEntitysInfo = efEntityInfo;
16 | ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
17 | }
18 |
19 | protected override void OnModelCreating(ModelBuilder modelBuilder)
20 | {
21 | modelBuilder.HasCharSet("utf8mb4 ");
22 | var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo;
23 | foreach (var entityType in Types)
24 | {
25 | modelBuilder.Entity(entityType);
26 | }
27 | modelBuilder.ApplyConfigurationsFromAssembly(Assembly);
28 | base.OnModelCreating(modelBuilder);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Mysql/MysqlOptions.cs:
--------------------------------------------------------------------------------
1 | namespace EFCoreEleganceUse.EF.Mysql
2 | {
3 | public class MysqlOptions
4 | {
5 | public string Version { get; set; }
6 | public string ConnectionString { get; set; }
7 | public string MigrationAssembly { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Mysql/Repository/GenericRepository.cs:
--------------------------------------------------------------------------------
1 | using EFCoreEleganceUse.Domain.Repository;
2 | using Microsoft.EntityFrameworkCore;
3 | using System.Linq.Expressions;
4 |
5 | namespace EFCoreEleganceUse.EF.Mysql.Repository
6 | {
7 | public class GenericRepository : IAsyncRepository where TEntity : class
8 | {
9 | protected readonly LibraryDbContext _dbContext;
10 |
11 | public GenericRepository(LibraryDbContext dbContext)
12 | {
13 | _dbContext = dbContext;
14 | }
15 |
16 | ~GenericRepository()
17 | {
18 | _dbContext?.Dispose();
19 | }
20 |
21 | public virtual IQueryable All()
22 | {
23 | return All(null);
24 | }
25 |
26 | public virtual IQueryable All(string[] propertiesToInclude)
27 | {
28 | var query = _dbContext.Set().AsNoTracking();
29 |
30 | if (propertiesToInclude != null)
31 | {
32 | foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p)))
33 | {
34 | query = query.Include(property);
35 | }
36 | }
37 |
38 | return query;
39 | }
40 |
41 | public virtual IQueryable Where(Expression> filter)
42 | {
43 | return Where(filter, null);
44 | }
45 |
46 | public virtual IQueryable Where(Expression> filter, string[] propertiesToInclude)
47 | {
48 | var query = _dbContext.Set().AsNoTracking();
49 |
50 | if (filter != null)
51 | {
52 | query = query.Where(filter);
53 | }
54 |
55 | if (propertiesToInclude != null)
56 | {
57 | foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p)))
58 | {
59 | query = query.Include(property);
60 | }
61 | }
62 |
63 | return query;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.EF.Mysql/Repository/WorkUnit.cs:
--------------------------------------------------------------------------------
1 | using EFCoreEleganceUse.Domain.Repository;
2 |
3 | namespace EFCoreEleganceUse.EF.Mysql.Repository
4 | {
5 | public class WorkUnit : IWorkUnit, IDisposable
6 | {
7 | private readonly LibraryDbContext _dbContext;
8 | public WorkUnit(LibraryDbContext dbContext)
9 | {
10 | _dbContext = dbContext;
11 | }
12 |
13 | public async Task SaveAsync()
14 | {
15 | await _dbContext.SaveChangesAsync();
16 | }
17 |
18 | protected virtual void Dispose(bool disposing)
19 | {
20 | if (!_disposed && disposing)
21 | {
22 | _dbContext.Dispose();
23 | }
24 |
25 | _disposed = true;
26 | }
27 |
28 | public void Dispose()
29 | {
30 | Dispose(true);
31 | GC.SuppressFinalize(this);
32 | }
33 |
34 | private bool _disposed = false;
35 | ~WorkUnit()
36 | {
37 | Dispose();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32929.385
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCoreEleganceUse", "EFCoreEleganceUse\EFCoreEleganceUse.csproj", "{145105F5-B6EB-4E63-AC74-472AFF96F380}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCoreEleganceUse.Core", "EFCoreEleganceUse.Core\EFCoreEleganceUse.Core.csproj", "{CAC4C2C2-3754-437C-B6B8-77F4602C9825}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCoreEleganceUse.EF.Mysql", "EFCoreEleganceUse.EF.Mysql\EFCoreEleganceUse.EF.Mysql.csproj", "{19DE9EEE-C71E-4014-89D4-FB650FD5D1BB}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCoreEleganceUse.EF.Migrations", "EFCoreEleganceUse.EF.Migrations\EFCoreEleganceUse.EF.Migrations.csproj", "{9BA28812-DDD2-4FED-84B5-2B1FDB28E6CA}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCoreEleganceUse.Domain", "EFCoreEleganceUse.Domain\EFCoreEleganceUse.Domain.csproj", "{A9065967-80A0-4CDA-9642-E489CCA7ECEA}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {145105F5-B6EB-4E63-AC74-472AFF96F380}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {145105F5-B6EB-4E63-AC74-472AFF96F380}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {145105F5-B6EB-4E63-AC74-472AFF96F380}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {145105F5-B6EB-4E63-AC74-472AFF96F380}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {CAC4C2C2-3754-437C-B6B8-77F4602C9825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {CAC4C2C2-3754-437C-B6B8-77F4602C9825}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {CAC4C2C2-3754-437C-B6B8-77F4602C9825}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {CAC4C2C2-3754-437C-B6B8-77F4602C9825}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {19DE9EEE-C71E-4014-89D4-FB650FD5D1BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {19DE9EEE-C71E-4014-89D4-FB650FD5D1BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {19DE9EEE-C71E-4014-89D4-FB650FD5D1BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {19DE9EEE-C71E-4014-89D4-FB650FD5D1BB}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {9BA28812-DDD2-4FED-84B5-2B1FDB28E6CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {9BA28812-DDD2-4FED-84B5-2B1FDB28E6CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {9BA28812-DDD2-4FED-84B5-2B1FDB28E6CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {9BA28812-DDD2-4FED-84B5-2B1FDB28E6CA}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {A9065967-80A0-4CDA-9642-E489CCA7ECEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {A9065967-80A0-4CDA-9642-E489CCA7ECEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {A9065967-80A0-4CDA-9642-E489CCA7ECEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {A9065967-80A0-4CDA-9642-E489CCA7ECEA}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(ExtensibilityGlobals) = postSolution
47 | SolutionGuid = {145997F1-AD44-4B39-890F-68E0B936F6E0}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse/EFCoreEleganceUse.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | PreserveNewest
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | all
27 | runtime; build; native; contentfiles; analyzers; buildtransitive
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse/Program.cs:
--------------------------------------------------------------------------------
1 | using Autofac;
2 | using Autofac.Extensions.DependencyInjection;
3 | using EFCoreEleganceUse.EF.Mysql;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.Extensions.Hosting;
9 | using Serilog;
10 |
11 | internal class Program
12 | {
13 | async static Task Main(string[] args)
14 | {
15 | Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
16 | var host = CreateHostBuilder(args).Build();
17 | host.UseSwagger();
18 | host.UseSwaggerUI();
19 | host.MapControllers();
20 | host.UseAuthentication();
21 | host.UseAuthorization();
22 |
23 | await host.RunAsync();
24 | }
25 |
26 | public static WebApplicationBuilder CreateHostBuilder(string[] args)
27 | {
28 | var hostBuilder = WebApplication.CreateBuilder(options:new WebApplicationOptions()
29 | {
30 | Args = args,
31 | EnvironmentName = Environments.Development
32 | });
33 | hostBuilder.Host.UseSerilog((context, logger) =>//Serilog
34 | {
35 | string date = DateTime.Now.ToString("yyyy-MM-dd");//按时间创建文件夹
36 | logger.ReadFrom.Configuration(context.Configuration);
37 | logger.Enrich.FromLogContext();
38 | logger.WriteTo.File($"Logs/{date}/", rollingInterval: RollingInterval.Hour);//按小时分日志文件
39 | }).UseServiceProviderFactory(new AutofacServiceProviderFactory()).UseEnvironment(Environments.Development);
40 | //生产下需要通过命令行参数或者配置文件设置环境:开发,测试,生产
41 |
42 | hostBuilder.Host.ConfigureServices((hostContext, services) =>
43 | {
44 | //注入mysql,生产中应该放置在应用层
45 | var mysqlConfig = hostContext.Configuration.GetSection("Mysql").Get();
46 | var serverVersion = new MariaDbServerVersion(new Version(mysqlConfig.Version));
47 | services.AddDbContext(options =>
48 | {
49 | options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>
50 | {
51 | optionsBuilder.MinBatchSize(4);
52 | optionsBuilder.CommandTimeout(10);
53 | optionsBuilder.MigrationsAssembly(mysqlConfig.MigrationAssembly);
54 | optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
55 | }).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
56 |
57 | if (hostContext.HostingEnvironment.IsDevelopment())
58 | {
59 | options.EnableSensitiveDataLogging();
60 | options.EnableDetailedErrors();
61 | }
62 | });
63 | services.AddControllers();
64 | services.AddEndpointsApiExplorer();
65 | services.AddSwaggerGen();
66 | });
67 |
68 | hostBuilder.Host.ConfigureContainer((hcontext, containerBuilder) =>
69 | {
70 | //生产中由应用层聚合各种基础设置等模块,最后交由Host程序注册应用层模块
71 | containerBuilder.RegisterModule();
72 | });
73 |
74 | return hostBuilder;
75 | }
76 | }
--------------------------------------------------------------------------------
/EFCoreEleganceUse/UsersController.cs:
--------------------------------------------------------------------------------
1 | using EFCoreEleganceUse.Domain.Entities;
2 | using EFCoreEleganceUse.Domain.Repository;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace EFCoreEleganceUse
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class UsersController : ControllerBase
13 | {
14 | private readonly ILogger _logger;
15 | //生产中可以在应用层下创建service层,聚合各种实体仓储
16 | private readonly IAsyncRepository _userAsyncRepository;
17 |
18 | public UsersController(ILogger logger, IAsyncRepository userAsyncRepository)
19 | {
20 | _logger = logger;
21 | _userAsyncRepository = userAsyncRepository;
22 | }
23 |
24 | [HttpGet]
25 | [AllowAnonymous]
26 | public async Task Get()
27 | {
28 | return Ok(await _userAsyncRepository.All().ToListAsync());
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EFCoreEleganceUse/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "MinimumLevel": {
4 | "Default": "Information",
5 | "Override": {
6 | "Default": "Information",
7 | "System": "Information",
8 | "Microsoft": "Information"
9 | }
10 | },
11 | "WriteTo": [
12 | { "Name": "Console" },
13 | {
14 | "Name": "Async",
15 | "Args": {
16 | "configure": [
17 | {
18 | "Name": "File",
19 | "Args": {
20 | "outputTemplate": "Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff} LogLevel:{Level} Class:{SourceContext} Message:{Message}{Exception}{NewLine}",
21 | "rollingInterval": "4"
22 | }
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | },
29 | "Mysql": {
30 | "ConnectionString": "Server=127.0.0.1;Port=3306;database=testlib;uid=root;pwd=sa12345;",
31 | "Version": "8.0.20",
32 | "MigrationAssembly": "EFCoreEleganceUse.EF.Migrations"
33 | }
34 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | EFCore是微软官方的一款ORM框架,主要是用于实体和数据库对象之间的操作。功能非常强大,在老版本的时候叫做EF,后来.net core问世,EFCore也随之问世。
2 | 本文我们将用一个控制台项目Host一个web服务,并且使用本地Mysql作为数据库,使用EFCore的Code First模式进行数据操作。
3 |
4 | [toc]
5 |
6 | #### DBSet清除计划
7 | 以前使用EF/EFCore的开发者应该都记得,需要在DBContext里写好多DBSet,一个表对应一个DBSet,然后在其他地方操作这些DBSet对相关的表进行增删改查。作为一个开发,这些重复操作都是我们希望避免的,我们可以利用反射机制将这些类型通过框架自带的方法循环注册进去。
8 | 1.EF实体继承统一的接口,方便我们反射获取所有EF实体,接口可以设置一个泛型,来泛化我们的主键类型,因为可能存在不同的表的主键类型也不一样。
9 | 统一的EF实体接口
10 | ```
11 | public interface IEFEntity
12 | {
13 | public TKey Id { get; set; }
14 | }
15 | ```
16 | 统一的接口实现类
17 | ```
18 | public abstract class AggregateRoot : IEFEntity
19 | {
20 | public TKey Id { get; set; }
21 | }
22 | ```
23 | 用户实体类
24 | ```
25 | public class User : AggregateRoot
26 | {
27 | public string UserName { get; set; }
28 | public DateTime Birthday { get; set; }
29 | public virtual ICollection Books { get; set; }
30 | }
31 | ```
32 | 2.利用反射获取某个程序集下所有的实体类
33 | ```
34 | public class EFEntityInfo
35 | {
36 | public (Assembly Assembly, IEnumerable Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly));
37 | private IEnumerable GetEntityTypes(Assembly assembly)
38 | {
39 | //获取当前程序集下所有的实现了IEFEntity的实体类
40 | var efEntities = assembly.GetTypes().Where(m => m.FullName != null
41 | && Array.Exists(m.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEFEntity<>))
42 | && !m.IsAbstract && !m.IsInterface).ToArray();
43 |
44 | return efEntities;
45 | }
46 | }
47 | ```
48 | 3.DBContext实现类中OnModelCreating方法中注册这些类型
49 | ```
50 | protected override void OnModelCreating(ModelBuilder modelBuilder)
51 | {
52 | //循环实体类型,并且通过Entity方法注册类型
53 | foreach (var entityType in Types)
54 | {
55 | modelBuilder.Entity(entityType);
56 | }
57 |
58 | base.OnModelCreating(modelBuilder);
59 | }
60 | ```
61 | 至此为止所有的实体类都被注册到DBContext中作为DBSets,再也不需要一个个写DBSet了,可以用过DbContext.Set<User>()获取用户的DBSet。
62 | #### IEntityTypeConfiguration(表配置)
63 | 用数据库创建过表的同学都知道,在设计表的时候,可以给表添加很多配置和约束,在Code First模式中,很多同学都是在对象中通过注解的方式配置字段。如下就配置了用户名是不能为NULL和最大长度为500
64 | ```
65 | [Required]
66 | [MaxLength(500)]
67 | public string UserName { get; set; }
68 | ```
69 | 也有的同学在DbContext中的OnModelCreating方法配置
70 | ```
71 | modelBuilder.Entity().Property(x => x.UserName).IsRequired();
72 | ```
73 | 这两种方法,前者入侵行太强,直接代码耦合到实体类中了,后者不够清楚,把一大堆表的配置写在一个方法里,当然了很多人说可以拆分不同的方法或者使用注释分开。但是!不够优雅!
74 | 我们可以使用IEntityTypeConfiguration接口实现我们所想的优雅的表配置。
75 | 1.创建一个配置基类,继承自IEntityTypeConfiguration,做一些通用的配置,比如设置主键,一般都是id啦,还有软删除等。
76 | ```
77 | public abstract class EntityTypeConfiguration : IEntityTypeConfiguration
78 | where TEntity : AggregateRoot
79 | {
80 | public virtual void Configure(EntityTypeBuilder builder)
81 | {
82 | var entityType = typeof(TEntity);
83 |
84 | builder.HasKey(x => x.Id);
85 |
86 | if (typeof(ISoftDelete).IsAssignableFrom(entityType))
87 | {
88 | builder.HasQueryFilter(d => EF.Property(d, "IsDeleted") == false);
89 | }
90 | }
91 | }
92 | ```
93 | 2.创建用户实体/表独有的配置,比如设置用户名的最大长度,以及seed一些数据
94 | ```
95 | public class UserConfig : EntityTypeConfiguration
96 | {
97 | public override void Configure(EntityTypeBuilder builder)
98 | {
99 | base.Configure(builder);
100 |
101 | builder.Property(x => x.UserName).HasMaxLength(50);
102 | //mock一条数据
103 | builder.HasData(new User()
104 | {
105 | Id = "090213204",
106 | UserName = "Bruce",
107 | Birthday = DateTime.Parse("1996-08-24")
108 | });
109 | }
110 | }
111 | ```
112 | 当然还有很多配置可以设置,比如索引,导航属性,唯一键等。如下图书实体
113 | ```
114 | public class BookConfig : EntityTypeConfiguration
115 | {
116 | public override void Configure(EntityTypeBuilder builder)
117 | {
118 | base.Configure(builder);
119 |
120 | builder.Property(x => x.Id).ValueGeneratedOnAdd(); //设置book的id自增
121 | builder.Property(x => x.BookName).HasMaxLength(500).IsRequired();
122 | builder.HasIndex(x => x.Author);//作者添加索引
123 | builder.HasIndex(x => x.SN).IsUnique();//序列号添加唯一索引
124 | builder.HasOne(r => r.User).WithMany(x=>x.Books)
125 | .HasForeignKey(r => r.UserId).IsRequired(false);//导航属性,本质就是创建外键,虽然查询很方便,生产中不建议使用!!!
126 | }
127 | }
128 | ```
129 | 3.DBContext中应用配置
130 | ```
131 | protected override void OnModelCreating(ModelBuilder modelBuilder)
132 | {
133 | modelBuilder.HasCharSet("utf8mb4 ");
134 | var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo;
135 | foreach (var entityType in Types)
136 | {
137 | modelBuilder.Entity(entityType);
138 | }
139 | //只需要将配置类所在的程序集给到,它会自动加载
140 | modelBuilder.ApplyConfigurationsFromAssembly(Assembly);
141 | base.OnModelCreating(modelBuilder);
142 | }
143 | ```
144 | #### Repository(仓储)
145 | 这个不过分介绍,特别是基于http的微服务中基本都有这个。
146 | 1.创建一个仓储基类,对于不同的实体,创建一样的增删改查方法。
147 | 简单写几个查询的方法定义。
148 | ```
149 | public interface IAsyncRepository where TEntity : class
150 | {
151 | IQueryable All();
152 | IQueryable All(string[] propertiesToInclude);
153 | IQueryable Where(Expression> filter);
154 | IQueryable Where(Expression> filter, string[] propertiesToInclude);
155 | }
156 | ```
157 | 2.创建仓储实现类,将DBContext注入到构造中
158 | ```
159 | public class GenericRepository : IAsyncRepository where TEntity : class
160 | {
161 | protected readonly LibraryDbContext _dbContext;
162 |
163 | public GenericRepository(LibraryDbContext dbContext)
164 | {
165 | _dbContext = dbContext;
166 | }
167 |
168 | ~GenericRepository()
169 | {
170 | _dbContext?.Dispose();
171 | }
172 |
173 | public virtual IQueryable All()
174 | {
175 | return All(null);
176 | }
177 | public virtual IQueryable All(string[] propertiesToInclude)
178 | {
179 | var query = _dbContext.Set().AsNoTracking();
180 |
181 | if (propertiesToInclude != null)
182 | {
183 | foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p)))
184 | {
185 | query = query.Include(property);
186 | }
187 | }
188 |
189 | return query;
190 | }
191 | }
192 | ```
193 | #### Autofac
194 | 1.注入DBContext到Repository的构造方法中,并且注入Repository
195 | ```
196 | public class EFCoreEleganceUseEFCoreModule : Module
197 | {
198 | protected override void Load(ContainerBuilder builder)
199 | {
200 | base.Load(builder);
201 |
202 | builder.RegisterModule(); //注入domain模块
203 | builder.RegisterGeneric(typeof(GenericRepository<,>))//将dbcontext注入到仓储的构造中
204 | .UsingConstructor(typeof(LibraryDbContext))
205 | .AsImplementedInterfaces()
206 | .InstancePerDependency();
207 |
208 | builder.RegisterType().As().InstancePerDependency();
209 | }
210 | }
211 | ```
212 | 2.Domain注入EFEntityInfo
213 | ```
214 | public class EFCoreEleganceUseDomainModule : Module
215 | {
216 | protected override void Load(ContainerBuilder builder)
217 | {
218 | builder.RegisterType().SingleInstance();
219 | }
220 | }
221 | ```
222 | #### 数据库配置
223 | 1.注入DBContext,从配置文件读取数据库配置,然后根据开发/生产环境做一些特殊处理
224 | ```
225 | var mysqlConfig = hostContext.Configuration.GetSection("Mysql").Get();
226 | var serverVersion = new MariaDbServerVersion(new Version(mysqlConfig.Version));
227 | services.AddDbContext(options =>
228 | {
229 | options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>
230 | {
231 | optionsBuilder.MinBatchSize(4);
232 | optionsBuilder.CommandTimeout(10);
233 | optionsBuilder.MigrationsAssembly(mysqlConfig.MigrationAssembly);//迁移文件所在的程序集
234 | optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
235 | }).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
236 |
237 | //开发环境可以打开日志记录和显示详细的错误
238 | if (hostContext.HostingEnvironment.IsDevelopment())
239 | {
240 | options.EnableSensitiveDataLogging();
241 | options.EnableDetailedErrors();
242 | }
243 | });
244 | ```
245 | #### 项目架构和源码
246 | 
247 |
248 | 项目只是一个demo架构,并不适用于生产,主程序是一个控制台项目,只需要引用相关的包和模块,就可以启动一个web host.
249 |
250 | 全部代码已经全部上传到github:https://github.com/BruceQiu1996/EFCoreDemo
251 | 该项目是一个可以启动运行的基于.net6的控制台项目,启动后会启动一个web host和一个swagger页面。
--------------------------------------------------------------------------------