[英]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);
}
}
其他建議:
SaveChanges()
方法添加到您的存儲庫 - 這應該是一個“工作單元”作業,以提交對數據庫的所有更改。 DbContext
時,如果您應該為單個“工作單元”創建一個存儲庫,則會遇到問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.