Change table name at runtime

Suppose I have a db table named Employee and a corresponding Eb 6.0 db-first model.

Retrieving all rows of the Employee table is performed through a query: context.Employees.ToList()

Is it possible at runtime and on demand to redirect the db table name to Test1 using the same name and object query?

Maybe an EF 6.0 interceptor use case?

+3


source to share


7 replies


I know it's been past since posting, but I'll add my answer to help someone else. I had shared SQL queue tables with different table names. That is, the schema is the same for both tables. I created a framework so that you can dynamically query the table of your choice by providing a name and hence I needed to update the table name at runtime. Basically, you can create an interceptor to intercept raw SQL queries from an entity and update the table name from there.

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

      

Of course you need to get the new table name from somewhere. Either from the constructor or from a stored field in your custom DBC text, which you can grab from interceptionContext.DbContexts.

Then you just need to register an interceptor for your context.



public class MyContext : DBContext
{
    public readonly string NewTableName;

    public MyContext(string connectionString, string newTableName)
        : base(connectionString)
    {
        NewTableName = newTableName;
        // Set interceptor
        DbInterception.Add(new QueueMessageInterceptor());
    }
}

      

UPDATE: I found that if you add an interceptor to the constructor above, it will cause a memory leak. DotMemory doesn't talk about it. Make sure you add an interceptor to your static constructor.

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

      

+3


source


I don't know if you should do this, but I think you can . You will have to look in Entity Framework metadata structures like MetadataWorkspace, which you can get from the underlying ObjectContext. See an example here: http://weblogs.asp.net/ricardoperes/entity-framework-metadata .



0


source


Thanks for answers.

I think my case is a real-world scenario, which is generally ignored in all typical EF scenarios and "bootstrap" examples.

Based on the fact that I am using a db-first approach and the switch needs to be at the application layer, I think I will create a Context instance based on another SSDL with a new table name that the user will use on demand

0


source


I would do something like this:

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

}

      

I think it works in your scenario. The only problem is the OnModelCreating () is triggered once. So if you use it in the same application, it will take the first table name as it caches the result.

0


source


Old question, but based on the problem, I suggest you take a look at splitting your table based on the "current" bit or datetime field. Partitioning is based on the value of the column and is supported by most modern DBMSs. This will avoid problems at the ORM level.

0


source


You have to create a new dbcontext that inherits from the db-first model context and treat it as code-first in ef. Check the link please. The same problem you have.

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

This way, when displaying, you can get the table name dynamically.

0


source


Why not use some good old fashioned polymorphism?

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

      

-2


source







All Articles