ایندکس منحصر به فرد با استفاده از Data Annotation در EF Code First
نویسنده: پدرام جباری
تاریخ: ۱۳۹۱/۰۹/۲۲ ۲۳:۲۸
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public class User
{
public int Id { get; set; }
[Unique]
public string Email { get; set; }
[Unique("MyUniqueIndex",UniqueIndexOrder.ASC)]
public string Username { get; set; }
[Unique(UniqueIndexOrder.DESC)]
public string PersonalCode{ get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
using System;
namespace SampleUniqueIndex
{
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public class UniqueAttribute : Attribute
{
public UniqueAttribute(UniqueIndexOrder order = UniqueIndexOrder.ASC) {
Order = order;
}
public UniqueAttribute(string indexName,UniqueIndexOrder order = UniqueIndexOrder.ASC)
{
IndexName = indexName;
Order = order;
}
public string IndexName { get; private set; }
public UniqueIndexOrder Order { get; set; }
}
public enum UniqueIndexOrder
{
ASC,
DESC
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Reflection;
namespace SampleUniqueIndex
{
public static class DbContextExtention
{
private static BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
public static void ExecuteUniqueIndexes(this DbContext context)
{
var tables = GetTables(context);
var query = "";
foreach (var dbSet in GetDbSets(context))
{
var entityType = dbSet.PropertyType.GetGenericArguments().First();
var table = tables[entityType.Name];
var currentIndexes = GetCurrentUniqueIndexes(context, table.TableName);
foreach (var uniqueProp in GetUniqueProperties(context, entityType, table))
{
var indexName = string.IsNullOrWhiteSpace(uniqueProp.IndexName) ?
"IX_Unique_" + uniqueProp.TableName + "_" + uniqueProp.FieldName :
uniqueProp.IndexName;
if (!currentIndexes.Contains(indexName))
{
query += "ALTER TABLE [" + table.TableSchema + "].[" + table.TableName + "] ADD CONSTRAINT [" + indexName + "] UNIQUE ([" + uniqueProp.FieldName + "] " + uniqueProp.Order + "); ";
}
else
{
currentIndexes.Remove(indexName);
}
}
foreach (var index in currentIndexes)
{
query += "ALTER TABLE [" + table.TableSchema + "].[" + table.TableName + "] DROP CONSTRAINT " + index + "; ";
}
}
if (query.Length > 0)
context.Database.ExecuteSqlCommand(query);
}
private static List<string> GetCurrentUniqueIndexes(DbContext context, string tableName)
{
var sql = "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS where table_name = '"
+ tableName + "' and CONSTRAINT_TYPE = 'UNIQUE'";
var result = context.Database.SqlQuery<string>(sql).ToList();
return result;
}
private static IEnumerable<PropertyDescriptor> GetDbSets(DbContext context)
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(context))
{
var notMapped = prop.GetType().GetCustomAttributes(typeof(NotMappedAttribute),true);
if (prop.PropertyType.Name == typeof(DbSet<>).Name && notMapped.Length == 0)
yield return prop;
}
}
private static List<UniqueProperty> GetUniqueProperties(DbContext context, Type entity, TableInfo tableInfo)
{
var indexedProperties = new List<UniqueProperty>();
var properties = entity.GetProperties(PublicInstance);
var tableName = tableInfo.TableName;
foreach (var prop in properties)
{
if (!prop.PropertyType.IsValueType && prop.PropertyType != typeof(string)) continue;
UniqueAttribute[] uniqueAttributes = (UniqueAttribute[])prop.GetCustomAttributes(typeof(UniqueAttribute), true);
NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])prop.GetCustomAttributes(typeof(NotMappedAttribute), true);
if (uniqueAttributes.Length > 0 && notMappedAttributes.Length == 0)
{
var fieldName = GetFieldName(context, entity, prop.Name);
if (fieldName != null)
{
indexedProperties.Add(new UniqueProperty
{
TableName = tableName,
IndexName = uniqueAttributes[0].IndexName,
FieldName = fieldName,
Order = uniqueAttributes[0].Order.ToString()
});
}
}
}
return indexedProperties;
}
private static Dictionary<string, TableInfo> GetTables(DbContext context)
{
var tablesInfo = new Dictionary<string, TableInfo>();
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var tables = metadata.GetItemCollection(DataSpace.SSpace)
.GetItems<EntityContainer>()
.Single()
.BaseEntitySets
.OfType<EntitySet>()
.Where(s => !s.MetadataProperties.Contains("Type")
|| s.MetadataProperties["Type"].ToString() == "Tables");
foreach (var table in tables)
{
var tableName = table.MetadataProperties.Contains("Table")
&& table.MetadataProperties["Table"].Value != null
? table.MetadataProperties["Table"].Value.ToString()
: table.Name;
var tableSchema = table.MetadataProperties["Schema"].Value.ToString();
tablesInfo.Add(table.Name, new TableInfo
{
EntityName = table.Name,
TableName = tableName,
TableSchema = tableSchema,
});
}
return tablesInfo;
}
public static string GetFieldName(DbContext context, Type entityModel, string propertyName)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var osMembers = metadata.GetItem<EntityType>(entityModel.FullName, DataSpace.OSpace).Properties;
var ssMebers = metadata.GetItem<EntityType>("CodeFirstDatabaseSchema." + entityModel.Name, DataSpace.SSpace).Properties;
if (!osMembers.Contains(propertyName)) return null;
var index = osMembers.IndexOf(osMembers[propertyName]);
return ssMebers[index].Name;
}
internal class UniqueProperty
{
public string TableName { get; set; }
public string FieldName { get; set; }
public string IndexName { get; set; }
public string Order { get; set; }
}
internal class TableInfo
{
public string EntityName { get; set; }
public string TableName { get; set; }
public string TableSchema { get; set; }
}
}
}
public static void ExecuteUniqueIndexes(this DbContext context)
{
var tables = GetTables(context);
...
}
private static Dictionary<string, TableInfo> GetTables(DbContext context)
{
var tablesInfo = new Dictionary<string, TableInfo>();
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var tables = metadata.GetItemCollection(DataSpace.SSpace)
.GetItems<EntityContainer>()
.Single()
.BaseEntitySets
.OfType<EntitySet>()
.Where(s => !s.MetadataProperties.Contains("Type")
|| s.MetadataProperties["Type"].ToString() == "Tables");
foreach (var table in tables)
{
var tableName = table.MetadataProperties.Contains("Table")
&& table.MetadataProperties["Table"].Value != null
? table.MetadataProperties["Table"].Value.ToString()
: table.Name;
var tableSchema = table.MetadataProperties["Schema"].Value.ToString();
tablesInfo.Add(table.Name, new TableInfo
{
EntityName = table.Name,
TableName = tableName,
TableSchema = tableSchema,
});
}
return tablesInfo;
}
public static void ExecuteUniqueIndexes(this DbContext context)
{
var tables = GetTables(context);
var query = "";
foreach (var dbSet in GetDbSets(context))
{
....
}
public static void ExecuteUniqueIndexes(this DbContext context)
{
...
foreach (var dbSet in GetDbSets(context))
{
var entityType = dbSet.PropertyType.GetGenericArguments().First();
var table = tables[entityType.Name];
var currentIndexes = GetCurrentUniqueIndexes(context, table.TableName);
}
}
public static string GetFieldName(DbContext context, Type entityModel, string propertyName)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var osMembers = metadata.GetItem<EntityType>(entityModel.FullName, DataSpace.OSpace).Properties;
var ssMebers = metadata.GetItem<EntityType>("CodeFirstDatabaseSchema." + entityModel.Name, DataSpace.SSpace).Properties;
if (!osMembers.Contains(propertyName)) return null;
var index = osMembers.IndexOf(osMembers[propertyName]);
return ssMebers[index].Name;
}
protected override void Seed(myDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
context.ExecuteUniqueIndexes();
}
Violation of UNIQUE KEY constraint 'IX_Unique_Members_Email'. Cannot insert duplicate key in object 'dbo.Members'
create unique nonclustered index idx on dbo.DimCustomer(emailAddress) where EmailAddress is not null;
public class MyContext: DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().Property(p => p.Name).HasMaxLength(100);
modelBuilder.Entity<Person>().HasIndex(p => p.Name).IsUnique();
}
}