Help with configuring DbContext with EntityFramework using good practices

7

I have my application layered with Class Library and my layer with Entity Framework , where I set DbContext , it's Repository .

My class inherited from DbContext:

public class Context : DbContext
{
    private static string connectionString = "Data Source=(local); Initial Catalog=CRM; Integrated Security=True;";
    public Context() : base(connectionString)
    {
        // Set the auto versioning
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Migrations.Configuration>()); 
        // Gets or sets the database initialization strategy. 
        Database.SetInitializer<Context>(new Repository.DatabaseAdditionalConfigurations<Context>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Pluralizing
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        // Non delete cascade
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        // Configure Pessoa
        modelBuilder.Configurations.Add<Domain.Pessoa>(new Domain.PessoaTypeConfiguration());

        base.OnModelCreating(modelBuilder);
    }

    #region DbSet´s ...
}

Notice that the first line of the constructor is for auto-database migration using Migrations : Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Migrations.Configuration>()); .

Is this a good time in the class life cycle for this line to be? Or should it be left out in another context?

And I also created the UniqueKeyAttribute class to configure the fields that are unique in my database:

using System;

namespace Domain.Attributes
{
    ///<summary> 
    ///A unique attribute 
    ///</summary> 
    public class UniqueKeyAttribute : Attribute { }
}

And for this attribute to be translated into the database I've added the following class, which unfortunately can not remember the source:

public class DatabaseAdditionalConfigurations<T> : IDatabaseInitializer<T> where T : DbContext
{
    public void InitializeDatabase(T context)
    {
        var created = false;

        if (context.Database.Exists())
            created = true;
        else
            context.Database.CreateIfNotExists();

        if (!created)
            CreateUniqueKeys(context);
    }

    public void CreateUniqueKeys(T context)
    {
        //Fetch all the father class's public properties 
        var masterProperties = typeof(DbContext).GetProperties().Select(x => x.Name).ToList();

        //Percorre cada DBSet<> do DbContext
        foreach (var item in typeof(T).GetProperties().Where(p => masterProperties.IndexOf(p.Name) < 0).Select(x => x))
        {
            //busca o tipo de "T" 
            Type entityType = item.PropertyType.GetGenericArguments()[0];
            // Cria as chaves únicas
            var fields = from f in entityType.GetProperties()
                         where f.GetCustomAttributes(typeof(Domain.Attributes.UniqueKeyAttribute), true).Count() > 0
                         select f.Name;

            var uniqueKeys = "";
            foreach (string s in fields)
            {
                if (string.IsNullOrEmpty(uniqueKeys) || string.IsNullOrWhiteSpace(uniqueKeys))
                    uniqueKeys = s;
                else
                    uniqueKeys += ", " + s;
            }
            if (!string.IsNullOrEmpty(uniqueKeys) && !string.IsNullOrWhiteSpace(uniqueKeys))
                context.Database.ExecuteSqlCommand("alter table " + entityType.Name + " add unique(" + uniqueKeys + ")");
        }
    }
}

Well, first I have a problem. Now that I've added the control with Migrations I do not know when I can set the Database.SetInitializer<Repository.Context>(new Repository.DatabaseAdditionalConfigurations<Repository.Context>()); setting so that the CreateUniqueKeys method can create the unique keys in the database. How could I treat this? Would it be in method Seed of class Configuration of Migrations ?

Another issue is that my base was automatically created only when% of the builder of the Migrations Configuration class was set to AutomaticMigrationsEnabled = true; . Do you need this, or am I doing something wrong? Is going this route a good practice?

Remembering here the ideal place question for: true .

    
asked by anonymous 18.03.2014 / 01:36

1 answer

7

ConnectionString in the Context Statement

Not good practice. By publishing your website on a different basis (such as in Azure, for example), the system would no longer function properly. Ideally, you should configure your context as follows:

public Context() : base("name=SeuSistema")

And in% root%, have this setting:

<configuration>
    <connectionStrings>
        <add name="SeuSistema" connectionString=""Data Source=(local); Initial Catalog=CRM; Integrated Security=True;" providerName="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Remove Pluralization of Tables

This is not exactly good practice. It just prevents the Framework from pluralizing its tables, which may be strange if you write the system in Portuguese (for example, a Model named Web.Config would have a controller named Contato ).

The easy and simple way to resolve the pluralization problem in the database is by using ContatoesController ".

Remove Cascade Delete

This is also not exactly a good practice. Cascade on delete is a dangerous option, but not using this option can create strange inconsistencies. The ideal is not to allow the exclusion of records that have a lot of dependent data.

[Table]

The command only indicates that all pending Migrations must be run at system startup (does not require the use of the SetInitializer command). It is not exactly a good practice because it can run erroneously Migrations , which can generate unpredictable results in case of frequent model changes. This setting is not required.

The Update-Database source already runs when you run the Migrations\Configuration.cs command and when publishing your system using the Web Deploy method. The Update-Database method already performs minimal database insertions and modifications, and you do not need to implement% integer% for startup unless you need a very specific behavior that normal boot does not do, which I think a lot difficult.

Seed()

The MVC is designed to use Surrogate Keys , that is, primary keys that are already unique. The idea of other unique keys only works well if it is in the interest of the programmer to identify two columns as keys, one independent of the other. To do this, simply use decorate all desired attributes with IDatabaseInitializer .

For example, if I have a template named CreateUniqueKeys and I do not want two manufacturers to have the same name. [Key] is not exactly the key. In this case, I use Fabricante .

However, even though the creation of a Nome goes against the idea of the Entity Framework , the database must be agnostic and not all databases support [Index(IsUnique=true)] . Within the MVC approach, still using the example of constraint unique , the ideal is to check if there is no record that has the same name of the record to be inserted / updated.

Automatic Migrations

As mentioned above, the Automatic Migrations mechanism can be useful only if you do not need a hard control of the database state (for example, if the scope has not yet been defined very well , if there are a few programmers working on the project, and if the project is right at the beginning). At the development stage, this can be very useful.

However, it is not recommended to use this setting if your system already has a production instance. In this case, it's best to use manual Migrations , generated by users.

    
18.03.2014 / 05:17