Separate read and write (CQRS)
Theory and a lot of documentation on the tall CQRS, where not tired, behind the technology is the same, but only my understanding, there will be a lot of text description, if you want to know more please move to another articles Kazakhstan.
What is?
Code database layer was separated read and write operations, with separate read and write functions of the database
why?
Improve database efficiency can be achieved, and may additionally incidental backtracking function
How to do it?
Chart
Internal class the ConnectionStrings { public String Key { GET ; SET ;} public String the Value { GET ; SET ;} } Internal class ConnectionStringConfig { /// <Summary> /// write library connection configuration /// </ Summary> public the ConnectionStrings CommandDB { GET ; SET ;} /// <Summary> /// read database connection configuration /// </ Summary> public List<ConnectionStrings> QueryDB { get; set; } }
Profiles ↑
Distribution management ↓
/// <Summary> /// the DC distribution plant /// </ Summary> public interface IDCDistributiveFactory { /// <Summary> /// selected libraries for a read operation /// </ Summary> String SelectQuery ( String QueryKey ); /// <Summary> /// selected for write operation, the library /// </ Summary> String the SelectCommand (); } public class DCDistributiveFactory: IDCDistributiveFactory { the IServiceProvider SP; ConnectionStringConfig config; publicDCDistributiveFactory (the IServiceProvider SP) { the this .sp = SP; var _config, sp.GetService = <IConfiguration> (); config = _config.GetSection ( " ConnectionStringConfig " ) .Get <ConnectionStringConfig> (); } /// <Summary> / // allocate a write operation /// </ Summary> public String the SelectCommand () { // replace the connection string for the database connection string write return the this .config.CommandDB.Value; } /// <Summary> ///Dispensing a read operation /// </ Summary> /// <param name = "QueryKey"> </ param> public String SelectQuery ( String QueryKey) { String Result = String .Empty; // replace the connection string is connected to a write library string IF ( String .IsNullOrWhiteSpace (QueryKey)) { Result = the this .? .config.QueryDB.OrderBy (X => Guid.NewGuid ()) FirstOrDefault () .Value; } the else { Result = config.QueryDB.FirstOrDefault (X => x.Key.Equals (QueryKey))?.Value; } IF (Result == null ) the throw new new Exception ($ " operation library selection error, there is no Key is read libraries {QueryKey} " ); return Result; } }
IDCDistributive should be called the (* ^ _ ^ *) I'm too lazy to change the, IDCDistributiveFactory also OK, but inevitably reminiscent of the factory
which is responsible for managing the database connection string
interceptor ↓ mark CQRS characteristics and Chloe's
characteristics
/// <Summary> /// this request will operate the database /// </ Summary> public class CommandAttribute: the Attribute { } /// <Summary> /// The reading operation only access /// < / Summary> public class QueryAttribute: the Attribute { /// <Summary> /// specified read Sql connection string library /// </ Summary> public string QueryKey { GET ;} /// <Summary> /// using random KEY /// </ Summary> public QueryAttribute () { } /// <Summary> /// the specified Key /// If there will be thrown /// </ Summary> /// <param name = "QueryKey"> database configuration file Key < / param> public QueryAttribute ( String QueryKey) { the this .QueryKey = QueryKey; } }
Interceptor:
/// <summary> /// 写操作拦截器 /// </summary> public class CQRSDCCommandInterceptor : IDbCommandInterceptor { IMediator mediator; public CQRSDCCommandInterceptor(IMediator mediator) { this.mediator = mediator; } public void NonQueryExecuted(IDbCommand command, DbCommandInterceptionContext<int> interceptionContext) { StringBuilder executeSql =new StringBuilder(command.CommandText); foreach (MySql.Data.MySqlClient.MySqlParameter item in command.Parameters) { executeSql.Replace(item.ParameterName, item.Value.ToString()); } mediator.Publish<DCCQRSCommandEvent>(new DCCQRSCommandEvent(executeSql.ToString(), DateTime.Now)); } public void NonQueryExecuting(IDbCommand command, DbCommandInterceptionContext<int> interceptionContext) { } public void ReaderExecuted(IDbCommand command, DbCommandInterceptionContext<IDataReader> interceptionContext) { } public void ReaderExecuting(IDbCommand command, DbCommandInterceptionContext<IDataReader> interceptionContext) { } public void ScalarExecuted(IDbCommand command, DbCommandInterceptionContext<object> interceptionContext) { } public void ScalarExecuting(IDbCommand command, DbCommandInterceptionContext<object> interceptionContext) { } }
/// <Summary> /// read interceptor /// </ Summary> public class CQRSDCQueryInterceptor: IDbCommandInterceptor { public void NonQueryExecuted (the IDbCommand Command, DbCommandInterceptionContext < int > interceptionContext) { } public void NonQueryExecuting (the IDbCommand Command, DbCommandInterceptionContext < int > interceptionContext) { command.Cancel (); the throw new new Exception ( " current context is not allowed DB DC operation, but the operation is performed in the DB method, as a method to change the controller Please perform superscript are characteristic CommandAttribute"); } public void ReaderExecuted(IDbCommand command, DbCommandInterceptionContext<IDataReader> interceptionContext) { } public void ReaderExecuting(IDbCommand command, DbCommandInterceptionContext<IDataReader> interceptionContext) { } public void ScalarExecuted(IDbCommand command, DbCommandInterceptionContext<object> interceptionContext) { } public void ScalarExecuting(IDbCommand command, DbCommandInterceptionContext<object> interceptionContext) { } }
After executing NonQueryExecuted non-query operation
before executing non-query operations NonQueryExecuting
not know anyone remember the last article I said the words Well, do not remember Ha, Papa Pa ~
Event by publishing a mediator object after the write operation is completed, tell other services Here is a write event
EventBus mediator objects are maintained by the service, I still have not said to that
in this there are several issues
1, I destroyed the principle of moving the ORM work (ORM service glare> _ <)
2, EventBus not encapsulated, exposed mediator Object
explanation:
1, CQRS cited ORM and EventBus service, CQRS their children, so I would like to temporarily change it. Another way to add objects IDbCommandInterceptor in IDCScoped in, so it's not so contrary to the principle of serving it
2, this is purely my lazy, good follow-up will change
the focus ↓
/// <summary> /// 用于辨别CQRS的操作对象 /// </summary> public class ChloeCQRSMiddleware { private readonly RequestDelegate _next; public ChloeCQRSMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext httpContext) { ReadOrWrite(httpContext); // Endpoint will be null if the URL didn't match an action, e.g. a 404 response return _next(httpContext); } public static Endpoint GetEndpoint(HttpContext context) { return context.Features.Get<IEndpointFeature>()?.Endpoint; } /// <summary> /// 读写判断 /// </summary> public static void ReadOrWrite(HttpContext httpContext) { var endpoint = GetEndpoint(httpContext); var mediator = httpContext.RequestServices.GetService<IMediator>(); IDbCommandInterceptor dbCommand; string connectionStr = string.Empty; DCOperationType OperationType = DCOperationType.Query; if (endpoint != null) { var command = endpoint.Metadata.GetMetadata<CommandAttribute>(); if (command != null) { connectionStr = httpContext.RequestServices.GetService<IDCDistributiveFactory>().SelectCommand(); OperationType = DCOperationType.Command; dbCommand = new CQRSDCCommandInterceptor(mediator); } else { var query = endpoint.Metadata.GetMetadata<QueryAttribute>(); connectionStr= httpContext.RequestServices.GetService<IDCDistributiveFactory>().SelectQuery(query?.QueryKey); OperationType = DCOperationType.Query; dbCommand = new CQRSDCQueryInterceptor(); } httpContext.RequestServices.GetService<IDCScoped>().SelectOpeation(connectionStr, OperationType); var context = httpContext.RequestServices.GetService<IDbContext>(); //添加操作拦截器 context.Session.AddInterceptor(dbCommand); } } } // Extension method used to add the middleware to the HTTP request pipeline. public static class ChloeCQRSMiddlewareExtensions { public static IApplicationBuilder UseChloeCQRSMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<ChloeCQRSMiddleware>(); } }
Middleware for the first time
It operates on the characteristics of the head identification method and operation of the DC connection string select the database objects and interceptors
Which IEndpointFeature this service needs AspNetCore2.2 version
And adding the intermediate layer CQRS app.UseEndpointRouting ();
At last
public static class Startup { public static void ConfigureServices(IServiceCollection services) { services.AddSingleton<IDCDistributiveFactory, DCDistributiveFactory>(); } public static void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseChloeCQRSMiddleware(); } }
OK, this time we introduced CQRS and implemented it,
there are two problems Kazakhstan, and then later change