combination of two field must be unique in Entity Framework code first approach. how it would?

Checking for duplicates means you have to go to the database to validate. In Entity Framework Code First, that means using the DbContext. See Implementing Validation in the Context with ValidateEntity for a great explanation of how to validate in Entity Framework.

You should override the ValidateEntity method in your context class:

    protected override DbEntityValidationResult ValidateEntity(
                                        DbEntityEntry entityEntry, 
                                        IDictionary<object, object> items)
    {
        //base validation for Data Annotations, IValidatableObject
        var result = base.ValidateEntity(entityEntry, items);

        //You can choose to bail out before custom validation
        //if (result.IsValid)
        //    return result;

        CustomValidate(result);
        return result;
    }

    private void CustomValidate(DbEntityValidationResult result)
    {
        ValidateContacts(result);
        ValidateOrganisation(result);
    }

private void ValidateContacts(DbEntityValidationResult result)
{
    var c = result.Entry.Entity as Contact;
    if (c== null)
        return;

    if (Contacts.Any(a => a.FirstName == c.FirstName 
                          && a.LastName == c.LastName 
                          && a.ID != c.ID))
        result.ValidationErrors.Add(
                          new DbValidationError("Name", 
                                "Name already exists"));
}

private void ValidateOrganisation(DbEntityValidationResult result)
{
    var organisation = result.Entry.Entity as Organisation;
    if (organisation == null)
        return;

    if (Organisations.Any(o => o.Name == organisation.Name 
                               && o.ID != organisation.ID))
        result.ValidationErrors.Add(
                              new DbValidationError("Name", 
                                    "Name already exists"));
}

This validation is triggered when there is a call to SaveChanges. If there are any errors, a DbEntityValidationException is thrown.

More about structuring validation here

For a “belt and braces” approach I also add unique indexes to the database on my natural keys – in a migration. Thus preventing invalid data because of inserts to the database that do not go via Entity Framework :

public partial class Adduniqueindexes : DbMigration
{
    public override void Up()
    {
        //You have to use Sql if the column is nullable:
        Sql(@"CREATE UNIQUE INDEX IX_UPRN ON Properties(UPRN, OrganisationID) 
            WHERE UPRN IS NOT NULL"));
        CreateIndex("dbo.Organisations", 
                    "Name", 
                     unique: true, 
                     name: "IX_NaturalKey");
        CreateIndex("dbo.Contacts", 
                    new string[] { "FirstName", "LastName" }, 
                    unique: true, 
                    name: "IX_NaturalKey");
    }

    public override void Down()
    {
        DropIndex("dbo.Properties", "IX_UPRN");
        DropIndex("dbo.Organisations", "IX_NaturalKey");
        DropIndex("dbo.Contacts", "IX_NaturalKey");
    }
}

More about indexes here

Additional note
From EF6.1 onwards it is possible to indicate that an index should be created on a field by adding a data attribute:

[Index("IX_NaturalKey", IsUnique = true)]
[Required] //If the field is nullable then you have to create the index in the migration
           //using sql, so I'd only expect IsUnique = true on a Required field
[StringLength(256)] //indexes must be less than 900 bytes in Sql Server,
                    //so nvarchar(max) will not do
public string Name{ get; set; }

Leave a Comment