DDD中的EFCore
EFCore在DDD中的使用在DDD中,我们对聚合根的操作都会通过仓储去获取聚合实例。 不过,关系型数据库通过EF也能方便的解决复杂模型的数据库映射。 本文使用EFCore,部分API不适用于EF;本文不谈DDD。 以下引出几个知识点:
让我们开始吧我们首先定义一个复杂关系的 对象模型; 大致上描述下这个
简单映射class BookEntity{ private BookEntity(string name){ Name = name; } public string Name { get; } public string BookCoverImage { get; private set; } public EnumBookType Type { get; private set; } //... } class BookEntityTypeConfiguration : IEntityTypeConfiguration<BookEntity>{ public void Configure(EntityTypeBuilder<BookEntity> builder){ builder.Property<string>("Id").HasColumnName("_id_") .HasValueGenerator<StringGuidValueGenerator>(); builder.Property(x => x.Name); builder.Property(x => x.Type) .HasConversion<string>(k => k.ToString(),v => Enum.Parse<EnumBookType>(v)); } } 在上述代码中,我们定义了一个简单对象类及它的配置项。
那么我们如何根据 主键 查询呢? EF 为我们提供了静态方法 var entity = ctx.Set<BookEntity>().FirstOrDefault(x=>EF.Property<string>(x,"Id") == "1"); 关系与固有类型在官方文档中,关系主要使用以下几种方法来配置的。
而 OwnsType (固有类型)是新近推出的API。
虽然都会创建导航属性,但是从定义和使用上来看
下面我们就对两种API进行配置。 OwnsType的配置从使用上的角度上来看, 扩展我们之前写义的实体类。 class BookEntity{ //...略 public AuthorInfo Author { get; private set; } private List<KeyWordInfo> _keyWords = new List<KeyWordInfo>(); public IEnumerable<KeyWordInfo> KeyWords => _keyWords; } class AuthorInfo { public AuthorInfo(string name){ Name = name; } public string Name { get; } } class KeyWordInfo { public KeyWordInfo(string word){ Word = word; } public string Word { get; } } 扩展配置类 class BookEntityTypeConfiguration : IEntityTypeConfiguration<BookEntity>{ builder.OwnsOne(x => x.Author,b => { b.Property(v => v.Name).HasColumnName("AuthorName"); }); builder.OwnsMany(x => x.KeyWords,b => { b.ToTable("BookKeyWords"); b.Property<int>("Id").HasColumnName("_id_"); b.HasKey("Id"); b.Property(x => x.Word); b.HasForeignKey("BookId"); }); builder.Metadata.FindNavigation(nameof(BookEntity.KeyWords)) .SetPropertyAccessMode(PropertyAccessMode.Field); } 默认情况下, 这里我们对导航属性
ReleationShip 配置如 class BookEntity{ //...略 private IList<BookChapterEntity> _chapters = new List<BookChapterEntity>(); public IEnumerable<BookChapterEntity> Chapters => _chapters; } class BookEntityTypeConfiguration : IEntityTypeConfiguration<BookEntity> { public void Configure(EntityTypeBuilder<BookEntity> builder) { //...略 builder.HasMany(x => x.Chapters).WithOne().HasForeignKey("BookId"); builder.Metadata.FindNavigation(nameof(BookEntity.Chapters)) .SetPropertyAccessMode(PropertyAccessMode.Field); } } class BookChapterEntityTypeConfiguration : IEntityTypeConfiguration<BookChapterEntity> { public void Configure(EntityTypeBuilder<BookChapterEntity> builder) { builder.Property(x => x.Title); builder.Property(x => x.Index); builder.Property<string>("Id"); } } 从配置上来看,我们的两个实体都是分开配置的,而从实体类角度上看,这里是两个类体的关系,我们配置的是 1-的关系。
backing field (支持字段)我们都知道C#中有这样子的写法。 class Foo{ public string Name{get;set;} } 写完整了是这样的。 class Foo{ private string _name; public string Name{ get{return _name;} set{_name = value;} } } 而在其它语言中,可能是这样的。 class Foo{ private string _name; public string GetName(){ return _name; } public void SetName(value){ _name = value; } } 我认为以上的 查询过滤器 Query Filter我们公司的业务设计上,数据不能真删,通过一个 IsDeleted 字段进行控制。这样在有必要的情况下,我们可以将数据进行还原。 class BookEntityTypeConfiguration : IEntityTypeConfiguration<BookEntity> { public void Configure(EntityTypeBuilder<BookEntity> builder) { //...略 builder.Property<bool>("IsDeleted"); builder.HasQueryFilter(x=>!EF.Property<bool>(x,"IsDeleted")); } } 我们通过 什么?你说又要查询的时候要查询 var allBooks = ctx.Set<BookEntity>() .IgnoreQueryFilters() .ToList(); //通过 IgnoreQueryFilters 忽视掉全局过滤器;
结尾总结在我们项目切换到DDD模式下开发的时候,使用关系型数据库作为仓储的实现真是头疼。还好,我们有EF,但是如果对EF的API和映射不熟悉的话,会导致出现因技术原因修改领域模型的情况,而这种情况是我们应该避免的。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |