【问题标题】:Get TableName of Entity while using Fluent API使用 Fluent API 时获取实体的表名
【发布时间】:2015-07-05 16:32:11
【问题描述】:

我正在使用 fluent api 为我的实体更改表的名称,例如:

 public class TestMap : EntityTypeConfiguration<Domain.Test>
    {
        public TestMap()
        {
            ToTable("Test");
        }
    }

现在稍后在我的DbContext 中,我想找到一个类型的表名,但是当我查看MetadataWorkspace 内部时,我发现我找不到类型Domain.Test。我能够找到“测试”表,但除非我硬编码,否则我无法匹配这些表。所以我的问题是如何在MetadataWorkspace 中找到类型名。

我用来尝试查找类型Domain.Test的代码:

     ObjectContext octx = (context as IObjectContextAdapter).ObjectContext; ;
                var es = octx.MetadataWorkspace
                                .GetItems<EntityContainer>(DataSpace.SSpace)
                                .SelectMany(c => c.BaseEntitySets);

编辑我的代码的简化示例供人们测试:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var db = new Database();
            db.Set<Test>().Add(new Test {Name = "test"});
            var amount = db.SaveChanges();
        }

        public abstract class AbstractTest
        {
            public int Id { get; set; }
        }

        public class Test : AbstractTest
        {
            public string Name { get; set; }
        }

        public class Database : DbContext
        {

            public Database() : base("Database")
            {
                this.Configuration.ProxyCreationEnabled = false;
                this.Configuration.LazyLoadingEnabled = false;
            }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);

                // Remove pluralized tables
                modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
                modelBuilder.Conventions.Remove<PluralizingEntitySetNameConvention>();

                // Add all configurations within the current assembly.
                modelBuilder.Configurations.AddFromAssembly(typeof(Database).Assembly);
            }

             public override int SaveChanges()
             {
                 foreach (DbEntityEntry ent in ChangeTracker.Entries())
                 {
                     Type entityType = ent.Entity.GetType();

                     var names = Utility.GetTableNames(entityType, this);
                 }

                 return base.SaveChanges();
             }
        }

        public class AbstractTestMap : EntityTypeConfiguration<AbstractTest>
        {
            public AbstractTestMap()
            {
                this.HasKey(t => t.Id);
            }
        }

        public class TestMap : EntityTypeConfiguration<Test>
        {
            public TestMap()
            {
                this.Property(t => t.Name)
                    .HasMaxLength(200);

                ToTable("Tests");
            }
        }

        public static class Utility
        {
            public static IEnumerable<string> GetTableNames<TEntity>(DbContext context)
            {
                return GetTableNames(typeof(TEntity), context);
            }
            public static IEnumerable<string> GetTableNames(Type type, DbContext context)
            {
                var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

                // Get the part of the model that contains info about the actual CLR types
                var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

                // Get the entity type from the model that maps to the CLR type
                var entityType = metadata
                        .GetItems<EntityType>(DataSpace.OSpace)
                        .Single(e => objectItemCollection.GetClrType(e) == type);

                // Get the entity set that uses this entity type
                var entitySet = metadata
                    .GetItems<EntityContainer>(DataSpace.CSpace)
                    .Single()
                    .EntitySets
                    .Single(s => s.ElementType.Name == entityType.Name);

                // Find the mapping between conceptual and storage model for this entity set
                var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                        .Single()
                        .EntitySetMappings
                        .Single(s => s.EntitySet == entitySet);

                // Find the storage entity sets (tables) that the entity is mapped
                var tables = mapping
                    .EntityTypeMappings.Single()
                    .Fragments;

                // Return the table name from the storage entity set
                return tables.Select(f => (string)f.StoreEntitySet.MetadataProperties["Table"].Value ?? f.StoreEntitySet.Name);
            }
        }
    }
}

