首先,如果这篇文章有点长,请原谅。但是如果没有提供足够的细节,我就无法正确地解释问题。
我在寻找一种方法来将我的数据访问层(DAL)与Entity Framework实现进行解耦。虽然我正在从事的项目非常小,但如果将来我想切换到另一个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)。当然,我会使用Entity Framework特定的属性(例如[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方法就可以反映数据库中的更改。
问题出在存储库实现上,在这种情况下,我将继续使用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) {
...???
}
}
现在的问题是:当DataContext只知道具体实现时,我如何与接口一起工作?我做错了吗?
更新: @TomTom指出,既然可以拥抱Entity Framework的力量,为什么还要与它对抗呢?我想我会让EF成为抽象层。事实上,通过让EF充当数据访问层,您可以专注于项目的业务逻辑。
至于存储库/工作单元问题,我可以将多个存储库包装在一个工作单元中,或者简单地让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();
}
}