最终答案:
我在InRequestScope()方法中使用了@WiktorZychla answer和Ninject的组合。我重新考虑了我的存储库以接受上下文的注入,然后在我的NinjectControllerFactory中添加了一行:
ninjectKernel.Bind<EFDbContext>().ToSelf().InRequestScope();
(注意:我替换了:
ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>().InRequestScope().WithConstructorArgument("context",new EFDbContext());
我在其中一条评论中提到过:
ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>();
因为它导致了错误)
我还使用nuget 安装了 Ninject.MVC3,并创建了文件:“NinjectWebCommon.cs”,其中包含以下行:
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
虽然有人说这一行是可选的,但其他文章指出应该使用它来使InRequestScope在MVC站点中正常工作。
原始问题:
我目前只有很少的EF存储库,每个都看起来类似于以下内容:
public class EFCityRepository : ICityRepository
{
private EFDbContext context = new EFDbContext();
public bool Create(City cityToCreate)
{
...
}
public City Get(int cityID)
{
...
}
}
正如你所看到的,现在我正在使用单个全局EFDbContext进行所有操作,从我读到的这个是坏的 - 所以我尝试将它(在Create,Get和其他方法中)更改为“using”语句,如下所示:
public City Get(int cityID)
{
using(EFDbContext context)
{
...some opeartions…
return entities;
}
}
现在我遇到了许多与实体延迟加载有关的问题,我必须使用类似下面的内容:
context.Entry(getResult.FirstOrDefault()).Reference(x => x.Address).Load();
context.Entry(getResult.FirstOrDefault()).Reference(x => x.Agency).Load();
context.Entry(getResult.FirstOrDefault().Address).Reference(x => x.City).Load();
只是为了简化以我想要的方式工作,否则每次我尝试访问地址时,我得到:“ObjectContext实例已被处理,不能再用于需要连接的操作” 。当然,当上下文是全局的时候,它可以正常工作。
我需要一些建议:我应该使用本地上下文并使用急切加载而不是延迟加载?或者这里的全球背景是否可以接受?
还有一个假设是使用延迟加载的人呢?正如我所看到的那样 - 我必须为使用某个存储库的每个操作编写单独的逻辑 - 或者我错了吗?
编辑1:
@Askolein:
好的,目前我的应用程序包含几个子项目:
Common
Domain - here I have my repositories
Helpers
Utils
WebUI
我用来触发错误的存储库如下所示:
public interface ISellingLocation
{
KeyValuePair<bool, Exception> Create(SellingLocation sellingLocationToAdd);
KeyValuePair<SellingLocation, Exception> Get(int sellingLocationID);
KeyValuePair<bool, Exception> Update(SellingLocation sellingLocationToUpdate);
KeyValuePair<bool, Exception> Delete(int sellingLocationID);
KeyValuePair<List<SellingLocation>, Exception> GetAll();
KeyValuePair<List<SellingLocation>, Exception> GetAll(int agencyID);
KeyValuePair<List<SellingLocation>, Exception> GetFiltered(string filter);
KeyValuePair<List<SellingLocation>, Exception> GetFiltered(Expression<Func<SellingLocation, bool>> filter);
KeyValuePair<bool, Exception> DisableSellingLocations(List<int> sellingLocationsIDs);
}
GetFiltered方法的实现,如下所示:
public KeyValuePair<List<SellingLocation>, Exception> GetFiltered(Expression<Func<SellingLocation, bool>> filter)
{
Exception lastException = null;
using (var transaction = new TransactionScope())
{
using (EFDbContext context = new EFDbContext())
{
try
{
var getResult = context.SellingPoints.Where(filter).ToList();
//var getResult2 = getResult.ToList();
context.Entry(getResult.FirstOrDefault()).Reference(x => x.Address).Load();
context.Entry(getResult.FirstOrDefault()).Reference(x => x.Agency).Load();
context.Entry(getResult.FirstOrDefault().Address).Reference(x => x.City).Load();
transaction.Complete();
return new KeyValuePair<List<SellingLocation>, Exception>(getResult, lastException);
}
catch (Exception ex)
{
lastException = ex;
return new KeyValuePair<List<SellingLocation>, Exception>(new List<SellingLocation>(), ex);
}
}
}
}
我在我的控制器中调用这个方法是这样的:
var allSellingLocationsForCurrentUser = sellingLocationRepository.GetFiltered(x => x.IsEnabled);
if(allSellingLocationsForCurrentUser.Value == null)
{
AgencySalesSellingLocationsListViewModel agencySalesSellingLocationsListViewModel = new AgencySalesSellingLocationsListViewModel();
foreach (var item in allSellingLocationsForCurrentUser.Key)
{
agencySalesSellingLocationsListViewModel.aaData.Add(new AgencySalesSellingLocationsListViewModelRow()
{
ID = item.SellingLocationID,
DT_RowId = item.SellingLocationID.ToString(),
Name = item.Name,
City = item.Address.City.Name,
Street = item.Address.Street
});
}
return Json(agencySalesSellingLocationsListViewModel, JsonRequestBehavior.AllowGet);
}
我理解为什么我会收到错误,正如我之前所说 - 如果我明确告诉实体,加载:地址,代理商和地址。城市 - 它会正常工作。
@WiktorZychla:
我当前的DataContext看起来像这样:
public class EFDbContext : DbContext
{
public EFDbContext():base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<UserData> UserDatas { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<SkillCategory> SkillCategories {get;set;}
public DbSet<Skill> Skills {get;set;}
public DbSet<SkillAnswer> SkillAnswers { get; set; }
//public DbSet<UserSkills> UserSkills { get; set; }
public DbSet<User2Skill> User2Skill { get; set; }
public DbSet<Agency> Agencies { get; set; }
public DbSet<UniversalDictionary> UniversalDictionaries { get; set; }
public DbSet<UserAddressTimeTable> UserAddressTimeTables { get; set; }
public DbSet<City> Cities { get; set; }
public DbSet<SellingLocation> SellingPoints { get; set; }
}
如果我理解正确 - 我必须封装我的EFCityRepository并使用类似的东西:
using(SomeContext context = new SomeContext())
{
EFCityRepository repository = new EFCityRepository(context);
var result = repository.Get(id);
...do some work...
}
这不是有点矫枉过正吗?现在我使用Ninject,并使用存储库接口(IUserRepository,IUserRepository等)注入我的控制器 - 所以我的控制器方法就像工作单元一样工作,如果我理解正确 - 我必须要么:在我的控制器方法中注入我的DbContext ,或者在控制器方法和存储库之间创建另一个层......我能正确理解吗?
@cosset:
正如我上面所说 - 我认为我的控制器方法是工作单元...如果我要实现你建议的东西 - 我应该把它放在哪里?内部域或WebUI?它必须是存储库和控制器之间的另一层,对吗?
谢谢大家的建议。
祝你好运
答案 0 :(得分:6)
不是让上下文本地存储库方法,为什么不反过来 - 使存储库独立于上下文:
public class EFCityRepository : ICityRepository
{
public EFCityRepository( EFDbContext context )
{
this.context = context;
}
private EFDbContext context;
public bool Create(City cityToCreate)
{
...
}
public City Get(int cityID)
{
...
}
}
这种方法为您提供了最佳的灵活性。您可以在存储库之间共享相同的上下文,每个存储库都可以有额外的上下文,无论如何。
对于基于Web的应用程序,通常的做法是让您的上下文“按请求”共享,这意味着在单个请求中使用完全相同的上下文,并将其置于请求管道的末尾。
按照Maess的建议编辑:,您一定要考虑通过依赖注入引擎(如Unity或Ninject)半自动管理上下文生命周期的可能性。 DI引擎还可以通过自动解析构造函数依赖性来显着帮助您。这是另一个故事,但可能是您的架构向前迈出的坚实一步。
答案 1 :(得分:1)
我建议使用使用模式UnitOfWork.Simple实现。
public interface IUnitOfWork : IDisposable
{
void Save();
}
public class EntityFrameworkUnitOfWork : IUnitOfWork
{
private EFDbContext context = new EFDbContext ();
internal ICityRepository cityRepo;
public ICityRepository CityRepository
{
get
{
if (cityRepo== null)
{
cityRepo = new EFCityRepository(context);
}
return cityRepo;
}
}
}