简体   繁体   English

实体框架-全局vs本地上下文vs延迟加载

[英]Entity framework - global vs local context vs lazy loading

Final answer: 最终答案:

I used the combination of @WiktorZychla answer and Ninject in InRequestScope() method. 我在InRequestScope()方法中使用了@WiktorZychla答案和Ninject的组合。 I re factored my repository to accept injections of context and then inside my NinjectControllerFactory I added the line: 我重构了我的存储库以接受上下文的注入,然后在我的NinjectControllerFactory内部添加了以下行:

ninjectKernel.Bind<EFDbContext>().ToSelf().InRequestScope();

(note: I replaced the: (注意:我替换了:

ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>().InReque‌​stScope().WithConstructorArgument("context",new EFDbContext());

line I mentioned in one of the comments, with the: 我在其中一项评论中提到的代码行:

ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>();

as it was causing errors) 因为它导致错误)

I also installed Ninject.MVC3 with nuget , and it created the file: "NinjectWebCommon.cs" with the line: 我还使用nuget安装了Ninject.MVC3 ,它创建了文件“ NinjectWebCommon.cs”,内容如下:

DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));

While some say this line is optional, other articles state it should be used in order for InRequestScope to work properly in MVC sites. 虽然有人说这行是可选的,但其他文章指出应该使用它,以便InRequestScope在MVC站点中正常工作。

Original qestion: 原始问题:

I currently have few EF repositories, every one looks similar to the following: 我目前没有几个EF储存库,每个储存库看起来都类似于以下内容:

public class EFCityRepository : ICityRepository
{
private EFDbContext context = new EFDbContext();

public bool Create(City cityToCreate)
{
...
}

public City Get(int cityID)
{
...
}
}

As you can see, right now I'm using single global EFDbContext for all operations, from what I read this is bad - so I tried changing it (inside the Create, Get and other methods) to the "using" statement, something like the following: 如您所见,现在我正在对所有操作使用单个全局EFDbContext,从我读到的内容很不好-因此我尝试将其(在Create,Get和其他方法内部)更改为“ using”语句,例如下列:

public City Get(int cityID)
{
using(EFDbContext context)
{
...some opeartions…
return entities;
}
}

And now I get a lot of problems related to Entities lazy loading, I have to use something like the following: 现在,我遇到了许多与实体延迟加载有关的问题,我必须使用如下所示的内容:

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();

just to make simple Get to work the way I want to, otherwise every time I try to access Address for example, I get: “The ObjectContext instance has been disposed and can no longer be used for operations that require a connection”. 只是为了简化操作,按照我想要的方式工作,否则,例如,每当我尝试访问Address时,我都会得到:“ ObjectContext实例已被处置,不能再用于需要连接的操作”。 Of course it works OK, when context is global. 当上下文是全局的时,当然可以。

I need some advise: should I use the local context and use eagerly loading instead of lazy loading? 我需要一些建议:我应该使用本地上下文并使用紧急加载而不是延迟加载吗? Or is the global context acceptable here? 还是在这里可以接受全球背景?

Also how the heck is one suppose to use lazy loading anyway? 另外,无论如何应该使用延迟加载呢? As I see it - I would have to write separate logic for every operation that use some repository - or am I wrong? 如我所见-我将不得不为使用某个存储库的每个操作编写单独的逻辑-还是我错了?

Edit 1: 编辑1:

@Askolein: @Askolein:

OK, currently my application consists of few sub projects: 好的,目前我的应用程序由几个子项目组成:

Common
Domain - here I have my repositories
Helpers
Utils
WebUI

The repository I use to trigger the error looks like this: 我用来触发错误的存储库如下所示:

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);
}

And the implementation of the GetFiltered method, like this: 以及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);
}
}
}
}

I'm calling this method in my controller like this: 我在控制器中这样调用此方法:

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);
}

I understand why I'm get the error, and as I said before - if I explicitly tell entity, to load: Address, Agency and Address.City - it will work just fine. 我知道为什么会收到错误,而且正如我之前所说的-如果我明确告诉实体要加载:Address,Agency和Address.City-它会正常工作。

@WiktorZychla: @WiktorZychla:

My current DataContext looks like this: 我当前的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; }
}

If I understand You correctly - I would have to encapsulate my EFCityRepository and use something like: 如果我正确理解您-我将必须封装我的EFCityRepository并使用类似以下内容的东西:

using(SomeContext context = new SomeContext())
{
    EFCityRepository repository = new EFCityRepository(context);
    var result = repository.Get(id);
    ...do some work...
}

Isn't that a little overkill? 那不是一点点矫kill过正吗? Right now I use Ninject, and inject my Controllers with repository interfaces (IUserRepository, IUserRepository etc.) - so my controller methods are working like Units of Work, If I understand correctly - I would have to either: inject my DbContext inside my controller methods, or create another layer between controler methods, and repository... do I understand this correctly? 现在,我使用Ninject,并使用存储库接口(IUserRepository,IUserRepository等)注入控制器-因此,我的控制器方法的工作方式类似于工作单元,如果我理解正确的话,我将不得不:在控制器方法中注入我的DbContext ,或在控制方法和存储库之间创建另一层...我是否正确理解这一点?

@cosset: @cosset:

As I stated above - I consider my controller methods as Units of Work... if I was to implement something You suggested - where should I put it? 如上所述,我将我的控制器方法视为工作单元...如果要实施您建议的内容,应该放在哪里? Inside Domain or WebUI? 在网域或WebUI中? It would have to be another layer between repository and controller, correct? 这将是存储库和控制器之间的另一层,对吗?

Thank you all for the suggestions. 谢谢大家的建议。

Best regards 最好的祝福

Instead of making the context local to repository methods, why don't you go the other way around - make the repository independent on the context: 除了将上下文局部化到存储库方法之外,您为什么不采取其他方法-使存储库独立于上下文:

public class EFCityRepository : ICityRepository
{ 
    public EFCityRepository( EFDbContext context )
    {
        this.context = context;
    }        

    private EFDbContext context;

    public bool Create(City cityToCreate)
    {
        ...
    }

    public City Get(int cityID)
    {
        ...
    }
}

This approach gives you the best flexibility. 这种方法为您提供了最大的灵活性。 You can share the very same context between repositories, you can have an extra context per repository, whatever. 您可以在存储库之间共享相同的上下文,也可以在每个存储库中拥有一个额外的上下文,无论如何。

For web-based applications, the common practice is to have your context shared "per request" which means that the very same context is used thoughout a single request and it is disposed at the end of the request pipeline. 对于基于Web的应用程序,通常的做法是让您的上下文按“每个请求”共享,这意味着即使在单个请求中也使用相同的上下文,并将其放置在请求管道的末尾。

Edit: as suggested by Maess, you should definitely look at the possibility to semi-automatically manage the lifetime of your context by a Dependency Injection engine (like Unity or Ninject). 编辑:根据Maess的建议,您绝对应该研究通过依赖注入引擎(例如Unity或Ninject)半自动管理上下文生存期的可能性。 The DI engine could also help you significantly by automatically resolving constructor dependencies. DI引擎还可以通过自动解决构造函数的依赖关系来为您提供极大的帮助。 This is another story but could be a solid step forward for your architecture. 这是另一个故事,但对于您的体系结构而言可能是坚实的一步。

I recommend use use pattern UnitOfWork.Simple implementation bellow. 我建议使用以下使用模式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;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM