2014-12-02 70 views
3

让我们假设我有一个名为的员工和相应的EF 6.0 db-first模型的db表。在运行时更改表名

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

是否有可能在运行时和需求,而使用相同的对象名称和查询到数据库表名重定向到的Test1

EF 6.0 Interceptor的使用情况可能是这样吗?

+0

似乎是一个非常奇怪的要求;你能否通过更改表名来解释更多关于你想要完成的事情? – Claies 2014-12-02 22:12:37

+0

我的req背后的故事如下:db表存储了数百万条记录。为了保证可接受的日常表现,我想在主表中保留“最近”的记录(大约1-2百万),并将所有“旧”记录(总共> 1亿)记录到次要相同表格中。用户可以通过应用程序级别的开关使用“历史数据”模式并使用完全相同的模型,对象和查询切换到辅助表格 – GrMikeD 2014-12-02 22:20:58

+0

我不确定这是否可以使用DB First模型,但我知道有可能使用Code First。你可以在我对类似问题的回答(使用Code First)中阅读更多我的ramblings [这里](http://stackoverflow.com/questions/27032292/entity-framework-code-first-returning-same-data-for - 不同-表映射到S/27050034#27050034)。 – 2014-12-02 22:28:47

回答

-1

为什么不使用一些很好的老式多态?

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()); 
0

我不知道你是否应该做到这一点,但我认为你可以。您将不得不深入实体框架元数据结构,例如MetadataWorkspace,您可以从底层ObjectContext获取该结构。在这里看到一个例子:http://weblogs.asp.net/ricardoperes/entity-framework-metadata

+0

对于它的价值,你可以按照你所描述的获取元数据,但是你不能通过这条路线改变表格映射(我已经尝试过)。尽管该属性是一个读写属性,但有一种内部机制将其标识为只读,并在尝试更改时引发异常。 – 2014-12-03 13:53:55

+0

是的,我以为是...谢谢! – 2014-12-03 14:32:26

0

感谢您的答案。

我认为我的情况是一个真实世界的情景,在EF教程和示例的所有“入门”典型场景中通常都会被忽略。

基于我使用db-first方法并且应该在应用程序级别的事实,我认为我将创建一个Context实例,它基于不同的SSDL以及用户将使用的新表名要求

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()只运行一次。因此,如果您在同一个应用程序中使用它,它将缓存结果后将第一个表名称。

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); 
    } 
} 

当然,你必须从某个地方得到新的表名。无论是从构造函数还是从您可以从interceptionContext.DbContexts中获取的自定义DBContext中的存储字段。

然后你只需要为你的上下文注册拦截器。

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; 
    } 
} 
0

老问题,但基于这个问题我建议你看一下基于“当前”位或时间字段partioning你的表。分区基于列值&由大多数现代DBMS支持。它会避免ORM级别的问题。