简体   繁体   English

从实体框架实现中抽象出DAL

[英]Abstract away the DAL from Entity Framework implementation

First of all I'm sorry if this is going to be a long post, but I don't know how to explain the problem in the correct way without the required details. 首先,如果这将是一个很长的帖子,我很抱歉,但我不知道如何在没有所需细节的情况下以正确的方式解释问题。

I'm having troubles finding a way to abstract my DAL from an Entity Framework implementation. 我找不到从实体框架实现中抽象DAL的方法很麻烦。 The project I'm working on is very small, but if in future I'd want to switch to another ORM like NHibernate, or just plain ADO.NET, I'd like to write code just for the implementation, not the entire DAL. 我正在处理的项目非常小,但是如果将来我想切换到另一个ORM,比如NHibernate,或者只是简单的ADO.NET,我想编写代码只是为了实现,而不是整个DAL 。

Say I have these entities in my MyWallet.DAL: 假设我在MyWallet.DAL中有这些实体:

public interface IWallet {
    long Id { get; set; }
    float TotalAmountOfMoney { get; set; }
    long CurrencyId { get; set; }
    ICurrency Currency { get; set; }
    DateTime RecordedOn { get; set; }
    ICollection<IMoneyMovement> MoneyMovements { get; set; }
}

public interface ICurrency {
    long Id { get; set; }
    char Symbol { get; set; }
    string Code { get; set; }
    string Description { get; set; }
}

public interface IMoneyMovement {
    long Id { get; set; }
    float Amount { get; set; }
    string Description { get; set; }
    long WalletId { get; set; }
    IWallet Wallet { get; set; }
    DateTime RecordedOn { get; set; }
    DateTime MovedOn { get; set; }
}

As you can see these are plain interfaces which I plan to implement on another library which will contain the actual Entity Framework implementation (say MyWallet.DAL.EntityFramework). 正如您所看到的,这些是简单的接口,我计划在另一个库上实现,该库将包含实际的Entity Framework实现(比如MyWallet.DAL.EntityFramework)。 Of course I'm going to decorate the entities implementation with Entity Framework specific attributes as [Key] or [ForeignKey] and stuff like that. 当然,我将使用[Key]或[ForeignKey]等实体框架特定属性来装饰实体实现。

I also defined some repository in MyWallet.DAL like IWalletRepository, IMoneyMovementRepository, ICurrencyRepository to gain access to the entities. 我还在MyWallet.DAL中定义了一些存储库,如IWalletRepository,IMoneyMovementRepository,ICurrencyRepository以获取对实体的访问权限。 Actually I don't know if this is the right way to design access to the entities. 实际上我不知道这是否是设计实体访问权的正确方法。 Of course I also defined factories to get the concrete implementation of the entities. 当然,我还定义了工厂来实现实体的具体实现。

In my business layer I defined services to handle the object request, work with the DAL entities and return a business object, like this: 在我的业务层中,我定义了处理对象请求的服务,使用DAL实体并返回业务对象,如下所示:

public class WalletService {
    private readonly IWalletRepository _walletRepository;
    private readonly IWalletFactory _walletFactory;

    public WalletService(IWalletRepository walletRepository, 
        IWalletFactory walletFactory) {

        _walletRepository = walletRepository;
        _walletFactory = walletFactory;
    }

    public CreatedWallet CreateWallet(CreateWalletRequest request) {
        var wallet = _walletFactory.Create();

        wallet.CurrencyId = request.CurrencyId;
        wallet.TotalAmountOfMoney = request.TotalAmountOfMoney;
        wallet.RecordedOn = DateTime.Now;

        _walletRepository.Create(wallet);
        _walletRepository.SaveChanges();

        return new CreatedWallet {
            Id = wallet.Id
        }
    }
}

I thought this was going to work seamlessly, or at worst - in a situation when I've got more than one repository - I could share the DataContext so I'd need to fire the SaveChanges method on just one to reflect the changes on the database. 我认为这将无缝地工作,或者在最坏的情况下 - 在我有多个存储库的情况下 - 我可以共享DataContext,所以我需要只在一个上启动SaveChanges方法以反映更改数据库。

The problem is with the repository implementation, in this case I'll continue with Entity Framework: 问题在于存储库实现,在这种情况下,我将继续使用实体框架:

public class EFDataContext : DbContext {
    public EFDataContext() : base ("name=MyConnectionString") {
    }

    public virtual DbSet<EFWallet> Wallets { get; set; }
    public virtual DbSet<EFMoneyMovement> MoneyMovements { get; set; }
    public virtual DbSet<EFCurrency> Currencies { get; set; }
}

