假设我有一个名为 Employee 的db表和一个相应的EF 6.0 db-first模型。
获取表 Employee 的所有行是通过查询完成的:context.Employees.ToList()
在运行时和按需时,是否可以在使用相同的对象名称和查询时将db表名称重定向到 Test1 ?
可能是EF 6.0拦截器使用的情况?
答案 0 :(得分:3)
我知道自原帖以来已经有一段时间了,但我会添加我的答案以帮助其他人。我有一个具有不同表名的通用SQL队列表。即两个表的模式完全相同。我创建了一个框架,以便您可以通过提供名称来动态轮询您选择的表,以及我在运行时需要更新表名的原因。基本上,您可以创建一个拦截器来拦截实体框架中的原始SQL查询,并从那里更新表名。
public class MyInterceptor : IDbCommandInterceptor
{
private const string TableReplaceString = "[TheTableNameToReplace]";
private void ReplaceTableName(DbCommand command, IEnumerable<DbContext> contexts)
{
var myContext = contexts?.FirstOrDefault(x => x is MyContext) as MyContext;
if (myContext != null && command != null && command.CommandText.Contains(TableReplaceString))
{
command.CommandText = command.CommandText.Replace(TableReplaceString, $"[{myContext.NewTableName}]");
}
}
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
ReplaceTableName(command, interceptionContext.DbContexts);
}
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
ReplaceTableName(command, interceptionContext.DbContexts);
}
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
ReplaceTableName(command, interceptionContext.DbContexts);
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
ReplaceTableName(command, interceptionContext.DbContexts);
}
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
ReplaceTableName(command, interceptionContext.DbContexts);
}
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
ReplaceTableName(command, interceptionContext.DbContexts);
}
}
当然,你必须从某个地方获取新的表名。从构造函数或自定义DBContext中的存储字段,您可以从interceptionContext.DbContexts中获取。
然后你只需要为你的上下文注册拦截器。
public class MyContext : DBContext
{
public readonly string NewTableName;
public MyContext(string connectionString, string newTableName)
: base(connectionString)
{
NewTableName = newTableName;
// Set interceptor
DbInterception.Add(new QueueMessageInterceptor());
}
}
更新: 我发现如果你在上面的构造函数中添加拦截器会导致内存泄漏。 DotMemory并没有告诉你这件事。确保在静态构造函数中添加拦截器。
public class MyContext : DBContext
{
public readonly string NewTableName;
static MyContext()
{
// Set interceptor only in static constructor
DbInterception.Add(new QueueMessageInterceptor());
}
public MyContext(string connectionString, string newTableName)
: base(connectionString)
{
NewTableName = newTableName;
}
}
答案 1 :(得分:0)
我不知道你应该这样做,但我认为你可以。您将不得不深入了解Entity Framework元数据结构,例如MetadataWorkspace,您可以从底层ObjectContext获取它。请在此处查看示例:http://weblogs.asp.net/ricardoperes/entity-framework-metadata。
答案 2 :(得分:0)
感谢您的回答。
我认为我的情况是一个现实世界的情景,通常在所有情况下被忽视&#34;入门&#34; EF教程和示例的典型场景。
基于我使用db-first方法并且交换机应该处于应用程序级别的事实,我认为我将基于具有用户将按需使用的新表名的不同SSDL创建Context实例
答案 3 :(得分:0)
我会做这样的事情:
public partial class MyContext : DbContext
{
private readonly ITableNameProvider _tableNameProvider;
public MyContext(ITableNameProvider tableNameProvider)
: base("name=ConnectionStringName")
{
_tableNameProvider = tableNameProvider;
}
public virtual DbSet<MyGenericEntity> Templates { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyGenericEntity>()
.ToTable(_tableNameProvider.GetTableName(), _tableNameProvider.GetSchemaName());
}
}
我认为它适用于您的场景。唯一的问题是OnModelCreating()运行一次。因此,如果您在同一个应用程序中使用它,它将获取第一个表名称,因为它会缓存结果。
答案 4 :(得分:0)
老问题,但基于这个问题,我建议你看一下基于&#34;当前&#34;位或日期时间字段。分区基于列值&amp;得到了大多数现代DBMS的支持。它可以避免ORM级别的问题。
答案 5 :(得分:0)
您必须创建一个新的dbcontext,该继承自db-first模型上下文,并在ef中将其视为代码优先。 请检查链接。和你一样的问题。
因此,在映射时,您可以动态获取表名。
答案 6 :(得分:-2)
为什么不使用一些好的老式多态?
partial class Employee : IEmployee { }
partial class HistoricalEmployee : IEmployee { }
interface IEmployee {
public string Name { get; set; }
}
void PrintEmployeeName(IEmployee employee)
{
Debug.WriteLine(employee.Name);
}
PrintEmployeeName(context.Employees.First());
PrintEmployeeName(context.HistoricalEmployees.First());