最终答案:
我在InRequestScope()方法中使用了@WiktorZychla answer和Ninject的组合.我重新考虑我的存储库以接受上下文注入,然后在我的NinjectControllerFactory中添加了以下行:
ninjectKernel.Bind().ToSelf().InRequestScope();
(注意:我更换了:
ninjectKernel.Bind().To ().InReque??stScope().WithConstructorArgument("context",new EFDbContext());
我在其中一条评论中提到的一行,其中:
ninjectKernel.Bind().To ();
因为它导致错误)
我还用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 { KeyValuePairCreate(SellingLocation sellingLocationToAdd); KeyValuePair Get(int sellingLocationID); KeyValuePair Update(SellingLocation sellingLocationToUpdate); KeyValuePair Delete(int sellingLocationID); KeyValuePair , Exception> GetAll(); KeyValuePair
, Exception> GetAll(int agencyID); KeyValuePair
, Exception> GetFiltered(string filter); KeyValuePair
, Exception> GetFiltered(Expression
> filter); KeyValuePair DisableSellingLocations(List sellingLocationsIDs); }
和GetFiltered方法的实现,如下所示:
public KeyValuePair, Exception> GetFiltered(Expression
> 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 , Exception>(getResult, lastException); } catch (Exception ex) { lastException = ex; return new KeyValuePair
, Exception>(new List
(), 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 DbSetUsers { get; set; } public DbSet UserDatas { get; set; } public DbSet Addresses { get; set; } public DbSet SkillCategories {get;set;} public DbSet Skills {get;set;} public DbSet SkillAnswers { get; set; } //public DbSet UserSkills { get; set; } public DbSet User2Skill { get; set; } public DbSet Agencies { get; set; } public DbSet UniversalDictionaries { get; set; } public DbSet UserAddressTimeTables { get; set; } public DbSet Cities { get; set; } public DbSet 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?它必须是存储库和控制器之间的另一层,对吗?
谢谢大家的建议.
最好的祝福
而不是将上下文设置为存储库方法的本地化,为什么不反过来 - 使存储库独立于上下文:
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引擎还可以通过自动解析构造函数依赖性来显着帮助您.这是另一个故事,但可能是您的架构向前迈出的坚实一步.