Thinking a good architecture (four)

 

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

 

Guess you like

Origin www.cnblogs.com/AnAng/p/12608826.html