首先,我很抱歉,如果这是一个很长的帖子,但我不知道如何在没有所需细节的情况下以正确的方式解释问题。
我找不到从实体框架实现中抽象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();
}
}
答案 0 :(得分:2)
简单地说:是的,你做错了。你引入了一个糟糕的抽象(在功能上花费了很多)&#34;因为&#34;。 EF已经是一种抽象。
在所使用的功能方面,任何抽象都会花费你 - 这在数据库方面会带来很大的性能影响。想要一个例子吗? &#34;包含&#34;预加载导航属性(而不是延迟加载)。你将不得不解决这个问题以及许多更具体的ORM行为 - 为了获得什么?如果你放弃那些更具特色的功能,你的表现就会受到影响。
答案 1 :(得分:1)
我看不出有任何理由抽象你的模型(实体)。当您更改访问数据库的方式时,您是否希望它们发生变化?
但是如果你想保持这种方式,你可以使你的存储库接口通用,并在定义存储库时传递具体的实体类型,所以最终会得到:
public class EFWalletRepository : IWalletRepository<EFWallet>
{
public void Create(EFWallet wallet)
{
_dataContext.Add(wallet);
}
}
其他建议:
SaveChanges()
方法添加到您的存储库 - 这应该是一个“工作单元”工作,用于将所有更改提交到数据库。DbContext
,那么当您应该为单个“工作单元”创建一个存储库时,您将遇到问题。