using SQLite.CodeFirst.Extensions; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Infrastructure; using System.Data.Entity.Infrastructure.Annotations; using System.Data.Entity.ModelConfiguration.Conventions; using System.Linq; using SQLite.CodeFirst.Builder.NameCreators; namespace SQLite.CodeFirst.Convention { /// /// Renames the indicies, generated by the /// -Convention, to match the scheme: "IX_{TableName}_{PropertyName}". /// This is necessary because in SQLite an index-name must be unique. /// Must be added right after the -Convention. /// internal class SqliteForeignKeyIndexConvention : IStoreModelConvention { private const string IndexAnnotationName = "http://schemas.microsoft.com/ado/2013/11/edm/customannotation:Index"; public virtual void Apply(AssociationType item, DbModel model) { if (item == null) { throw new ArgumentNullException("item"); } if (model == null) { throw new ArgumentNullException("model"); } if (item.Constraint == null) { return; } foreach (var edmProperty in item.Constraint.ToProperties) { var annotation = GetAnnotation(edmProperty.MetadataProperties, IndexAnnotationName); if (annotation == null) { continue; } string tableName = item.Constraint.ToRole.GetEntityType().GetTableName(); string propertyName = edmProperty.Name; // The original attribute is removed. The none-ForeignKeyIndicies will be remained and readded without any modification // and the foreignKeyIncidies will be readded with the correct name. edmProperty.RemoveAnnotation(IndexAnnotationName); // The schema for the automatically generated index name is "IX_{TableName}_{PropertyName}" var noneForeignKeyIndicies = annotation.Indexes.Where(index => index.Name != "IX_" + propertyName); var newIndexAnnotation = new IndexAnnotation(noneForeignKeyIndicies); // The schema for a FK index, which is generated by the Entity Framework, is "IX_{PropertyName}" var foreignKeyIndicies = annotation.Indexes.Where(index => index.Name == "IX_" + propertyName); for (int i = 0; i < foreignKeyIndicies.Count(); i++) { IndexAnnotation foreignKeyIndexAnnotation = CreateIndexAnnotation(tableName, propertyName, i); newIndexAnnotation = (IndexAnnotation)newIndexAnnotation.MergeWith(foreignKeyIndexAnnotation); } edmProperty.AddAnnotation(IndexAnnotationName, newIndexAnnotation); } } private static IndexAnnotation CreateIndexAnnotation(string tableName, string propertyName, int count) { var indexName = IndexNameCreator.CreateIndexName(tableName, propertyName); // If there are two Indicies on the same property, the count is added. // In SQLite an Index name must be global unique. // To be honest, it should never happen. But because its possible by using the API, it should be covered. if (count > 0) { indexName = String.Format("{0}_{1}", indexName, count); } var indexAttribute = new IndexAttribute(indexName); return new IndexAnnotation(indexAttribute); } private static IndexAnnotation GetAnnotation(IEnumerable metadataProperties, string name) { foreach (MetadataProperty metadataProperty in metadataProperties) { if (metadataProperty.Name.Equals(name, StringComparison.Ordinal)) { return (IndexAnnotation)metadataProperty.Value; } } return null; } } }