【发布时间】:2014-01-28 14:47:07
【问题描述】:
最终答案:
我在 InRequestScope() 方法中使用了@WiktorZychla 答案和 Ninject 的组合。我重构了我的存储库以接受上下文注入,然后在我的 NinjectControllerFactory 中添加了以下行:
ninjectKernel.Bind<EFDbContext>().ToSelf().InRequestScope();
(注意:我替换了:
ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>().InRequestScope().WithConstructorArgument("context",new EFDbContext());
我在其中一个 cmets 中提到的行,带有:
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 和其他方法中)更改为“使用”语句,类似于以下内容:
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();
只是为了简单 Get 以我想要的方式工作,否则每次我尝试访问 Address 时,例如,我得到:“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 内部?它必须是存储库和控制器之间的另一层,对吗?
谢谢大家的建议。
最好的问候
【问题讨论】:
-
您能否添加产生您所指错误的代码? (可能放在你写“...一些操作...”的地方)
-
关于这个话题有很多可以说和已经说过的。通常,您希望上下文的生命周期与您的工作单元相同。
-
@Askolein:我回家后(1-2 小时)会添加更多代码。
-
@Aaron Palmer:问题是 - 我需要填充数据表,所以我想获取数据然后为我的表处理它,问题是由于延迟加载,我不能访问一些属性(即使我返回如下内容: context.City.Where(someCondition).ToList() - 因为我得到的是 City 的实体代理,而不是 City POCO 本身)。如何处理这样的事情?
-
在给定的请求(你的工作单元)中,如果你总是要加载城市,那么你应该急切地加载它们。
标签: c# asp.net-mvc entity-framework lazy-loading code-first