entity-frameworkunique-constraintef-model-first

Entity Framework model first: create UNIQUE constraint programmatically


I am trying to add a UNIQUE constraint to the "Username" property of my "UserAccount" entity/class. With code-first, that would be no problem, but for model-first, I can't find anything on how to achieve that. The designer does not support this feature. I cannot use annotations because the entity classes are auto-generated. I cannot use Fluent API because the OnModelCreating() method is not called in model-first and thus I have no DbModelBuilder instance. The only thing I can think of is executing some kind of manual SQL statement at application start that creates the UNIQUE constraint, which kind of defeats the purpose of EF. Here is my current DbContext class:

 public partial class UserAccountsModelContainer : DbContext
 {
    public UserAccountsModelContainer()
        : base("name=UserAccountsModelContainer")
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<UserAccount> UserAccounts { get; set; }
}

I won't even bother to post the UserAccount class since it's auto-generated and shouldn't be modified (I know that the DbContext is also auto-generated, but modifying it is possible). Any help on this is appreciated!


Solution

  • First I will recommend you to switch to Entity Framework Code First, too. It gives you much more controll about every thing that is possible with EF.

    I never used it before, but I know Model Conventions. They are applicable to the model configuration. Maybe it will be an approach to set up a convention for a defined model type/property that should be configured as unique constraint.

    Based on the following it should be possible to modify the set up of model first on creating database.

    Model Conventions are based on the underlying model metadata. There are conventions for both CSDL and SSDL. Create a class that implements IConceptualModelConvention from CSDL conventions and implement IStoreModelConvention for SSDL convention.

    Source: http://www.entityframeworktutorial.net/entityframework6/custom-conventions-codefirst.aspx

    There are two types of model conventions, Conceptual (C-Space) and Store (S-Space). This distinction indicates where in the pipeline a convention is executed. A C-Space convention is applied to the model that the application builds, whereas an S-Space convention is applied to the version of the model.

    Source: https://entityframework.codeplex.com/wikipage?title=Custom%20Conventions

    Some more example implementations incl. explainations are findabe on msdn. I guess they are very helpful for your case.

    One example from MSDN:

    public class DiscriminatorRenamingConvention : IStoreModelConvention<EdmProperty>  
    {  
        public void Apply(EdmProperty property, DbModel model)  
        {            
            if (property.Name == "Discriminator")  
            {  
                property.Name = "EntityType";  
            }  
        }  
    }
    

    It will rename the column Discriminator into EntityType. It is a very simple example but you could modify it to solve your problem to:

    public class ModelBasedConvention : IConceptualModelConvention<EdmProperty>
    {
        public void Apply(EdmProperty property, DbModel model)
        {
            if (property.Name == "Username"
                && property.DeclaringType.GetType() == typeof(UserAccount))
            {
                property.AddAnnotation("UniqueKey", property);
            }
        }
    }