public class EFWalletRepository : IWalletRepository {
    private readonly EFDbContext _dataContext;

    public EFWalletRepository(EFDbContext dataContext) {
        _dataContext = dataContext ?? new EFDbContext();
    }

    public int SaveChanges() {
        return _dataContext.SaveChanges();
    }

    public void Dispose() {
        _dataContext.Dispose();
    }

    public void Create(IWallet wallet) {
        ...???
    }
}

Now that's the problem: how do I work with interfaces when the DataContext knows only about concrete implementations? 现在问题是:当DataContext只知道具体实现时,我如何使用接口? Am I doing this all wrong? 我做错了吗?

UPDATE: 更新:

Ok so, basically, as stated out by @TomTom, why fight Entity Framework when you could just embrace its power? 好的,基本上,正如@TomTom所说,为什么在你能够拥抱它的力量时与实体框架作斗争? I guess I'll just let EF be the abstraction. 我想我只是让EF成为抽象。 In fact, by letting EF act as the DAL, you can just focus on the business logic of your project. 实际上,通过让EF充当DAL,您可以专注于项目的业务逻辑。

And to put it all together and respond to @tdragon regarding the repositories / unit of work issue: yes, I could either wrap multiple repositories inside an unit of work or simply let the DbContext be the unit of work: 并将所有内容放在一起并回复@tdragon关于存储库/工作单元问题:是的,我可以将多个存储库包装在一个工作单元内,或者只是让DbContext成为工作单元:

public class EFWalletRepository : IWalletRepository {
    private readonly EFDbContext _dataContext;

    public EFWalletRepository() {
        _dataContext = new EFDbContext();
    }

    public void Dispose() {
        _dataContext.Dispose();
    }

    public IEnumerable<Wallet> Wallets {
        get { return _dataContext.Wallets; }
    }

    public void SaveWallet(Wallet wallet) {
        if (wallet.Id == 0) {
            _dataContext.Wallets.Add(wallet);
        } else {
            var databaseEntry = _dataContext.Wallets.Find(wallet.Id);
            //update properties
        }

        _dataContext.SaveChanges();
    }
}

Simply speaking: yes, you do it wrong. 简单地说:是的,你做错了。 You introduce a bad abstraction (that costs you dearly in functionality) "because of". 你引入了一个糟糕的抽象(在功能上花费了很多)“因为”。 EF already is an abstraction. EF已经是一种抽象。

Any abstraction on top of it will cost you in terms of functionality used - which in terms of databases comes with a big performance impact. 任何抽象都将使您在使用的功能方面付出代价 - 就数据库而言,这会带来巨大的性能影响。 Want an example? 想要一个例子吗? "Include" to preload navigation properties (instead of lazy loading). “包含”以预加载导航属性(而不是延迟加载)。 You will have to work around this and a lot of more detailed behavior that is ORM specific - for the gain of having what? 你将不得不解决这个问题以及许多更具体的ORM行为 - 为了获得什么? And if you give up on those higher more specific functions your performance WILL suffer. 如果你放弃那些更具特色的功能,你的表现就会受到影响。

I can't see any reason to abstract your model (entities). 我看不出有任何理由抽象你的模型(实体)。 Do you expect them to change when you change the way you access your database? 当您更改访问数据库的方式时,您是否希望它们发生变化?

But if you want to keep it that way, you can make your repository interfaces generic, and pass the concrete entity type when defining repository, so you would end up with: 但是如果你想保持这种方式,你可以使你的存储库接口通用,并在定义存储库时传递具体的实体类型,所以你最终会得到:

public class EFWalletRepository : IWalletRepository<EFWallet>
{
    public void Create(EFWallet wallet)
    {
        _dataContext.Add(wallet);
    }
}

Other suggestions: 其他建议:

  1. You should not expose sets for your model properties. 您不应公开模型属性的集合。 It's against OOP rules - you should rather expose some methods to manipulate the objects, the state should be more internal. 这是违反OOP规则的 - 你应该更多地暴露一些方法来操纵对象,状态应该是更内部的。
  2. You probably should not add SaveChanges() method to your repository - this should be a "unit of work" job to commit all changes to the database. 您可能不应该将SaveChanges()方法添加到您的存储库 - 这应该是一个“工作单元”作业,以提交对数据库的所有更改。
  3. You would face a problem when you would use more than one repository in your service layer, as you create a new DbContext for repository, when you should have one for single "unit of work". 当您在服务层中使用多个存储库时,当您为存储库创建新的DbContext时,如果您应该为单个“工作单元”创建一个存储库,则会遇到问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM