Write a framework of domain model from scratch
Each article will be marked with a corresponding tag
Github Warehouse address
Complete a business module (single table addition, deletion, modification and query, application layer, domain layer, storage layer and work unit)
Create a unit test (use the unit test to debug the code before you start writing the front-end code)
Record the log of CRUD operations of EF operation database (write the log to the text file temporarily, and then transfer it to Redis or message queue to be saved to mongoDB)
domain model
Application layer and domain layer
Whether a business code is placed in the business layer or the domain layer depends on the specific situation of each step of the business.
Obtain user data according to query criteria
The query parameters are directly built in the application layer, and the domain objects of users can be obtained through the storage layer
Get a user's Department, mobile number, friend, customer and leader
Get the user's information through the storage layer and return a UserDomain
The attributes of name, age and mobile phone are the basic information of the user, which can be obtained directly through the Get method of the domain model
Personal department, friends and customers are also user attributes. Call the method in UserDomain to obtain these attributes
UserDomain.GetDept() returns DempDomain
UserDomain.GetFriends() returns list < frienddomain >
To obtain the data with specified conditions, you need to use the storage layer to obtain it
UserRepository. Find (parameter, specified friend) returns list < frienddomain >
Other complex operations
Send SMS and email to the user
Call interface
Domain object to Dto
As long as it feels that it cannot be divided into a domain object and is not a data operation, it will be put into the business layer
Comparison between obtaining data through domain object and executing SQL
SQL obtains the data of employees and their departments, and one SQL is finished
The domain model should first obtain the employee information, then convert the UserEntity into a UserDomain, and then obtain the DeptDomain through the UserDomain
SQL can be written at will
The domain model can only obtain one or more pieces of data according to the Id each time
After the three-tier SQL Select, only the specified fields need to be returned
The domain model executes SQL Select, followed by all the fields of the table
Advantages and disadvantages of domain model
Low development and maintenance cost
The operation efficiency is relatively low
give an example:
A new business has been added within the company, and every employee can participate. After the employee completes the performance, he / she sends a message to the leader of the direct Department...... And a series of operations
Domain model
Application layer write a new method
Call warehouse to get a UserDomain
Get the UserDomain of the direct leader through the UserDomain
The action of calling UserDomain to send message
Get department information through UserDomain DeptDomain
Department performance improvement
After the execution is completed, the whole transaction is committed
When writing the whole logic, it is written in the dimension of business. An application layer method is a business. If the business involves those fields, it will call the methods in those fields
Whether old employees or new employees, as long as everyone writes according to this specification, it will not be too bad, and the whole project will not be too chaotic after several years of operation, and there will not be too much technical debt.
unit testing
Create a unit test project
Create a base class to initialize the project
TestClassBase.cs Unit test base class, initialization, configuration information, initialization IoC Container, initialize project global Global class using Autofac; using Core2022.Framework; using Core2022.Framework.Commons.Autofac; using Microsoft.Extensions.Configuration; namespace Core2022.Application.Services.Test { public class TestClassBase { ContainerBuilder builder = new ContainerBuilder(); IContainer rootContainer; public TestClassBase() { // initialization appsettings IConfiguration configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); Global.InitAppSettings(configuration); // initialization Autofac container // Autofac injection Orm object builder.AutofacInjectionOrmModel(); // Autofac Inject dependencies between layers builder.AutofacInjectionServices(); rootContainer = builder.Build(); Global.AppAutofacContainer(rootContainer); } } }
UserAppServiceTest.cs complete CreateUser Unit test of method using Core2022.Application.Services.DTO.User; using Core2022.Application.Services.Interface; using Core2022.Framework; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Core2022.Application.Services.Test { [TestClass] public class UserAppServiceTest : TestClassBase { IUserAppService userAppService; public UserAppServiceTest() { userAppService = Global.GetT<IUserAppService>(); } [TestMethod] public void CreateUser() { var respDto = userAppService.CreateUser(new UserRequestDto() { UserName = "222333", PassWord = "111111" }); } } }
EF operation log
using Core2022.Framework.Entity; using Core2022.Framework.Settings; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; namespace Core2022.Framework.UnitOfWork { public class AppUnitOfWork : DbContext, IUnitOfWork { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(Global.ConnectionString); base.OnConfiguring(optionsBuilder); } protected override void OnModelCreating(ModelBuilder modelBuilder) { Global.OrmModelInit.ForEach(t => { modelBuilder.Model.AddEntityType(t); }); base.OnModelCreating(modelBuilder); } public DbSet<OrmEntity> CreateSet<OrmEntity>() where OrmEntity : class { return base.Set<OrmEntity>(); } public new int SaveChanges() { EFLog(); return base.SaveChanges(); } #region EF Log private void EFLog() { IEnumerable<EntityEntry> list = this.ChangeTracker.Entries(); foreach (var item in list) { //Corresponding table name string tableName = ""; #region Get table name Type type = item.Entity.GetType(); Type patientMngAttrType = typeof(TableAttribute); TableAttribute attribute = null; if (type.IsDefined(patientMngAttrType, true)) { attribute = type.GetCustomAttributes(patientMngAttrType, true).FirstOrDefault() as TableAttribute; if (attribute != null) { tableName = attribute.Name; } } if (string.IsNullOrEmpty(tableName)) { tableName = type.Name; } #endregion BaseOrmModel model = item.Entity as BaseOrmModel; #region EntityState switch (item.State) { //case EntityState.Detached: //case EntityState.Unchanged: //case EntityState.Deleted: case EntityState.Modified: model.UpdateTime = DateTime.Now; model.Version = ++model.Version; WriteEFUpdateLog(item, tableName); break; case EntityState.Added: model.UpdateTime = DateTime.Now; model.CreateTime = DateTime.Now; model.IsDelete = false; model.Version = 0; WriteEFCreateLog(item, tableName); break; } #endregion } } private void WriteEFCreateLog(EntityEntry entry, string tableName) { var propertyList = entry.CurrentValues.Properties; string userName = "System user"; PropertyEntry keyEntity = entry.Property("KeyId"); Dictionary<string, object> dic = new Dictionary<string, object>(); foreach (var prop in propertyList) { PropertyEntry entity = entry.Property(prop.Name); dic.Add(prop.Name, entity.CurrentValue); } DBLogCreateModel createLog = new DBLogCreateModel() { CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TableName = tableName, OperatorUserName = userName, PrimaryKeyId = Guid.Parse(keyEntity.CurrentValue.ToString()), CreateValue = JsonConvert.SerializeObject(dic) }; WriteLog(createLog); } /// <summary> /// record EF Modify operation log /// </summary> /// <param name="entry"></param> /// <param name="tableName"></param> private void WriteEFUpdateLog(EntityEntry entry, string tableName) { var propertyList = entry.CurrentValues.Properties.Where(i => entry.Property(i.Name).IsModified); string userName = "System user"; PropertyEntry keyEntity = entry.Property("KeyId"); foreach (var prop in propertyList) { PropertyEntry entity = entry.Property(prop.Name); DBLogUpdateModel updateLog = new DBLogUpdateModel() { CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), CulumnName = prop.Name, TableName = tableName, OperatorUserName = userName, CurrentValue = entity.CurrentValue, OriginalValue = entity.OriginalValue, PrimaryKeyId = Guid.Parse(keyEntity.CurrentValue.ToString()), }; if (entity.OriginalValue == null || entity.CurrentValue == null) { if (entity.OriginalValue != entity.CurrentValue) { WriteLog(updateLog); } continue; } if (!entity.OriginalValue.Equals(entity.CurrentValue)) { WriteLog(updateLog); } } } private void WriteLog(DBLogBaseModel updateLog) { updateLog.OperatorKeyId = ""; DBLog.WriteLog(updateLog); } #endregion } }
Add and record according to each line of data (operator)
Modify, delete and record in units of each field (original value, modified value, modified time, modified person and other information)
Query, query criteria, time consumption and other information (completed in the next version)
The operator information is written dead due to the lack of global verification