Я использую EF Core v. 5.0 и базу данных SQLite и пытаюсь динамически добавить DbSet в свой DbContext. Я следовал этому руководству и повторно адаптировал его для EF Core: https://romiller.com/2012/03/26/dynamically-building-a-model-with-code-first/ и я реализовал этот класс DbContext:
internal class GenericAppContext : DbContext
{
public GenericAppContext()
{
//Disable the EF cache system to execute every running the OnModelCreating method.
//ATTENTION: This is a performance loss action!
this.ChangeTracker.LazyLoadingEnabled = false;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
//if "bin" is present, remove all the exceeding path starting from "bin" word
if (baseDir.Contains("bin"))
{
int index = baseDir.IndexOf("bin");
baseDir = baseDir.Substring(0, index);
}
options.UseSqlite($"Data Source = {baseDir}Database\\TestSQLite.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
MethodInfo addMethod = typeof(ModelBuilder).GetMethods().First(e => e.Name == "Entity");
foreach (var assembly in AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.GetName().Name != "EntityFramework"))
{
IEnumerable<Type> configTypes = assembly
.GetTypes()
.Where(t => t.BaseType != null
&& t.BaseType.IsGenericType
&& t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in configTypes)
{
Type entityType = type.BaseType.GetGenericArguments().Single();
object entityConfig = assembly.CreateInstance(type.FullName);
addMethod?.MakeGenericMethod(entityType)
.Invoke(modelBuilder, new object[] { });
}
}
}
}
Мои классы "Блог" и "Статья":
internal class Blog : EntityTypeConfiguration<Blog>
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
}
internal class Article : EntityTypeConfiguration<Article>
{
[Key]
public int Id { get; set; }
public string Body { get; set; }
}
И вот как я инициализировал DbContext, в переменной "tables" я вижу, что таблица "Article" добавляется динамически с использованием context.Model.GetRelationalModel().Tables.ToList()
public static string TestMethod()
{
using (var context = new GenericAppContext())
{
string result = string.Empty;
//Ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created.
context.Database.EnsureCreated();
List<ITable> tables = context.Model.GetRelationalModel().Tables.ToList();
}
}
На данный момент я успешно увидел динамически добавленный класс «Статья», но я, конечно, не могу запросить «Статью» с помощью Linq, потому что он физически не существует в DbContext.
Есть ли способ использовать Linq против динамической добавленной таблицы DbSet, такой как «Статья»?
@Yinqiu спасибо за ответ. Проблема в том, что context.Set<Entity>() требует, чтобы класс "Article" передавался напрямую, вы не можете передать его динамически, как хотелось бы, например: object instantiatedObject = Activator.CreateInstance(objectType); context.Set<instantiatedObject>();
В конце концов я решил использовать «Linq.Dynamic.Core», который позволяет вам использовать Linq и писать лямбда-выражение в виде строки, и многое другое, здесь больше информации о Dynamic Linq.
Вот как я изменил TestMethod(), показанный ранее, и как я выполнил запрос к таблице «Статья», добавленной динамически через мой класс «GenericContextApp».
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
public static string TestMethod()
{
using (var context = new GenericAppContext())
{
string result = string.Empty;
//Ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created.
context.Database.EnsureCreated();
IEnumerable<IEntityType> contextEntitiesList = context.Model.GetEntityTypes();
IQueryable<IEntityType> entitiesList = contextEntitiesList.Select(p => p).ToList().AsQueryable();
//Query against the dinamycally added "Article" table. It will return the SQL query as a string.
result = context.Query("Your.Complete.Namespace.Article").Where("Body == \"ciao\"").ToQueryString();
return result;
}
}
Если
DbSet
не существует, вы можете использоватьcontext.Set<Entity>()
для запроса