使用实体框架时,将域和数据层分开的正确方法是什么?

我下面的示例显然是错误的,因为数据层在应用IsRelevant逻辑之前正在从存储库中获取并转换所有ThingEntities。

但是,我看不到如何在不将域逻辑放入数据层的情况下获取存储库来过滤ThingEntities。

这里正确的方法是什么?谢谢。

namespace DomainLayer
{
    public class Thing
    {
        public int Id;
        public Thing(int id) { Id = id; }
        public bool IsRelevant()
        {
            return Id%2 == 0;  // or some domain-specific logic
        }
    }

    public interface IThingRepo
    {
        List<Thing> Things { get; }
    }

    public class ThingManager
    {
        private readonly IThingRepo _thingRepo;
        public ThingManager(IThingRepo thingRepo)
        {
            _thingRepo = thingRepo;
        }
        public List<Thing> RelevantThings()
        {
            return _thingRepo.Things.Where(x => x.IsRelevant()).ToList();
        }
    }
}

namespace DataLayer
{
    public class EfDbContext : DbContext
    {
        public virtual IDbSet<ThingEntity> ThingEntities { get; set; }
    }

    public class ThingEntity
    {
        public int Id { get; set; }
    }

    public class ThingEntityMapper
    {
        public static Thing Transform(ThingEntity thingEntity)
        {
            return new Thing(thingEntity.Id);
        }
    }

    public class ThingRepo : IThingRepo
    {
        private readonly EfDbContext _context;
        public ThingRepo(EfDbContext context)
        {
            _context = context;
        }
        public List<Thing> Things
        {
            get
            {
                var result = Enumerable.Empty<Thing>();
                foreach (var thingEntity in _context.ThingEntities)
                {
                    result = result.Concat(new[] {ThingEntityMapper.Transform(thingEntity)});
                }
                return result.ToList();
            }
        }
    }
}

最佳答案

我不知道有没有正确的方法-但是从您的示例中确实可以改变一些事情。

域层应该知道数据层,反之亦然。这意味着您的映射器应该在域层中。另外,我将使数据层尽可能小,并摆脱存储库-我看不出将存储库与EF一起使用的意义。

尽管大多数人似乎都这样做,但我不会将保持状态的对象与业务逻辑混合在一起。换句话说,IsRelevent()不应位于Thing中。

考虑将ThingManager重命名为更具体的名称,以避免违反关注点分离。我将名称留在示例中,因为我不知道它会做什么,因此不知道该叫什么。

只要返回IQueryable,您就可以将查询分为单独的内部方法,然后将您的公共方法将它们全部组合在一起。这样,您可以重用诸如IsRelevent()之类的查询,但只能公开公开更多更高级别的业务特定查询。

因此,您的数据层将类似于

namespace DataLayer
{
    public class EfDbContext : DbContext
    {
        public virtual IDbSet<ThingEntity> ThingEntities { get; set; }
    }

    public class ThingEntity
    {
        public int Id { get; set; }
    }
}


和你的领域层

namespace DomainLayer
{
    public class Thing
    {
        public int Id;
    }

    internal class ThingEntityMapper
    {
        public static Thing Transform(ThingEntity thingEntity)
        {
            return new Thing{ Id = thingEntity.Id };
        }
    }

    internal interface IThingQueries
    {
        IQueryable<ThingEntity> FilterRelevent(IQueryable<ThingEntity> things);
    }

    internal class ThingQueries : IThingQueries
    {
        public IQueryable<ThingEntity> FilterRelevent(IQueryable<ThingEntity> things)
        {
            return things.Where(x => x.Id%2 == 0);
        }
    }

    public class ThingManager
    {
        private readonly DbContext _context;
        private readonly IThingQueries _queries;
        public ThingManager(DbContext _context, IThingQueries queries)
        {
            _context = context;
            _queries = queries;
        }
        public List<Thing> RelevantThings()
        {
            return _queries
                   .FilterRelevent(_context.ThingEntities)
                   .Select(ThingEntityMapper.Transform)
                   .ToList();
        }
    }
}


这可能包含使它停止构建的错误,因为它只是用来指示通用体系结构,在该体系结构中,数据层仅用于保留实体和上下文,而域层则负责其余部分。

10-06 02:02