zoukankan      html  css  js  c++  java
  • 我的第一个微服务系列(九):使用领域驱动创建Project项目

    一、领域驱动设计DDD

      因为在之前的工作中也未曾涉及到DDD,所以到最近才开始学习。看过《实现领域驱动设计》这本书,说实话,看完还是一知半解,在实际项目中怎么应用,也许需要锤炼才能逐渐明白。还好,在博客园里面找到了比较有深度的阐述DDD的文章进行学习,感谢。

      关于DDD的限界上下文,实体、聚合、值对象、领域服务、仓储等概念这里就不说了。个人觉得DDD最重要的还是在对业务需求的把控上,只有深入了解了业务,才能更好的界定上下文。

      DDD中认为涉及到操作数据库的需要通过聚合根来实现,其他的不应该直接的操作数据库。

      关于DDD更多的知识可以参考:

      Jesse的关于DDD的博文: https://www.cnblogs.com/jesse2013/category/610362.html 

      dax.net 关于DDD的博文: https://www.cnblogs.com/daxnet/archive/2010/11/02/1867392.html

    二、CQRS命令查询职责分离 

      CQRS本身只是一个读写分离的架构思想,全称是:Command Query Responsibility Segregation,即命令查询职责分离,表示在架构层面,将一个系统分为写入(命令)和查询两部分。一个命令表示一种意图,表示命令系统做什么修改,命令的执行结果通常不需要返回;一个查询表示向系统查询数据并返回。

      CQRS架构中,另外一个重要的概念就是事件,事件表示命令操作领域中的聚合根,然后聚合根的状态发生变化后产生的事件。

      在我们的本项目中将使用MediatR来实现CQRS。

      关于CQRS的更多内容可以学习汤雪华的博客:https://www.cnblogs.com/netfocus/

    三、MediraR

      什么是MediatR呢?我们看下其 Github 上的介绍。

      .NET中的简单中介者模式实现,一种进程内消息传递机制(无其他外部依赖)。 支持以同步或异步的形式进行请求/响应,命令,查询,通知和事件的消息传递,并通过C#泛型支持消息的智能调度。

      其核心是一个中介者模式的.NET实现,其目的是消息发送和消息处理的解耦。它支持以单播和多播形式使用同步或异步的模式来发布消息,创建和侦听事件。

      关于其使用可以参考:https://github.com/jbogard/MediatR/wiki

    四、项目分层

      我们将项目分为Project.Domain、Project.Infrastructure、Project.Api。经典的DDD分层为四层,领域层、基础设施层、应用层、展现层。这里我们把应用层和Project.Api放到一起。

      1、实现领域层Domain。定义实体,聚合根,值对象。在实体中我们可以添加或移除领域事件。

    using System;
    using MediatR;
    using System.Collections.Generic;
    
    namespace Project.Domain.Seedwork
    {
        /// <summary>
        /// Description: Entity
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:36:46
        /// </summary>
        public abstract class Entity
        {
            int? _requestedHashCode;
            int _Id;        
            public virtual  int Id 
            {
                get
                {
                    return _Id;
                }
                protected set
                {
                    _Id = value;
                }
            }
    
            private List<INotification> _domainEvents;
            public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();
    
            public void AddDomainEvent(INotification eventItem)
            {
                _domainEvents = _domainEvents ?? new List<INotification>();
                _domainEvents.Add(eventItem);
            }
    
            public void RemoveDomainEvent(INotification eventItem)
            {
                _domainEvents?.Remove(eventItem);
            }
    
            public void ClearDomainEvents()
            {
                _domainEvents?.Clear();
            }
    
            public bool IsTransient()
            {
                return this.Id == default(Int32);
            }
    
            public override bool Equals(object obj)
            {
                if (obj == null || !(obj is Entity))
                    return false;
    
                if (Object.ReferenceEquals(this, obj))
                    return true;
    
                if (this.GetType() != obj.GetType())
                    return false;
    
                Entity item = (Entity)obj;
    
                if (item.IsTransient() || this.IsTransient())
                    return false;
                else
                    return item.Id == this.Id;
            }
    
            public override int GetHashCode()
            {
                if (!IsTransient())
                {
                    if (!_requestedHashCode.HasValue)
                        _requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
    
                    return _requestedHashCode.Value;
                }
                else
                    return base.GetHashCode();
    
            }
            public static bool operator ==(Entity left, Entity right)
            {
                if (Object.Equals(left, null))
                    return (Object.Equals(right, null)) ? true : false;
                else
                    return left.Equals(right);
            }
    
            public static bool operator !=(Entity left, Entity right)
            {
                return !(left == right);
            }
        }
    }
    namespace Project.Domain.Seedwork
    {
        /// <summary>
        /// Description: IAggregateRoot
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:36:46
        /// </summary>
        public interface IAggregateRoot { }
    
    }
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Project.Domain.Seedwork
    {
        /// <summary>
        /// Description: ValueObject
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:36:46
        /// </summary>
        public abstract class ValueObject
        {
            protected static bool EqualOperator(ValueObject left, ValueObject right)
            {
                if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
                {
                    return false;
                }
                return ReferenceEquals(left, null) || left.Equals(right);
            }
    
            protected static bool NotEqualOperator(ValueObject left, ValueObject right)
            {
                return !(EqualOperator(left, right));
            }
    
            protected abstract IEnumerable<object> GetAtomicValues();
    
            public override bool Equals(object obj)
            {
                if (obj == null || obj.GetType() != GetType())
                {
                    return false;
                }
                ValueObject other = (ValueObject)obj;
                IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
                IEnumerator<object> otherValues = other.GetAtomicValues().GetEnumerator();
                while (thisValues.MoveNext() && otherValues.MoveNext())
                {
                    if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null))
                    {
                        return false;
                    }
                    if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current))
                    {
                        return false;
                    }
                }
                return !thisValues.MoveNext() && !otherValues.MoveNext();
            }
    
            public override int GetHashCode()
            {
                return GetAtomicValues()
                 .Select(x => x != null ? x.GetHashCode() : 0)
                 .Aggregate((x, y) => x ^ y);
            }
    
            public ValueObject GetCopy()
            {
                return this.MemberwiseClone() as ValueObject;
            }
        }
    }

      定义仓储接口

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Project.Domain.Seedwork
    {
        /// <summary>
        /// Description: IUnitOfWork
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:36:46
        /// </summary>
        public interface IUnitOfWork : IDisposable
        {        
            Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
            Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken));
        }
    }
    namespace Project.Domain.Seedwork
    {
        /// <summary>
        /// Description: IRepository
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:36:46
        /// </summary>
        public interface IRepository<T> where T : IAggregateRoot
        {
            IUnitOfWork UnitOfWork { get; }
        }
    }
    using Project.Domain.Seedwork;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Project.Domain.AggregatesModel
    {
    
        /// <summary>
        /// Description: IProjectRepository
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:30:09
        /// </summary>
        public interface IProjectRepository : IRepository<Project>
        {
            Task<Project> GetAsync(int id);
    
            Project Add(Project project);
    
            void Update(Project project);
        }
    }

      在此项目中,项目查看者和项目参与者都是需要现有项目后才能拥有,因此将项目作为聚合根来对待。

    using Project.Domain.Events;
    using Project.Domain.Seedwork;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Project.Domain.AggregatesModel
    {
        /// <summary>
        /// Description: Project
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:30:23
        /// </summary>
        public class Project : Entity,IAggregateRoot
        {
            /// <summary>
            /// 用户Id
            /// </summary>
            public int UserId { get; set; }
    
            /// <summary>
            /// 用户名称
            /// </summary>
            public string UserName { get; set; }
    
            /// <summary>
            ///  项目logo
            /// </summary>
            public string Avatar { get; set; }
    
            /// <summary>
            /// 公司名称
            /// </summary>
            public string Company { get; set; }
    
            /// <summary>
            /// 原BP文件地址
            /// </summary>
            public string OriginBPFile { get; set; }
    
            /// <summary>
            /// 转换后的BP文件地址
            /// </summary>
            public string FormatBPFile { get; set; }
    
            /// <summary>
            /// 是否显示敏感信息
            /// </summary>
            public bool  ShowSecurityInfo { get; set; }
    
            /// <summary>
            /// 公司所在省Id
            /// </summary>
            public int ProvinceId { get; set; }
    
            /// <summary>
            /// 公司所在省名称
            /// </summary>
            public string Province { get; set; }
    
            /// <summary>
            /// 公司所在城市Id
            /// </summary>
            public int CityId { get; set; }
    
            /// <summary>
            /// 公司所在城市名称
            /// </summary>
            public string City { get; set; }
    
            /// <summary>
            /// 区域Id
            /// </summary>
            public int AreaId { get; set; }
    
            /// <summary>
            /// 区域名称
            /// </summary>
            public string AreaName { get; set; }
    
            /// <summary>
            /// 公司成立时间
            /// </summary>
            public DateTime RegisterTime { get; set; }
    
            /// <summary>
            /// 项目基本信息
            /// </summary>
            public string Introduction { get; set; }
    
            /// <summary>
            /// 出让股份比例
            /// </summary>
            public string FinPercentage { get; set; }
    
            /// <summary>
            /// 融资阶段
            /// </summary>
            public string FinStage { get; set; }
    
            /// <summary>
            /// 融资金额 单位(万)
            /// </summary>
            public int FinMoney { get; set; }
    
            /// <summary>
            /// 收入 单位(万)
            /// </summary>
            public int Income { get; set; }
    
            /// <summary>
            /// 利润 单位(万)
            /// </summary>
            public int Revenue { get; set; }
    
            /// <summary>
            /// 估值 单位(万)
            /// </summary>
            public int Valuation { get; set; }
    
            /// <summary>
            /// 佣金分配方式 : 线下商议  等比例分配
            /// </summary>
            public int BrokerageOptions { get; set; }
    
    
            /// <summary>
            /// 是否委托给平台finbook
            /// </summary>
            public bool OnPlatform { get; set; }
    
            /// <summary>
            /// 可见范围设置
            /// </summary>
            public ProjectVisibleRule VisibleRule { get; set; }
    
            /// <summary>
            /// 根引用项目Id
            /// </summary>
            public int SourceId { get; set; }
    
            /// <summary>
            /// 上级引用项目Id
            /// </summary>
            public int ReferenceId { get; set; }
    
            /// <summary>
            /// 项目标签
            /// </summary>
            public string Tags { get; set; }
    
            /// <summary>
            /// 项目属性:行业领域、融资币种
            /// </summary>
            public List<ProjectProperty> Properties { get; set; }
    
            /// <summary>
            /// 贡献者列表
            /// </summary>
            public List<ProjectContributor> Contributors { get; set; }
    
            /// <summary>
            /// 查看者
            /// </summary>
            public List<ProjectViewer> Viewers { get; set; }
    
    
            /// <summary>
            /// 更新时间
            /// </summary>
            public DateTime UpdateTime { get; set; }
    
            /// <summary>
            /// 创建时间
            /// </summary>
            public DateTime CreatedTime { get; set; }
    
            private Project Clone(Project source = null)
            {
                if (source == null)
                {
                    source = this;
                }
    
                var newProject = new Project()
                {
                    AreaId = source.AreaId,
                    AreaName = source.AreaName,
                    Avatar = source.Avatar,
                    BrokerageOptions = source.BrokerageOptions,
                    City = source.City,
                    CityId = source.CityId,
                    Company = source.Company,
                    Contributors = new List<ProjectContributor> { },
                    CreatedTime = DateTime.Now,
                    FinMoney = source.FinMoney,
                    FinPercentage = source.FinPercentage,
                    FinStage = source.FinStage,
                    FormatBPFile = source.FormatBPFile,
                    Income = source.Income,
                    Introduction = source.Introduction,
                    OnPlatform = source.OnPlatform,
                    OriginBPFile = source.OriginBPFile,
                    Province = source.Province,
                    ProvinceId = source.ProvinceId,
                    ReferenceId = source.ReferenceId,
                    RegisterTime = source.RegisterTime,
                    Revenue = source.Revenue,
                    ShowSecurityInfo = source.ShowSecurityInfo,
                    SourceId = source.SourceId,
                    Tags = source.Tags,
                    UpdateTime = source.UpdateTime,
                    UserId = source.UserId,
                    Valuation = source.Valuation,
                    Viewers = new List<ProjectViewer> { },
                    VisibleRule = source.VisibleRule  == null ? null : new ProjectVisibleRule
                    {
                        Visible = source.VisibleRule.Visible,
                        Tags = source.VisibleRule.Tags
                    }
                };
    
                newProject.Properties = new List<ProjectProperty> { };
                foreach(var item in source.Properties)
                {
                    newProject.Properties.Add(new ProjectProperty(item.Key, item.Text, item.Value));
                }
    
                return newProject;
            }
    
            /// <summary>
            /// 参与者得到项目拷贝
            /// </summary>
            /// <param name="contributorId"></param>
            /// <param name="source"></param>
            /// <returns></returns>
            public Project ContributorFork(int contributorId,Project source = null)
            {
                if(source == null)
                {
                    source = this;
                }
    
                var newProject = Clone(source);
    
                newProject.UserId = contributorId;
                newProject.SourceId = source.SourceId;
                newProject.ReferenceId = source.ReferenceId;
                newProject.UpdateTime = DateTime.Now;
    
                return newProject;
            }
    
    
            public Project()
            {
                this.Viewers = new List<ProjectViewer>();
                this.Contributors = new List<ProjectContributor>();
    
                AddDomainEvent(new ProjectCreatedEvent { Project = this }); //添加创建项目事件
            }
    
    
            public void AddViewer(int userId,string userName,string avatar)
            {
    
                ProjectViewer viewer = new ProjectViewer() {
                    UserId = UserId,
                    UserName = userName,
                    Avatar = avatar,
                    CreatedTime = DateTime.Now
                };
    
                if(!Viewers.Any(v=>v.UserId == userId))
                {
                    Viewers.Add(viewer);
    
                    AddDomainEvent(new ProjectViewedEvent {
                        Company = this.Company,
                        Introduction = this.Introduction,
                        Viewer = viewer });
                }
                
            }
    
            public void AddContributor(ProjectContributor contributor)
            {
                if (!Contributors.Any(c => c.UserId == contributor.UserId))
                {
                    Contributors.Add(contributor);
                    AddDomainEvent(new ProjectJoinedEvent {
                        Company = this.Company,
                        Introduction = this.Introduction,
                        Contributor = contributor });
                }
            }
        }
    }
    using Project.Domain.Seedwork;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Project.Domain.AggregatesModel
    {
    
        /// <summary>
        /// Description: ProjectContributor
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:30:46
        /// </summary>
        public class ProjectContributor : Entity
        {
            public int ProjectId { get; set; }
    
            public int UserId { get; set; }
    
            public string UserName { get; set; }
    
            public string Avatar { get; set; }
    
            public DateTime CreatedTime { get; set; }
    
            /// <summary>
            /// 关闭者
            /// </summary>
            public bool IsCloser { get; set; }
    
            /// <summary>
            /// 参与者类型:1 财务顾问  2 投资机构
            /// </summary>
            public int ContributorType { get; set; }
        }
    }
    using Project.Domain.Seedwork;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Project.Domain.AggregatesModel
    {
    
        /// <summary>
        /// Description: ProjectProperty
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:31:01
        /// </summary>
        public class ProjectProperty : ValueObject
        {
            public ProjectProperty(string key,string text,string value)
            {
                this.Key = key;
                this.Text = text;
                this.Value = value;
            }
    
            public string Key { get; set; }
    
            public string Text { get; set; }
    
            public string Value { get; set; }
    
            public int ProjectId { get; set; } //违背了DDD值对象
    
            protected override IEnumerable<object> GetAtomicValues()
            {
                yield return Key;
                yield return Text;
                yield return Value;
            }
        }
    }
    using Project.Domain.Seedwork;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Project.Domain.AggregatesModel
    {
    
        /// <summary>
        /// Description: ProjectViewer
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:31:13
        /// </summary>
        public class ProjectViewer : Entity
        {
            public int ProjectId { get; set; }
    
            public int UserId { get; set; }
    
            public string UserName { get; set; }
    
            public string Avatar { get; set; }
    
            public DateTime CreatedTime { get; set; }
        }
    }

      同时,当项目被创建、查看、参与的时候可能需要发布一些事件,所以我们定义如下领域事件。

    using MediatR;
    namespace Project.Domain.Events
    {
    
        /// <summary>
        /// Description: ProjectCreatedEvent
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/10 22:15:56
        /// </summary>
        public class ProjectCreatedEvent :INotification
        {
            public AggregatesModel.Project Project { get; set; }
        }
    }
    
    
    using MediatR;
    using Project.Domain.AggregatesModel;
    
    namespace Project.Domain.Events
    {
    
        /// <summary>
        /// Description: ProjectJoinedEvent
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/10 22:16:25
        /// </summary>
        public class ProjectJoinedEvent : INotification
        {
            public string Company { get; set; }
    
            public string Introduction { get; set; }
    
            public ProjectContributor Contributor { get; set; }
        }
    }
    
    using MediatR;
    using Project.Domain.AggregatesModel;
    
    namespace Project.Domain.Events
    {
    
        /// <summary>
        /// Description: ProjectViewedEvent
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/10 22:16:09
        /// </summary>
        public class ProjectViewedEvent :INotification
        {
            public string Company { get; set; }
    
            public string Introduction { get; set; }
    
            public ProjectViewer Viewer { get; set; }
        }
    }

      2、实现基础设施层

      在基础设施层中,我们将定义数据访问上下文和具体的仓储实现

    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Project.Domain.Seedwork;
    using System.Threading;
    using System.Threading.Tasks;
    using MediatR;
    using Project.Infrastructure.EntityConfigurations;
    
    namespace Project.Infrastructure
    {
    
        /// <summary>
        /// Description: ProjectContext
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:26:13
        /// </summary>
        public class ProjectContext : DbContext, IUnitOfWork
        {
            private IMediator _mediator;
    
            public DbSet<Domain.AggregatesModel.Project> Projects { get; set; }
    
            public ProjectContext(DbContextOptions<ProjectContext> options,
                IMediator mediator) : base(options)
            {
                _mediator = mediator;
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.ApplyConfiguration(new ProjectEntityConfiguration());
                modelBuilder.ApplyConfiguration(new ProjectPropertyConfiguration());
                modelBuilder.ApplyConfiguration(new ProjectPropertyConfiguration());
                modelBuilder.ApplyConfiguration(new ProjectViewerConfiguration());
                modelBuilder.ApplyConfiguration(new ProjectContributorConfiguration());
    
                //modelBuilder.Entity<Domain.AggregatesModel.Project>()
                //    .ToTable("Projects")
                //    .HasKey(p => p.Id);
    
    
    
    
            }
    
            public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken= default(CancellationToken))
            {
                //发送领域事件
                await _mediator.DispatchDomainEventsAsync(this);
    
                await base.SaveChangesAsync();
    
                return true;
            }
        }
    }
    using Project.Domain.AggregatesModel;
    using Project.Domain.Seedwork;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    using System.Linq;
    using ProjectEntity = Project.Domain.AggregatesModel.Project;
    using Microsoft.EntityFrameworkCore;
    
    namespace Project.Infrastructure.Repositories
    {
    
        /// <summary>
        /// Description: ProjectRepository
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 23:29:29
        /// </summary>
        public class ProjectRepository : IProjectRepository
        {
            private readonly ProjectContext _context;
            public IUnitOfWork UnitOfWork => _context;
    
            public ProjectRepository(ProjectContext cotnext)
            {
                _context = cotnext;
            }
    
            public ProjectEntity Add(ProjectEntity project)
            {
                if (project.IsTransient())
                {
                    return _context.Add(project).Entity;
                }
                else
                {
                    return project;
                }
            }
    
            public async Task<ProjectEntity> GetAsync(int id)
            {
                return await _context.Projects.Include(p => p.Properties)
                    .Include(p => p.Viewers)
                    .Include(p => p.Contributors)
                    .Include(p => p.VisibleRule)
                    .SingleOrDefaultAsync();
            }
    
            public void Update(ProjectEntity project)
            {
                _context.Update(project);
            }
        }
    }
    using MediatR;
    using Project.Domain.Seedwork;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace Project.Infrastructure
    {
    
        /// <summary>
        /// Description: MediatorExtension
        /// Author: Jesen
        /// Version: 1.0
        /// Date: 2019/8/8 22:25:51
        /// </summary>
        static class MediatorExtension
        {
            public static async Task DispatchDomainEventsAsync(this IMediator mediator, ProjectContext ctx)
            {
                var domainEntities = ctx.ChangeTracker
                    .Entries<Entity>()
                    .Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any());
    
                var domainEvents = domainEntities
                    .SelectMany(x => x.Entity.DomainEvents)
                    .ToList();
    
                domainEntities.ToList()
                    .ForEach(entity => entity.Entity.ClearDomainEvents());
    
                var tasks = domainEvents
                    .Select(async (domainEvent) => {
                        await mediator.Publish(domainEvent);
                    });
    
                await Task.WhenAll(tasks);
            }
        }
    }

      3、完成应用层

      在应用层中,我们采用CQRS架构,利用MediatR来实现Command和CommandHandler,在CommandHandler中调用ProjectContext的Save方法出发领域事件处理程序,其也在Application层实现,而在领域事件处理程序中,又可以触发集成事件,而在其他服务中订阅这个集成事件。

    using MediatR;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using ProjectEntity = Project.Domain.AggregatesModel.Project;
    
    namespace Project.Api.Applications.Commands
    {
        public class CreateProjectCommand : IRequest<ProjectEntity>
        {
            public ProjectEntity Project { get; set; }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using MediatR;
    using Project.Domain;
    using Project.Domain.AggregatesModel;
    using ProjectEntity = Project.Domain.AggregatesModel.Project;
    
    namespace Project.Api.Applications.Commands
    {
        public class CreateProjectCommandHandler : IRequestHandler<CreateProjectCommand, ProjectEntity>
        {
            private IProjectRepository _projectRepository;
    
            public CreateProjectCommandHandler(IProjectRepository projectRepository)
            {
                _projectRepository = projectRepository;
            }
    
            public async Task<ProjectEntity> Handle(CreateProjectCommand request, CancellationToken cancellationToken)
            {
                _projectRepository.Add(request.Project);
    
                await _projectRepository.UnitOfWork.SaveEntitiesAsync();
    
                return request.Project;
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using DotNetCore.CAP;
    using MediatR;
    using Project.Api.Applications.IntergrationEvents;
    using Project.Domain.Events;
    
    namespace Project.Api.Applications.DomainEventHandlers
    {
        public class ProjectCreatedDomainEventHandler : INotificationHandler<ProjectCreatedEvent>
        {
            private ICapPublisher _capPublisher;
            public ProjectCreatedDomainEventHandler(ICapPublisher capPublisher)
            {
                _capPublisher = capPublisher;
            }
    
            public Task Handle(ProjectCreatedEvent notification, CancellationToken cancellationToken)
            {
                var @event = new ProjectCreatedIntergrationEvent
                {
                    CreatedTime = DateTime.Now,
                    ProjectId = notification.Project.Id,
                    UserId = notification.Project.UserId
                };
                _capPublisher.Publish("finbook.projectapi.projectcreated",@event);
    
                return Task.CompletedTask;
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace Project.Api.Applications.IntergrationEvents
    {
        public class ProjectCreatedIntergrationEvent
        {
            public int ProjectId { get; set; }
    
            public int UserId { get; set; }
    
            public DateTime CreatedTime { get; set; }
    
            public int FromUserId { get; set; }
    
            public string FromUserName { get; set; }
    
            public string FromUserAvatar { get; set; }
    
            /// <summary>
            /// 项目logo
            /// </summary>
            public string ProjectAvatar { get; set; }
    
            /// <summary>
            /// 公司名称
            /// </summary>
            public string Company { get; set; }
    
            /// <summary>
            /// 项目介绍
            /// </summary>
            public string Introduction { get; set; }
    
            /// <summary>
            /// 项目标签
            /// </summary>
            public string Tags { get; set; }
    
            /// <summary>
            /// 融资阶段
            /// </summary>
            public string FinStage { get; set; }
    
        }
    }

      而Query就显得比较简单

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace Project.Api.Applications.Queries
    {
        public interface IProjectQueries
        {
            Task<dynamic> GetProjectByUserId(int userId);
    
            Task<dynamic> GetProjectDetail(int projectId);
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Dapper;
    using Microsoft.EntityFrameworkCore;
    using MySql.Data.MySqlClient;
    using Project.Infrastructure;
    
    namespace Project.Api.Applications.Queries
    {
        public class ProjectQueries : IProjectQueries
        {
            private readonly ProjectContext _context;
    
            public ProjectQueries(ProjectContext context)
            {
                _context = context;
            }
    
            public async Task<dynamic> GetProjectByUserId(int userId)
            {
                using (var conn = _context.Database.GetDbConnection())
                {
                    conn.Open();
    
                    var sql = @"SELECT Projects.Id,Projects.Avatar,Projects.Company,
    Projects.FinStage,Projects.Introduction,Projects.ShowSecurityInfo,Projects.CreatedTime
    FROM Projects WHERE Projects.UserId=@userId;";
    
                    var result = await conn.QueryAsync<dynamic>(sql,new { userId });
    
                    return result;
                }
            }
    
            public async Task<dynamic> GetProjectDetail(int projectId)
            {
                //using(var conn = new MySqlConnection(_connStr))
                using (var conn = _context.Database.GetDbConnection())
                {
                    conn.Open();
    
                    string sql = @"SELECT
                                    b.Visible,
                                    b.Tags,
                                    a.UserId,
                                    a.Avatar,
                                    a.Company,
                                    a.OriginBPFile,
                                    a.FormatBPFile,
                                    a.ShowSecurityInfo,
                                    a.Province,
                                    a.AreaName,
                                    a.RegisterTime,
                                    a.Introduction,
                                    a.FinPercentage,
                                    a.FinStage,
                                    a.FinMoney,
                                    a.Income,
                                    a.Revenue,
                                    a.Valuation,
                                    a.BrokerageOptions,
                                    a.OnPlatform,
                                    a.Tags,
                                    a.UserName,
                                    a.City 
                                FROM
                                    Projects AS a
                                    INNER JOIN ProjectVisibleRule AS b ON a.Id = b.ProjectId 
                                WHERE
                                    a.Id = @projectId;";
    
                    var result = await conn.QueryAsync<dynamic>(sql, new { projectId });
    
                    return result;
                }
            }
        }
    }

      最后需要注入MediatR

    services.AddScoped<IProjectQueries, ProjectQueries>();
    services.AddScoped<IRecommendService, RecommendService>();
    services.AddScoped<IProjectRepository, ProjectRepository>();
    
    //从CreateProjectCommand所在程序集中扫描相应的类进行注入
    services.AddMediatR(typeof(Applications.Commands.CreateProjectCommand).Assembly);
    //services.AddMediatR();
  • 相关阅读:
    刷题62—生命游戏
    刷题61—有效括号的嵌套深度
    system.transfer.list深度解析
    recovery 升级界面顶部花屏问题分析
    recovery 升级过程LED灯闪烁
    recovery 差分升级包制作超时
    recovery 升级过程执行自定义shell命令
    recovery log直接输出到串口
    android recovery代码修改之原生建议
    android recovery 升级UI显示之资源文件
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11550241.html
Copyright © 2011-2022 走看看