C# 在上下文之外构建模型时.Net核心懒散加载问题

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

当在OnModelCreating和关联对象之外构建模型时,关联实体对象的延迟加载不起作用,尽管所有方法都是虚拟的

例如

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