.NET Core Lazyloading问题在上下文之外构建模型时



关联实体对象的懒惰加载在模型之外构建在OnModelCreating和关联对象之外时不起作用,尽管将所有方法都保留了。

例如,

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    { 
        optionsBuilder
            .UseLazyLoadingProxies()
            .UseModel(new ModelBuilderService().GetOrCreateCompiledModel())
            .UseSqlServer(@"connectionstring",
                sqlOption => sqlOption.UseNetTopologySuite());
    }
}
public class ModelBuilderService 
{
    private static IModel GetOrCreateCompiledModel(IEnumerable<string> modelSupplyingAssemblyPatterns)
    {
        var conventions = SqlServerConventionSetBuilder.Build();
        var modelBuilder = new ModelBuilder(conventions);
        var modelBuilderType = typeof(ModelBuilder);
        var entityMethod = modelBuilderType.GetMethod("Entity", modelBuilderType.GetGenericArguments());
        var pathToUse = AppDomain.CurrentDomain.BaseDirectory;
        if (!AppDomain.CurrentDomain.BaseDirectory.Contains("bin"))
        {
            pathToUse = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");
        }
        var entitiesAdded = new HashSet<string>();
        if (entityMethod == null)
        {
            throw new NullReferenceException("Cannot find Entity method on DbModelBuilder");
        }
        foreach (var assemblyPattern in modelSupplyingAssemblyPatterns)
        {
            var dataProviderModels = Directory.EnumerateFiles(pathToUse, assemblyPattern, SearchOption.AllDirectories);
            foreach (var dll in dataProviderModels)
            {
                var assembly = Assembly.LoadFrom(dll);
                modelBuilder.ApplyConfigurationsFromAssembly(assembly);
                var typesToRegister = assembly.GetTypesInheritingFrom<BaseObject>();
                foreach (var entity in typesToRegister)
                { 
                    if (entitiesAdded.Add(entity.FullName))
                    {
                        entityMethod.MakeGenericMethod(entity)
                                    .Invoke(modelBuilder, new object[] { });
                    }
                }
            }
        } 
        return modelBuilder.Model;
    } 
}

试图找到解决此问题的解决方法,因为我有一个通用解决方案,并且数据实体是使用" usemodel"方法在上下文之外构建的,但是以这种方式消失了lazyloading支持,并且未为从中获取的实体创建代理对象数据库。

问题是懒惰加载代理程序包使用构建模型并修改后执行的约定。虽然构建外部模型没有该约定,但根本没有激活该功能。

以下解决方法是在撰写本文时针对最新的EF核心版本2.2.4。如果您升级到较新的EF Core版本(3.0 (,则很可能需要相应地进行更新,或者将其修复。

EF Core 2.2.4 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);
        }
    }
}

您可以看到,它使用了一些骗局,其中最重要的是自己的DbContextOptionsBuilder。所以我们只需要将UseLazyLoadingProxies()调用添加到该构建器。

为了做到这一点,请使用修改的代码创建一个私有静态方法:

static ConventionSet BuildSqlServerConventionSet()
{
    var serviceProvider = new ServiceCollection()
        .AddEntityFrameworkSqlServer()
        .AddDbContext<DbContext>(o => o.UseSqlServer("Server=.").UseLazyLoadingProxies()) // <--
        .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属性在建筑物期间返回可突变的模型。为了获得最终的模型"准备好由运行时使用" ,请替换

return modelBuilder.Model;

return modelBuilder.FinalizeModel();

此方法由EF核心基础结构执行"使用OnModelCreating"

自动执行。

在这种情况下,用efcore 2.2.4延迟加载的一个解决方法是将iLazyloader服务注入实体。此方法不需要从或导航属性继承或导航属性是虚拟的,并且允许使用new创建的实体实例将一旦附加到上下文上的lazy-load。但是,它需要对Microsoft.EntityFrameworkCore.Abstractions软件包中定义的Ilazyloader服务的引用。用于延误具有多种关系的datamodel的示例代码如下:

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

相关内容

  • 没有找到相关文章

最新更新