使用Entity Framework分离数据和域层的正确方法

时间:2015-04-13 14:56:52

标签: c# entity-framework

使用Entity Framework时分离域和数据层的正确方法是什么?

下面的示例显然是错误的,因为在应用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();
            }
        }
    }
}

2 个答案:

答案 0 :(得分:0)

我不知道有正确的方法 - 但肯定会有一些事情会改变你的例子。

域层应该知道数据层,反之亦然。这意味着您的映射器应位于域层中。此外,我会保持数据层尽可能小,并摆脱存储库 - 我没有看到使用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();
        } 
    }
}

这可能包含阻止其构建的错误,因为它只是用于表示数据层仅用于保存实体和上下文以及域层执行其余操作的一般体系结构。

答案 1 :(得分:0)

实体框架几乎可以看作是关于域和数据访问层的结合和简化的公共API,所以如果你想要一个明显的分离,你通常最终会编写多余的代码来封装存储库或域层之后的所有内容(这很快就会脱离时尚)。

我的建议是,通过似乎违反关注点分离的方式来实现和平,并利用首先直接从业务逻辑中调用上下文。然后,如果您看到在单独的图层后面抽象它的理由,那就让它有机地发生,而不是从一开始就在图层之间定义一条分界线。无论是否遵守“规则”,你都会为自己节省很多时间。

我更喜欢这样思考:如果我从一开始就设计了EF,那么在我的代码中使用上下文会有多少问题吗?可能不是,因为我会查看我编写的代码,即在DAL和我的更高级逻辑之间创建一个层。

总的来说,我认为开发人员不舒服的一般原因来自于不负责设计上下文和表格抽象提供的代码。如果你是,你可能会以不同的方式看待问题。