尽管在使所有方法保持虚拟的情况下在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”方法在Context外部构建了数据实体,但是这种方式的惰性加载支持消失了,并且不为从中获取的实体创建代理对象数据库。
答案 0 :(得分:2)
问题在于,延迟加载代理程序包使用约定,该约定在构建模型并对其进行修改后执行。尽管外部模型是在没有该约定的情况下构建的,所以根本无法激活该功能。
以下解决方法适用于撰写本文时最新的官方EF Core版本2.2.4。如果您升级到新的EF Core版本(3.0+),则很可能需要对其进行相应的更新;如果他们对其进行了修复,则需要将其删除。
您使用的SqlServerConventionSetBuilder.Build()
方法中的EF Core 2.2.4 code看起来像这样:
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 Core基础架构“在使用OnModelCreating
时自动执行” 。
答案 1 :(得分:0)
在这种情况下,使用EFCore 2.2.4进行延迟加载的一种解决方法是将ILazyLoader服务注入到实体中。此方法不需要继承实体类型,也不要求导航属性是虚拟的,并且允许使用new
创建的实体实例附加到上下文后就可以延迟加载。但是,它需要引用ILazyLoader服务,该服务在Microsoft.EntityFrameworkCore.Abstractions
包中定义。延迟加载具有多对多关系的数据模型的示例代码如下:
`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;
}
}`