关联实体对象的懒惰加载在模型之外构建在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;
}
}`