免费手机网站模板,教育网络系统管理,北京做网站建设公司排名,网站开发商城实例29 | 定义仓储#xff1a;使用EF Core实现仓储层首先定义仓储层的接口#xff0c;以及仓储层实现的基类#xff0c;抽象类仓储层的接口namespace GeekTime.Infrastructure.Core
{/// summary/// 包含普通实体的仓储/// 约束 TEntity 必须是继承 Entity 的基类#… 29 | 定义仓储使用EF Core实现仓储层首先定义仓储层的接口以及仓储层实现的基类抽象类仓储层的接口namespace GeekTime.Infrastructure.Core
{/// summary/// 包含普通实体的仓储/// 约束 TEntity 必须是继承 Entity 的基类必须实现聚合根 IAggregateRoot/// 也就是说仓储里面存储的对象必须是一个聚合根对象/// /summary/// typeparam nameTEntity/typeparampublic interface IRepositoryTEntity where TEntity : Entity, IAggregateRoot{IUnitOfWork UnitOfWork { get; }TEntity Add(TEntity entity);TaskTEntity AddAsync(TEntity entity, CancellationToken cancellationToken default);TEntity Update(TEntity entity);TaskTEntity UpdateAsync(TEntity entity, CancellationToken cancellationToken default);bool Remove(Entity entity);// 由于没有指定主键只能根据当前实体进行删除操作Taskbool RemoveAsync(Entity entity);}/// summary/// 包含指定主键的类型的实体的仓储/// 继承了上面的接口 IRepositoryTEntity也就是说拥有了上面定义的所有方法/// 另外一个它实现了几个跟 Id 相关的操作的方法/// /summary/// typeparam nameTEntity/typeparam/// typeparam nameTKey/typeparampublic interface IRepositoryTEntity, TKey : IRepositoryTEntity where TEntity : EntityTKey, IAggregateRoot{bool Delete(TKey id);Taskbool DeleteAsync(TKey id, CancellationToken cancellationToken default);TEntity Get(TKey id);TaskTEntity GetAsync(TKey id, CancellationToken cancellationToken default);}
}具体抽象类的实现namespace GeekTime.Infrastructure.Core
{/// summary/// 定义普通实体的仓储/// 定义约束 TDbContext 必须是 EFContext也就是仓储必须依赖于 EFContext 及其子类/// 将来就可以把自己定义的比如 DomainContext 作为泛型参数传入 Repository就可以很快捷地定义出来自己的仓储/// /summary/// typeparam nameTEntity/typeparam/// typeparam nameTDbContext/typeparampublic abstract class RepositoryTEntity, TDbContext : IRepositoryTEntity where TEntity : Entity, IAggregateRoot where TDbContext : EFContext{// 具体实现需要依赖 DbContextprotected virtual TDbContext DbContext { get; set; }public Repository(TDbContext context){this.DbContext context;}public virtual IUnitOfWork UnitOfWork DbContext;// 因为 DbContext EFContext 实际上实现了 IUnitOfWork所以直接返回// 下面这些方法都是 EntityFramework 提供的能力所以就能通过简单的几行代码来实现基本的仓储操作public virtual TEntity Add(TEntity entity){return DbContext.Add(entity).Entity;}public virtual TaskTEntity AddAsync(TEntity entity, CancellationToken cancellationToken default){return Task.FromResult(Add(entity));}public virtual TEntity Update(TEntity entity){return DbContext.Update(entity).Entity;}public virtual TaskTEntity UpdateAsync(TEntity entity, CancellationToken cancellationToken default){return Task.FromResult(Update(entity));}public virtual bool Remove(Entity entity){DbContext.Remove(entity);return true;}public virtual Taskbool RemoveAsync(Entity entity){return Task.FromResult(Remove(entity));}}/// summary/// 定义主键的实体的仓储/// /summary/// typeparam nameTEntity/typeparam/// typeparam nameTKey/typeparam/// typeparam nameTDbContext/typeparampublic abstract class RepositoryTEntity, TKey, TDbContext : RepositoryTEntity, TDbContext, IRepositoryTEntity, TKey where TEntity : EntityTKey, IAggregateRoot where TDbContext : EFContext{public Repository(TDbContext context) : base(context){}/// summary/// 根据 Id 从 DbContext 获取 Entity然后再 Remove/// 这样的好处是可以跟踪对象的状态/// 坏处是任意的删除都需要先去数据库里面做查询/// /summary/// param nameid/param/// returns/returnspublic virtual bool Delete(TKey id){var entity DbContext.FindTEntity(id);if (entity null){return false;}DbContext.Remove(entity);return true;}public virtual async Taskbool DeleteAsync(TKey id, CancellationToken cancellationToken default){var entity await DbContext.FindAsyncTEntity(id, cancellationToken);if (entity null){return false;}DbContext.Remove(entity);return true;}public virtual TEntity Get(TKey id){return DbContext.FindTEntity(id);}public virtual async TaskTEntity GetAsync(TKey id, CancellationToken cancellationToken default){return await DbContext.FindAsyncTEntity(id, cancellationToken);}}}实现自己的 DbContextDomainContextnamespace GeekTime.Infrastructure
{public class DomainContext : EFContext{public DomainContext(DbContextOptions options, IMediator mediator, ICapPublisher capBus) : base(options, mediator, capBus){}public DbSetOrder Orders { get; set; }public DbSetUser Users { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder){#region 注册领域模型与数据库的映射关系modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration());modelBuilder.ApplyConfiguration(new UserEntityTypeConfiguration());#endregionbase.OnModelCreating(modelBuilder);}}
}映射关系针对每一个领域模型创建一个 EntityTypeConfigurationOrderEntityTypeConfigurationnamespace GeekTime.Infrastructure.EntityConfigurations
{class OrderEntityTypeConfiguration : IEntityTypeConfigurationOrder{public void Configure(EntityTypeBuilderOrder builder){// 定义主键builder.HasKey(p p.Id);//builder.ToTable(order);//builder.Property(p p.UserId).HasMaxLength(20);//builder.Property(p p.UserName).HasMaxLength(30);// 定义导航属性builder.OwnsOne(o o.Address, a {a.WithOwner();//a.Property(p p.City).HasMaxLength(20);//a.Property(p p.Street).HasMaxLength(50);//a.Property(p p.ZipCode).HasMaxLength(10);});}}
}UserEntityTypeConfigurationnamespace GeekTime.Infrastructure.EntityConfigurations
{class UserEntityTypeConfiguration : IEntityTypeConfigurationUser{public void Configure(EntityTypeBuilderUser builder){builder.HasKey(p p.Id);}}
}事务处理要实现对 DomainContext 的事务处理的话仅仅需要创建一个类 DomainContextTransactionBehaviornamespace GeekTime.Infrastructure
{public class DomainContextTransactionBehaviorTRequest, TResponse : TransactionBehaviorDomainContext, TRequest, TResponse{public DomainContextTransactionBehavior(DomainContext dbContext, ICapPublisher capBus, ILoggerDomainContextTransactionBehaviorTRequest, TResponse logger) : base(dbContext, capBus, logger){}}
}为了演示效果在应用程序启动时添加一行代码Startup// 这一行代码的作用是创建一个 Scope在这个范围内创建 DomainContext
using (var scope app.ApplicationServices.CreateScope())
{var dc scope.ServiceProvider.GetServiceDomainContext();// 确定数据库已经创建如果数据库没有创建这个时候会执行数据库的自动创建过程根据模型创建数据库dc.Database.EnsureCreated();
}数据库的注册部分ServiceCollectionExtensions/// summary
/// 这个定义就是将连接字符串配置到 dDomainContext
/// /summary
/// param nameservices/param
/// param nameconnectionString/param
/// returns/returns
public static IServiceCollection AddMySqlDomainContext(this IServiceCollection services, string connectionString)
{return services.AddDomainContext(builder {builder.UseMySql(connectionString);});
}这一行代码的调用位置是在 ConfigureServices 里面// 从配置中获取字符串
services.AddMySqlDomainContext(Configuration.GetValuestring(Mysql));启动程序运行过程中 EF 框架会根据定义的实体映射关系生成数据库可在 Mysql 数据库中查看生成结果接着丰富一下 Order 的映射关系namespace GeekTime.Infrastructure.EntityConfigurations
{class OrderEntityTypeConfiguration : IEntityTypeConfigurationOrder{public void Configure(EntityTypeBuilderOrder builder){// 定义主键builder.HasKey(p p.Id);builder.ToTable(order);// 修改表名为 order不带 sbuilder.Property(p p.UserId).HasMaxLength(20);// 修改字段长度builder.Property(p p.UserName).HasMaxLength(30);// 定义导航属性// OwnsOne 的方式可以将 Address 这个值类型作为同一个表的字段来设置builder.OwnsOne(o o.Address, a {a.WithOwner();a.Property(p p.City).HasMaxLength(20);a.Property(p p.Street).HasMaxLength(50);a.Property(p p.ZipCode).HasMaxLength(10);});}}
}启动程序可以看到数据库修改结果这说明可以在仓储层定义领域模型与数据库的映射关系这个映射关系可以组织为一个目录为每一个领域模型设置一个类型来定义并且这个过程是强类型的这样的结构便于后期维护另外仓储层的话定义了一个 IOrderRepository仅仅实现了 IRepository 泛型接口引进 Order由于 Order 实际上有一个主键是 long所以这里把主键类型也传给 IRepositorynamespace GeekTime.Infrastructure.Repositories
{public interface IOrderRepository : IRepositoryOrder, long{}
}Orderpublic class Order : Entitylong, IAggregateRoot这样子Order 的仓储就定义完毕那么 Order 仓储的实现也非常简单仅仅需要继承 Repository把 OrderlongDomainContext 传入泛型 Repository 即可这里还实现了 IOrderRepositorynamespace GeekTime.Infrastructure.Repositories
{public class OrderRepository : RepositoryOrder, long, DomainContext, IOrderRepository{public OrderRepository(DomainContext context) : base(context){}}
}通过这样简单的继承可以复用之前定义的代码快速实现仓储层的定义可以通过代码提升看到仓储层是有 AddUpdateRemoveDelete 方法还有 UnitOfWork 的属性这样一来就完成了仓储层的定义可以看到仓储层的代码非常的薄仅仅包含了一些接口的定义和类的继承需要自定义一些方法的时候可以在仓储层定义一些特殊方法比如 AddABC 等特殊的逻辑都可以在这里去实现namespace GeekTime.Infrastructure.Repositories
{public class OrderRepository : RepositoryOrder, long, DomainContext, IOrderRepository{public OrderRepository(DomainContext context) : base(context){}}public void AddABC(){}
}另外一个在组织领域模型和数据库的关系的时候可以很清晰的看到是在 EntityConfiguration 这个目录下面为每一个模型定义一个映射类当领域模型越来越复杂数据库的结构越来越复杂的时候这样的组织结构会非常的清晰