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

 奇力0_843 发布于 2023-01-30 19:05

最终答案:

我在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
{
KeyValuePair Create(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 DbSet Users { 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?它必须是存储库和控制器之间的另一层,对吗?

谢谢大家的建议.

最好的祝福

1 个回答
  • 而不是将上下文设置为存储库方法的本地化,为什么不反过来 - 使存储库独立于上下文:

    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引擎还可以通过自动解析构造函数依赖性来显着帮助您.这是另一个故事,但可能是您的架构向前迈出的坚实一步.

    2023-01-30 19:10 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有