簡體   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