C# 在上下文之外构建模型时.Net核心懒散加载问题
当在OnModelCreating和关联对象之外构建模型时,关联实体对象的延迟加载不起作用,尽管所有方法都是虚拟的 例如C# 在上下文之外构建模型时.Net核心懒散加载问题,c#,entity-framework-core,lazy-loading,ef-core-2.2,C#,Entity Framework Core,Lazy Loading,Ef Core 2.2,当在OnModelCreating和关联对象之外构建模型时,关联实体对象的延迟加载不起作用,尽管所有方法都是虚拟的 例如 protectedoverride void onconfiguration(DbContextOptionsBuilder optionsBuilder) { 如果(!optionBuilder.IsConfigured) { 选项生成器 .UseLazyLoadingProxies() .UseModel(新ModelBuilderService().GetOrCrea
protectedoverride void onconfiguration(DbContextOptionsBuilder optionsBuilder)
{
如果(!optionBuilder.IsConfigured)
{
选项生成器
.UseLazyLoadingProxies()
.UseModel(新ModelBuilderService().GetOrCreateCompiledModel())
.UseSqlServer(@“connectionstring”,
sqlOption=>sqlOption.UseNetTopologySuite());
}
}
公共类ModelBuilderService
{
私有静态IModel GetOrCreateCompiledModel(IEnumerable模型支持AssemblyPatterns)
{
var conventions=SqlServerConventionSetBuilder.Build();
var modelBuilder=新的modelBuilder(约定);
var modelBuilderType=typeof(ModelBuilder);
var entityMethod=modelBuilderType.GetMethod(“实体”,modelBuilderType.GetGenericArguments());
var pathToUse=AppDomain.CurrentDomain.BaseDirectory;
如果(!AppDomain.CurrentDomain.BaseDirectory.Contains(“bin”))
{
pathToUse=Path.Combine(AppDomain.CurrentDomain.BaseDirectory,“bin”);
}
var entitiesAdded=new HashSet();
if(entityMethod==null)
{
抛出新的NullReferenceException(“在DbModelBuilder上找不到实体方法”);
}
foreach(modelSupplyingAssemblyPatterns中的var assemblyPattern)
{
var dataProviderModels=Directory.EnumerateFiles(pathToUse、assemblyptern、SearchOption.AllDirectories);
foreach(dataProviderModels中的var dll)
{
var assembly=assembly.LoadFrom(dll);
modelBuilder.ApplyConfiguration组件(组件);
var typesToRegister=assembly.GetTypesInheritingFrom();
foreach(TypeStoreRegister中的var实体)
{
if(entitiesAdded.Add(entity.FullName))
{
entityMethod.MakeGenericMethod(实体)
.Invoke(modelBuilder,新对象[]{});
}
}
}
}
返回modelBuilder.Model;
}
}
试图找到解决此问题的方法,因为我有一个通用的解决方案,并且数据实体是使用“UseModel”在上下文之外构建的方法,但lazyloading支持以这种方式消失,并且不会为从数据库获取的实体创建代理对象。问题在于,延迟加载代理包使用的是在构建并修改模型后执行的约定。而外部模型是在没有约定的情况下构建的,因此功能根本没有被激活 以下解决方法适用于撰写本文时最新的官方EF Core版本2.2.4。如果您升级到较新的EF Core版本(3.0+),则很可能需要相应地进行更新;如果他们修复了它,则很可能需要将其删除 您正在使用的
SqlServerConventionSetBuilder.Build()
方法如下所示:
public static ConventionSet Build()
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkSqlServer()
.AddDbContext<DbContext>(o => o.UseSqlServer("Server=."))
.BuildServiceProvider();
using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetService<DbContext>())
{
return ConventionSet.CreateConventionSet(context);
}
}
}
并使用它来代替SqlServerConventionSetBuilder.Build()调用,例如
var conventions = BuildSqlServerConventionSet();
// ... the rest
更新:还要注意,ModelBuilder.Model
属性在构建过程中返回挂起的可变模型。为了使最终模型“准备好供运行时使用”,请替换
与
此方法由EF Core infrastructure“在使用
OnModelCreating
时自动执行”在这种情况下,EFCore 2.2.4延迟加载的一个解决方法是将ILazyLoader服务注入实体。此方法不要求从中继承实体类型,也不要求导航属性是虚拟的,并且允许使用new
创建的实体实例在连接到上下文后延迟加载。但是,它需要对ILazyLoader服务的引用,该服务在Microsoft.EntityFrameworkCore.Abstractions
包中定义。lazyloading具有多个关系的数据模型的示例代码如下:
`public partial class PersonOrganisation
{
private Person person;
private Organisation organisation;
private ILazyLoader LazyLoader { get; set; }
private PersonOrganisation(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
}
public PersonOrganisation()
{
}
public Guid? PersonId { get; set; }
public Guid? OrganisationId { get; set; }
public virtual Organisation Organisation {
get => LazyLoader.Load(this, ref organisation);
set => organisation = value;
}
public virtual Person Person {
get => LazyLoader.Load(this, ref person);
set => person = value;
}
}`
您可以分享示例模型构建代码吗?我这样问是因为延迟加载代理正在修改模型,这在您在外部构建模型时不会发生。@IvanStoev您是对的!Lazyloading代理仅在使用OnModelCreating()方法构建模型时添加约定集。此外,EFCore团队也证实了这一点。因此,我们正在努力为这个主要的拦截器找到一个解决办法。这就是为什么我需要查看模型构建代码,以便找到一些解决方法(通过某种方式插入延迟加载约定或手动应用它在构建模型上的作用)。实际上,我在您发布的GitHub问题中看到了它,但在我(或其他人)的情况下,在这里很好合成一个答案。@IvanStoev已使用建模代码进行了更新。。感谢您提供解决方法@IvanStoev。。我尝试使用此约定集初始化modelbuilder,但它不起作用。正如您所指出的,Lazyloadingproxies包仅在构建模型之后才添加proxyconvention。但是,由于在上下文之外构建模型,我们不得不在创建模型之前初始化约定集,这似乎是这个问题的根本原因。嗯,我没有说-约定是在开始时创建的,只是在不同的模型操作上运行不同的约定类型。延迟加载代理约定只是在构建的模型上运行的类型之一。您可以在
conventionSet.ModelBuiltConventions
中看到10多个类似的约定。不管怎么说,如果它不能像你说的那样工作,那就太糟糕了,尽管我看不出它不能工作的原因。我想我找到了它不能为你工作的原因-请参阅更新。干杯,你太棒了。。“FinalizeModel()”方法发挥了神奇的作用,使其正常工作。谢谢!
var conventions = BuildSqlServerConventionSet();
// ... the rest
return modelBuilder.Model;
return modelBuilder.FinalizeModel();
`public partial class PersonOrganisation
{
private Person person;
private Organisation organisation;
private ILazyLoader LazyLoader { get; set; }
private PersonOrganisation(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
}
public PersonOrganisation()
{
}
public Guid? PersonId { get; set; }
public Guid? OrganisationId { get; set; }
public virtual Organisation Organisation {
get => LazyLoader.Load(this, ref organisation);
set => organisation = value;
}
public virtual Person Person {
get => LazyLoader.Load(this, ref person);
set => person = value;
}
}`