Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change entity map to another "unknown" table at runtime

I have to write a C# application that works with a SQL server database created and mantained by an old application. The application creates new tables each year and the "year property" is in the table name. The number of tables it creates may vary depending of the number of "sections" that the user has created inside the application. So, I have to work with tables like Cwx_DRyz (quite self explanatory...), where "wx" can be the section, and "yz" would be the year. An example of group of table could be:

C01_DR07

C01_DR08

C01_DR09

C02_DR08

C02_DR09

C03_DR06

C04_DR12

And all of those tables could represent, for example, clients. They would be clients from different sections and years, but clients with the same structure.

My question is: Can I have a Client entity to handle all those tables and change the mapping from one to another at runtime? The title says "unknown" because I don't know the tables before runtime.

The most similar question I have found is Entity Framework map multiple tables to one entity and the answer is to use the "Table Per Concrete Type Inheritance", but it is not useful for my case.

PS: EF version 4.3.1 and VS2010

EDIT: The tables don't have primary keys... Most of them have columns that are supossed to have unique values (integer or string).

like image 224
CarlosJ Avatar asked Oct 08 '22 11:10

CarlosJ


1 Answers

If you use "code first" you could create a mapping as you want. This also works with existing databases when the mapping you have created match the database.

So whenever you create a context you can build the string (tablename) you want to map to.

Some codesamples for "code first" and how you could start:

The DbContext:

public DbSet<YourEntity> YourEntities { get; set; }
...

// this is called when the db gets created and does the configuration for you => maybe not needed in your case
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    ConfigurationRegistrar configurationRegistrar = modelBuilder.Configurations;

    new GeneralEntitiesConfiguration(configurationRegistrar);
}

GeneralEntitiesConfiguration is a class im using to handle the configurations, nothing more than a helper which looks like:

public class GeneralEntitiesConfiguration
{
    public GeneralEntitiesConfiguration(ConfigurationRegistrar configurationRegistrar)
    {
        configurationRegistrar.Add(new YourEntityConfiguration());
        //and additional configurations for each entity, just to splitt it a bit and have it more read and maintenance able
    }
}

YourEntityConfiguration is a class where i have all the configurations for this entity:

public class YourEntityConfiguration : EntityTypeConfiguration<YourEntity>
{
    public YourEntityConfiguration ()
    {
        ToTable("WhatEverYouLike"); // here you can do any magic to map this entity to a table, just make sure that your properties are mapped to the correct colums
        Property(entity => entity.Id).HasColumnName("YouColumnName");

        //and here you also have to do the other configurations
    }
}

At the application startup (or before you initialize your context the first time) you have to initialize the database. Therefore you can use an initializer which checks the database and handles differences. Build in there are things like "DropCreateDatabaseAlways" or "DropCreateDatabaseIfModelChanges" => you would need to create your own which just ignores any differences. In my sample i have create one which just throws an exception when the model differs (i wanted to handle model changes with scipts for the first try):

//before using the context the first time i'm calling, you can ignore the connection string
DbContextInitializer.Init(conString);

public static class DbContextInitializer
{
    public static void Init (string connectionString)
    {
        Database.SetInitializer(new CreateDbThrowExceptionIfModelDiffersInitializer<SMDbContext>());

        using(var dbContenxt = new MyDbContext(connectionString))
        {
            try
            {
                dbContenxt.Database.Initialize(true);
            }
            catch(DatabaseModelDiffersException diffException)
            {
                // some magic...
            }
            catch(Exception ex)
            {
                // TODO: log
                throw;
            }
        }
    }

    public class CreateDbThrowExceptionIfModelDiffersInitializer<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext
    {
        public void InitializeDatabase(TContext context)
        {
            using (new TransactionScope(TransactionScopeOption.Suppress))
            {
                if (!context.Database.Exists())
                    context.Database.Create();
            }

            if (!context.Database.CompatibleWithModel(true))
            {
                throw new DatabaseModelDiffersException("Database Model differs!");
            }
        }

        protected virtual void Seed(TContext context)
        {
            // create data if you like
        }
    }

    // just an exception i'm using for later useage
    public class DatabaseModelDiffersException : Exception
    {
        public DatabaseModelDiffersException(string msg) : base(msg)
        {}
    }
}

Hope you have got an idea of you can handle dynamic table names with entity framework! If there are more questions just ask ;)

like image 107
Dominik Kirschenhofer Avatar answered Oct 13 '22 10:10

Dominik Kirschenhofer