繁体   English   中英

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

[英]Abstract away the DAL from Entity Framework implementation

首先,如果这将是一个很长的帖子,我很抱歉,但我不知道如何在没有所需细节的情况下以正确的方式解释问题。

我找不到从实体框架实现中抽象DAL的方法很麻烦。 我正在处理的项目非常小,但是如果将来我想切换到另一个ORM,比如NHibernate,或者只是简单的ADO.NET,我想编写代码只是为了实现,而不是整个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; }
}

正如您所看到的,这些是简单的接口,我计划在另一个库上实现,该库将包含实际的Entity Framework实现(比如MyWallet.DAL.EntityFramework)。 当然,我将使用[Key]或[ForeignKey]等实体框架特定属性来装饰实体实现。

我还在MyWallet.DAL中定义了一些存储库,如IWalletRepository,IMoneyMovementRepository,ICurrencyRepository以获取对实体的访问权限。 实际上我不知道这是否是设计实体访问权的正确方法。 当然,我还定义了工厂来实现实体的具体实现。

在我的业务层中,我定义了处理对象请求的服务,使用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
        }
    }
}

我认为这将无缝地工作,或者在最坏的情况下 - 在我有多个存储库的情况下 - 我可以共享DataContext,所以我需要只在一个上启动SaveChanges方法以反映更改数据库。

问题在于存储库实现,在这种情况下,我将继续使用实体框架:

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) {
        ...???
    }
}

现在问题是:当DataContext只知道具体实现时,我如何使用接口? 我做错了吗?

更新:

好的,基本上,正如@TomTom所说,为什么在你能够拥抱它的力量时与实体框架作斗争? 我想我只是让EF成为抽象。 实际上,通过让EF充当DAL,您可以专注于项目的业务逻辑。

并将所有内容放在一起并回复@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();
    }
}

简单地说:是的,你做错了。 你引入了一个糟糕的抽象(在功能上花费了很多)“因为”。 EF已经是一种抽象。

任何抽象都将使您在使用的功能方面付出代价 - 就数据库而言,这会带来巨大的性能影响。 想要一个例子吗? “包含”以预加载导航属性(而不是延迟加载)。 你将不得不解决这个问题以及许多更具体的ORM行为 - 为了获得什么? 如果你放弃那些更具特色的功能,你的表现就会受到影响。

我看不出有任何理由抽象你的模型(实体)。 当您更改访问数据库的方式时,您是否希望它们发生变化?

但是如果你想保持这种方式,你可以使你的存储库接口通用,并在定义存储库时传递具体的实体类型,所以你最终会得到:

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

其他建议:

  1. 您不应公开模型属性的集合。 这是违反OOP规则的 - 你应该更多地暴露一些方法来操纵对象,状态应该是更内部的。
  2. 您可能不应该将SaveChanges()方法添加到您的存储库 - 这应该是一个“工作单元”作业,以提交对数据库的所有更改。
  3. 当您在服务层中使用多个存储库时,当您为存储库创建新的DbContext时,如果您应该为单个“工作单元”创建一个存储库,则会遇到问题。

暂无
暂无

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

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