Baris
Baris

Reputation: 455

Entity Framework Intercept Generate Migration Script

I use Entity Framework 6.2 Code First (.net framework 4.6.1) and I map few entities to view via Table Attribute. It works for select operations and I handle Insert/Update/Delete with writing trigger to view at sql server side. It works as expected, however when I add a new migration, Entity Framework generate RenameTable scripts for used Table Attribute (actuallyis expected behavior for EF). But I want to intercept migration generation and change these entities tableName to original name.

my code like;

[MapToView("Users","UsersView")]
public class User
{
...
}

I wrote MapToView Attribute, this attribute inherited by TableAttribute and pass to second parameter to TableAttribute. I create this Attribute because if I intercept migration generation, return original table name with this attribute parameters.

In this case when I run "add-migration migrationName" it creates migration scripts like this;

RenameTable(name: "dbo.Users", newName: "UsersView");

but i want to create empty migration when I run "add-migration migrationName" script.

anyone can help me?

Upvotes: 0

Views: 485

Answers (1)

Baris
Baris

Reputation: 455

I solve the problem. First: Problem is; When I Map Entity to View EF Code-first generate migration with ViewName. This is problem because I want to use View Instead of Table. So I solve problem with this instructions;

1- I Create BaseEntityConfiguration that Inherited from EntityTypeConfiguration and all entity configuration classes are inherited by. for example:

public class UserConfig: BaseEntityConfiguration<User> //Generic Type is Entity
    {
        public UserConfig()
        {
        }
    }

2- I Create MapToViewAttribute that inherited by TableAttribute

public class MapToViewAttribute : TableAttribute
    {
        public string TableName { get; }
        public string ViewName { get; }

        public MapToViewAttribute(string tableName, string viewName) : base(viewName)
        {
            TableName = tableName;
            ViewName = viewName;
        }
    }

3- I Use MapToViewAttribute for example User Entity to use View.

 [MapToView("User","UserView")]
    public class User
    {
      ...
    }

And in BaseEntityConfiguration's Constructor I Get Generic Type and custom attributes. If any entity has MapToView Attribute, I pass to TableName parameter to ToTable Method. So at runtime EF uses View for these entities but doesn't create migration with RenameTable for these entities.

protected BaseEntityConfiguration()
        {
            var baseType = typeof(TEntityType);
            var attributes = baseType.GetCustomAttributes(true);
            foreach (var attr in attributes)
            {
                if (attr.GetType() == typeof(MapToViewAttribute))
                {
                    var tableName = ((MapToViewAttribute)attr).TableName;
                    ToTable(tableName);
                }
            }
        }

Last EF don't use your configuration files, so you must tell the EF to use this in DbContext class's InternalModelCreate method. My implementation like this;

protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            var typesToRegister = Assembly.GetExecutingAssembly()
                .GetTypes().Where(IsConfigurationType);

            foreach (var type in typesToRegister)
            {
                dynamic configurationInstance = type.BaseType != null
                                                && type.BaseType.IsGenericType
                                                && type.BaseType.GetGenericTypeDefinition() == typeof(BaseEntityConfiguration<>)
                    ? Activator.CreateInstance(type, culture)
                    : Activator.CreateInstance(type);

                modelBuilder.Configurations.Add(configurationInstance);
            }

            modelBuilder.Types().Configure(t => t.ToTable(t.ClrType.Name));
            BaseDbContext.InternalModelCreate(modelBuilder);
        }

But if you use this approach you must create Insert, Update and Delete Triggers/Rule (if you use SQLServer trigger is an option but if you use postgresql rule is better option) because EF uses this view for insert, update and delete operations.

Upvotes: 2

Related Questions