【问题讨论】:

  • 您是要专门将表名更改为独特的名称,还是只是因为复数而使用此名称?如果问题是复数,请查找modelBuilder.Conventions.Remove&lt;PluralizingTableNameConvention&gt;();
  • @Claies 我正在尝试获取表名,因为我正在为我的实体构建某种审计/历史记录。所以在History 表中有一个包含表名的列。因此,当实体更新时,我需要按其类型获取该实体的表。另外(我已经删除了PluralizingTableNameConvention 并通过ToTabe(xx)EntityTypeConfiguration 中使用我自己的名字。
  • @GertArnold 这样我只能得到表名,我不确定(没有硬编码)哪个是我拥有的类型。
  • 你应该可以使用BaseEntitySets按类型过滤。

标签: c# entity-framework ef-fluent-api


【解决方案1】:

使用该实体:

namespace YourNamespace.Domain{
    public class Test{
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

还有这张地图:

namespace YourNamespace {
    public class TestMap : EntityTypeConfiguration<Domain.Test> {
        public TestMap() {
            ToTable("Test");
        }
    }
}

还有这个上下文:

namespace YourNamespace {
    public class MyContext : DbContext{
        public DbSet<Test> Tests { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder){
            base.OnModelCreating(modelBuilder);
            modelBuilder.Configurations
                .AddFromAssembly(Assembly.GetExecutingAssembly());
        }
    }
}

你可以使用这个方法:

public static class Utility {
    public static IEnumerable<string> GetTableNames<TEntity>(this DbContext context) {
        return GetTableNames(typeof(TEntity), context);
    }
    public static IEnumerable<string> GetTableNames(Type type, DbContext context) {
        var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

        // Get the part of the model that contains info about the actual CLR types
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        // Get the entity type from the model that maps to the CLR type
        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        // Get the entity set that uses this entity type
        var entitySet = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace)
            .Single()
            .EntitySets
            .Single(s => s.ElementType.Name == entityType.Name);

        // Find the mapping between conceptual and storage model for this entity set
        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        // Find the storage entity sets (tables) that the entity is mapped
        var tables = mapping
            .EntityTypeMappings.Single()
            .Fragments;

        // Return the table name from the storage entity set
        return tables.Select(f => (string)f.StoreEntitySet.MetadataProperties["Table"].Value ?? f.StoreEntitySet.Name);
    }
}

它将返回所有表名的列表。对于单个表实体 - 您的情况是 - ,只需使用 tableNames.FirstOrDefault(t=&gt; !string.IsNullOrWhiteSpace(t))。此外,您可以将其用作实用方法或扩展方法:

class Program {
    static void Main(string[] args) {
        // getting the list:
        using (var ctx = new MyContext()){
            var tableNames = ctx.GetTableNames<Test>();
            foreach (var tableName in tableNames)
                Console.WriteLine(tableName);
            Console.ReadLine();
        }

        // getting the single table-name - your case - 
        using (var ctx = new MyContext()){
            var tableNames = ctx.GetTableNames<Test>();
            var tableName = tableNames.FirstOrDefault(t=> !string.IsNullOrWhiteSpace(t))
            Console.WriteLine(tableName);
            Console.ReadLine();
        }
    }
}

见源文章here

更新:

基于更新的问题:我们在这里有一个HierarchyMapping;所以我们需要改变我们的GetTableNames 方法来支持继承:

public static class Utility {
    public static IEnumerable<string> GetTableNames<TEntity>(this DbContext context) {
        return GetTableNames(typeof(TEntity), context);
    }
    public static IEnumerable<string> GetTableNames(Type type, DbContext context) {
        var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

        // Get the part of the model that contains info about the actual CLR types
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        // Get the entity type from the model that maps to the CLR type
        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        // Get the entity set that uses this entity type
        var entitySetTop = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace).SelectMany(s => s.EntitySets);
        //.Single()
        //.BaseEntitySets;

        var entitySet = entitySetTop
            .SingleOrDefault(s => s.ElementType.Name == entityType.Name);
        EntitySet entitySet2 = null;
        foreach (var s in entitySetTop) {
            if (s.ElementType.Name == entityType.Name) {
                entitySet2 = s;
                break;
            }
            var temp = entityType.BaseType;
            while (temp != null) {
                if (s.ElementType.Name == temp.Name) {
                    entitySet2 = s;
                    break;
                }
                temp = temp.BaseType;
            }
            if (entitySet2 != null)
                break;
        }
        entitySet = entitySet2;


        // Find the mapping between conceptual and storage model for this entity set
        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        // Find the storage entity sets (tables) that the entity is mapped
        var tables = mapping
            .EntityTypeMappings.Where(f => {
                if (f.IsHierarchyMapping) {
                    return f.EntityTypes.Any(e => e.Name == entityType.Name);
                }
                return f.EntityType.Name == entityType.Name;
            }).SelectMany(t => t.Fragments); //.Single()
        //.Fragments;

        // Return the table name from the storage entity set
        return tables.Select(f => (string)f.StoreEntitySet.MetadataProperties["Table"].Value ?? f.StoreEntitySet.Name);
    }
}

【讨论】:

  • 由于某种原因,metadata.GetItems&lt;EntityContainer&gt;(DataSpace.CSpace).Single().EntitySets 不包含我的类型。它确实存在于数据库中并且确实存在于SSpace 但那里ElementType.Name 是“测试”。
  • @Julian 发布您的模型、上下文和映射器。
  • 我在帖子中添加了一个控制台应用程序示例。这段代码给了我同样的错误。
  • @Julian 查看更新后的答案,基于您的继承
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多