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;
}
}
}