在运行时更改表名称

时间:2014-12-02 22:07:24

标签: entity-framework entity-framework-6

假设我有一个名为 Employee 的db表和一个相应的EF 6.0 db-first模型。

获取表 Employee 的所有行是通过查询完成的:context.Employees.ToList()

在运行时和按需时,是否可以在使用相同的对象名称和查询时将db表名称重定向到 Test1

可能是EF 6.0拦截器使用的情况?

7 个答案:

答案 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中将其视为代码优先。 请检查链接。和你一样的问题。

https://www.codeproject.com/Articles/421643/How-to-Use-MVC-Net-on-the-Dynamics-NAV-Database-St#_articleTop

因此,在映射时,您可以动态获取表名。

答案 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());