簡體   English   中英

實體框架-全局vs本地上下文vs延遲加載

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

最終答案:

我在InRequestScope()方法中使用了@WiktorZychla答案和Ninject的組合。 我重構了我的存儲庫以接受上下文的注入,然后在我的NinjectControllerFactory內部添加了以下行:

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

(注意:我替換了:

ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>().InReque‌​stScope().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();

只是為了簡化操作,按照我想要的方式工作,否則,例如,每當我嘗試訪問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);
}

我知道為什么會收到錯誤,而且正如我之前所說的-如果我明確告訴實體要加載:Address,Agency和Address.City-它會正常工作。

@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...
}

那不是一點點矯kill過正嗎? 現在,我使用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引擎還可以通過自動解決構造函數的依賴關系來為您提供極大的幫助。 這是另一個故事,但對於您的體系結構而言可能是堅實的一步。

我建議使用以下使用模式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