Similar to Tobias Schittkowski's answer but C# and has the capability to have multiple fields in the constrtaints.
To use this, just place a [Unique] on any field you wish to be unique. For strings, you will have to do something like (note the MaxLength attribute):
[Unique]
[MaxLength(450)]
public string Name { get; set; }
because the default string field is nvarchar(max) and that will not be allowed in a key.
For multiple fields in the constraint you can do:
[Unique(Name="UniqueValuePairConstraint", Position=1)]
public int Value1 { get; set; }
[Unique(Name="UniqueValuePairConstraint", Position=2)]
public int Value2 { get; set; }
First, the UniqueAttribute:
internal class UniqueAttribute : Attribute
{
public string Name { get; set; }
public int Position { get; set; }
}
Then, include a useful extension to get the database table name from a type:
public static class Extensions
{
public static string GetTableName(this DbContext context, Type type)
{
return ((IObjectContextAdapter)context)
.ObjectContext.GetTableName(type);
}
public static string GetTableName(this ObjectContext context, Type type)
{
var genericTypes = new[] { type };
var takesNoParameters = new Type[0];
var noParams = new object[0];
object objectSet = context.GetType()
.GetMethod("CreateObjectSet", takesNoParameters)
.MakeGenericMethod(genericTypes)
.Invoke(context, noParams);
var sql = (string)objectSet.GetType()
.GetMethod("ToTraceString", takesNoParameters)
.Invoke(objectSet, noParams);
Match match =
Regex.Match(sql, @"FROM\s+(.*)\s+AS", RegexOptions.IgnoreCase);
return match.Success ? match.Groups[1].Value : null;
}
}
Then, the database initializer:
public class DatabaseInitializer : IDatabaseInitializer<PedContext>
{
public void InitializeDatabase(FooContext context)
{
if (context.Database.Exists()
&& !context.Database.CompatibleWithModel(false))
{
context.Database.Delete();
}
if (!context.Database.Exists())
{
context.Database.Create();
foreach (PropertyInfo contextPropertyInfo in
context.GetType().GetProperties())
{
var contextPropertyType = contextPropertyInfo.PropertyType;
if (contextPropertyType.IsGenericType
&& contextPropertyType.Name.Equals("DbSet`1"))
{
Type tableType =
contextPropertyType.GetGenericArguments()[0];
var tableName = context.GetTableName(tableType);
foreach (var uc in UniqueConstraints(tableType, tableName))
{
context.Database.ExecuteSqlCommand(uc);
}
}
}
context.SaveChanges();
}
}
private static IEnumerable<string> UniqueConstraints(
Type tableClass, string tableName)
{
var uniqueConstraints =
new Dictionary<string, List<Tuple<int, string>>>();
foreach (PropertyInfo entityPropertyInfo in tableClass.GetProperties())
{
var unique = entityPropertyInfo.GetCustomAttributes(true)
.OfType<UniqueAttribute>().FirstOrDefault();
if (unique != null)
{
string fieldName = entityPropertyInfo.Name;
string constraintName = unique.Name
?? string.Format(
"constraint_{0}_unique_{1}",
tableName
.Replace("[", string.Empty)
.Replace("]", string.Empty)
.Replace(".", "_"),
fieldName);
List<Tuple<int, string>> constraintEntry;
if (!uniqueConstraints.TryGetValue(
constraintName, out constraintEntry))
{
uniqueConstraints.Add(
constraintName,
new List<Tuple<int, string>>
{
new Tuple<int, string>(
unique.Position, fieldName)
});
}
else
{
for (int i = 0; ; ++i)
{
if (i == constraintEntry.Count)
{
constraintEntry.Add(
new Tuple<int, string>(
unique.Position, fieldName));
break;
}
if (unique.Position < constraintEntry[i].Item1)
{
constraintEntry.Insert(
i,
new Tuple<int, string>(
unique.Position, fieldName));
break;
}
}
}
}
}
return
uniqueConstraints.Select(
uc =>
string.Format(
"ALTER TABLE {0} ADD CONSTRAINT {1} UNIQUE ({2})",
tableName,
uc.Key,
string.Join(",", uc.Value.Select(v => v.Item2))));
}
}
ObjectContext
orDbContext
. – Shimmy Weitzhandler