实体框架代码First Fluent Api:向列添加索引

Jim*_*lff 63 c# fluent-interface entity-framework-4 data-annotations ef-code-first

我正在运行EF 4.2 CF并想在我的POCO对象的某些列上创建索引.

举个例子,假设我们有这个员工类:

public class Employee
{
  public int EmployeeID { get; set; }
  public string EmployeeCode { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public DateTime HireDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我们经常通过EmployeeCode对员工进行搜索,因为有很多员工,因为性能原因而将索引编入索引会很不错.

我们能用流利的api以某种方式做到这一点吗?或者数据注释?

我知道可以执行这样的sql命令:

context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
Run Code Online (Sandbox Code Playgroud)

我非常想避免那样的原始SQL.

我知道这不存在,但寻找沿着这些方向的东西:

class EmployeeConfiguration : EntityTypeConfiguration<Employee>
    {
        internal EmployeeConfiguration()
        {
            this.HasIndex(e => e.EmployeeCode)
                .HasIndex(e => e.FirstName)
                .HasIndex(e => e.LastName);
        }
    }
Run Code Online (Sandbox Code Playgroud)

或者使用System.ComponentModel.DataAnnotationsPOCO可能看起来像这样(我知道这不存在):

public class Employee
{
  public int EmployeeID { get; set; }
  [Indexed]
  public string EmployeeCode { get; set; }
  [Indexed]
  public string FirstName { get; set; }
  [Indexed]
  public string LastName { get; set; }
  public DateTime HireDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

任何人对如何做到这一点有任何想法,或者如果有任何计划实现这样做,代码第一种方式?

更新:正如Robba的回答中所提到的,此功能在EF 6.1版中实现

Jim*_*lff 46

在EF 4.3中引入迁移后,您现在可以在修改或创建表时添加索引.以下是ADO.NET团队博客中基于EF 4.3代码的迁移演练的摘录

namespace MigrationsCodeDemo.Migrations
{
    using System.Data.Entity.Migrations;

    public partial class AddPostClass : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "Posts",
                c => new
                    {
                        PostId = c.Int(nullable: false, identity: true),
                        Title = c.String(maxLength: 200),
                        Content = c.String(),
                        BlogId = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.PostId)
                .ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true)
                .Index(t => t.BlogId)
                .Index(p => p.Title, unique: true);

            AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
        }

        public override void Down()
        {
            DropIndex("Posts", new[] { "BlogId" });
            DropForeignKey("Posts", "BlogId", "Blogs");
            DropColumn("Blogs", "Rating");
            DropTable("Posts");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一种很好的强类型添加索引的方法,这是我在第一次发布问题时所寻找的.

  • 这个解决方案还不够好.如果从头开始创建数据库,则迁移中添加的索引将丢失.你真的想要一个索引的数据注释. (7认同)
  • 它也在答案中代码上面说了:) (5认同)

jws*_*ler 31

您可以创建一个名为indexed的属性(如您所建议的),然后在自定义初始值设定项中选取该属性.

我创建了以下属性:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
    public IndexAttribute(bool isUnique = false, bool isClustered = false, SortOrder sortOrder = SortOrder.Ascending)
    {
        IsUnique = isUnique;
        IsClustered = isClustered;
        SortOrder = sortOrder == SortOrder.Unspecified ? SortOrder.Ascending : sortOrder;

    }

    public bool IsUnique { get; private set; }
    public bool IsClustered { get; private set; }
    public SortOrder SortOrder { get; private set; }
    //public string Where { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个自定义初始化程序,它获得了为我的上下文中的实体创建的表名列表.我有两个基类,我的所有实体都继承,所以我做了以下来获取表名:

 var baseEF = typeof (BaseEFEntity);
        var baseLink = typeof (BaseLinkTable);
        var types =
            AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(s => s.GetTypes()).Where(
                baseEF.IsAssignableFrom).Union(AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(
                    s => s.GetTypes()).Where(
                        baseLink.IsAssignableFrom));

        var sqlScript = context.ObjectContext.CreateDatabaseScript();

        foreach (var type in types)
        {
            var table = (TableAttribute) type.GetCustomAttributes(typeof (TableAttribute), true).FirstOrDefault();
            var tableName = (table != null ? table.Name : null) ?? Pluralizer.Pluralize(type.Name);
Run Code Online (Sandbox Code Playgroud)

然后,我在每个具有此属性的实体上找到所有属性,然后执行SQL命令以在每个属性上生成索引.甜!

//Check that a table exists
            if (sqlScript.ToLower().Contains(string.Format(CREATETABLELOOKUP, tableName.ToLower())))
            {

                //indexes

                var indexAttrib = typeof (IndexAttribute);
                properties = type.GetProperties().Where(prop => Attribute.IsDefined(prop, indexAttrib));
                foreach (var property in properties)
                {
                    var attributes = property.GetCustomAttributes(indexAttrib, true).ToList();

                    foreach (IndexAttribute index in attributes)
                    {
                        var indexName = string.Format(INDEXNAMEFORMAT, tableName, property.Name,
                                                      attributes.Count > 1
                                                          ? UNDERSCORE + (attributes.IndexOf(index) + 1)
                                                          : string.Empty);
                        try
                        {
                            context.ObjectContext.ExecuteStoreCommand(
                                string.Format(INDEX_STRING, indexName,
                                              tableName,
                                              property.Name,
                                              index.IsUnique ? UNIQUE : string.Empty,
                                              index.IsClustered ? CLUSTERED : NONCLUSTERED,
                                              index.SortOrder == SortOrder.Ascending ? ASC : DESC));
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
Run Code Online (Sandbox Code Playgroud)

我甚至以相同的方式继续添加基于类的索引(可能有多个列),唯一约束和默认约束.什么也很好,如果你把这些属性放在一个继承的类上,索引或约束就会被应用到继承它的所有类(表).

BTW复数形式助手包含以下内容:

public static class Pluralizer
{
    private static object _pluralizer;
    private static MethodInfo _pluralizationMethod;

    public static string Pluralize(string word)
    {
        CreatePluralizer();
        return (string) _pluralizationMethod.Invoke(_pluralizer, new object[] {word});
    }

    public static void CreatePluralizer()
    {
        if (_pluralizer == null)
        {
            var aseembly = typeof (DbContext).Assembly;
            var type =
                aseembly.GetType(
                    "System.Data.Entity.ModelConfiguration.Design.PluralizationServices.EnglishPluralizationService");
            _pluralizer = Activator.CreateInstance(type, true);
            _pluralizationMethod = _pluralizer.GetType().GetMethod("Pluralize");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


hig*_*ace 19

要建立冻结的响应,您可以自己将其编码为迁移.

首先,转到程序包管理器控制台并使用add-migration,然后为其命名,创建一个新的迁移.将出现空白迁移.坚持下去:

    public override void Up()
    {
        CreateIndex("TableName", "ColumnName");
    }

    public override void Down()
    {
        DropIndex("TableName",new[] {"ColumnName"});
    }
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用字符串字段,则需要将其限制为450个字符的长度.

  • 这里使用的DropIndex方法不正确.第二个参数是`string [] columns`或`string name`.Down方法将尝试删除名为"ColumnName"的索引.您需要在Up中指定索引名称或在Down中传入列名称数组.请参阅http://msdn.microsoft.com/en-us/library/hh829733(v=vs.103).aspx (3认同)

ECC*_*Dan 17

我最近也研究了这个,没有找到其他方法,所以我决定在播种数据库时创建索引:

public class MyDBInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
    private MyContext _Context;

    protected override void Seed(MyContext context)
    {
        base.Seed(context);
        _Context = context;

        // We create database indexes
        CreateIndex("FieldName", typeof(ClassName));

        context.SaveChanges();
    }

    private void CreateIndex(string field, Type table)
    {
        _Context.Database.ExecuteSqlCommand(String.Format("CREATE INDEX IX_{0} ON {1} ({0})", field, table.Name));
    }    
}   
Run Code Online (Sandbox Code Playgroud)


Rob*_*bba 14

请注意,在Entity Framework 6.1(目前处于测试阶段)将支持IndexAttribute来注释索引属性,这将自动导致Code First Migrations中的(唯一)索引.

  • 它现在:[EF 6.1:使用IndexAttribute创建索引](http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/) (2认同)

Dav*_*ret 11

对于使用Entity Framework 6.1+的任何人,您可以使用流畅的api执行以下操作:

modelBuilder 
    .Entity<Department>() 
    .Property(t => t.Name) 
    .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));
Run Code Online (Sandbox Code Playgroud)

阅读文档中的更多内容.


Pet*_*ter 7

那么我在网上找到了一个解决方案,并根据我的需求对其进行了调整,它是:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
    public IndexAttribute(string name, bool unique = false)
    {
        this.Name = name;
        this.IsUnique = unique;
    }

    public string Name { get; private set; }

    public bool IsUnique { get; private set; }
}

public class IndexInitializer<T> : IDatabaseInitializer<T> where T : DbContext
{
    private const string CreateIndexQueryTemplate = "CREATE {unique} INDEX {indexName} ON {tableName} ({columnName});";

    public void InitializeDatabase(T context)
    {
        const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
        Dictionary<IndexAttribute, List<string>> indexes = new Dictionary<IndexAttribute, List<string>>();
        string query = string.Empty;

        foreach (var dataSetProperty in typeof(T).GetProperties(PublicInstance).Where(p => p.PropertyType.Name == typeof(DbSet<>).Name))
        {
            var entityType = dataSetProperty.PropertyType.GetGenericArguments().Single();
            TableAttribute[] tableAttributes = (TableAttribute[])entityType.GetCustomAttributes(typeof(TableAttribute), false);

            indexes.Clear();
            string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;

            foreach (PropertyInfo property in entityType.GetProperties(PublicInstance))
            {
                IndexAttribute[] indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
                NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])property.GetCustomAttributes(typeof(NotMappedAttribute), false);
                if (indexAttributes.Length > 0 && notMappedAttributes.Length == 0)
                {
                    ColumnAttribute[] columnAttributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), false);

                    foreach (IndexAttribute indexAttribute in indexAttributes)
                    {
                        if (!indexes.ContainsKey(indexAttribute))
                        {
                            indexes.Add(indexAttribute, new List<string>());
                        }

                        if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
                        {
                            string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
                            indexes[indexAttribute].Add(columnName);
                        }
                        else
                        {
                            indexes[indexAttribute].Add(property.PropertyType.Name + "_" + GetKeyName(property.PropertyType));
                        }
                    }
                }
            }

            foreach (IndexAttribute indexAttribute in indexes.Keys)
            {
                query += CreateIndexQueryTemplate.Replace("{indexName}", indexAttribute.Name)
                            .Replace("{tableName}", tableName)
                            .Replace("{columnName}", string.Join(", ", indexes[indexAttribute].ToArray()))
                            .Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);
            }
        }

        if (context.Database.CreateIfNotExists())
        {
            context.Database.ExecuteSqlCommand(query);
        }
    }

    private string GetKeyName(Type type)
    {
        PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
        foreach (PropertyInfo propertyInfo in propertyInfos)
        {
            if (propertyInfo.GetCustomAttribute(typeof(KeyAttribute), true) != null)
                return propertyInfo.Name;
        }
        throw new Exception("No property was found with the attribute Key");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在dbcontext中重载OnModelCreating

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer(new IndexInitializer<MyContext>());
        base.OnModelCreating(modelBuilder);
    }
Run Code Online (Sandbox Code Playgroud)

将index属性应用于您的实体类型,使用此解决方案,您可以在同一索引中使用相同的名称和唯一的多个字段.