【问题标题】:Entity framework split model into multiple tables programmatically实体框架以编程方式将模型拆分为多个表
【发布时间】:2017-12-02 07:09:09
【问题描述】:

我正在使用 Entity Framework 6 Code First 将大型 POCO 模型存储在数据库中。该模型恰好有 1000 多个属性(不要问 lol**),这意味着它必须拆分为多个 SQL 表(列限制为 1024)。我知道这通常是通过像这样指定各个列来完成的:

modelBuilder.Entity<HugeEntity>.Map(m =>
{
    m.Properties(e => new { e.Prop1, e.Prop2 });
    m.ToTable("HugeEntity1");
}.Map(m =>
{
    m.Properties(e => new { e.Prop3, e.Prop4 });
    m.ToTable("HugeEntity2");
};

我想知道是否有任何方法可以做到这一点而不必单独指定属性。理想情况下,它可以根据给定的列限制(即 1000)自动划分实体。

即使没有标准方法,最简单的破解方法是什么?模型上的属性可能会发生变化,所以我真的很想避免在不止一个地方详尽地列出它们。

任何建议表示赞赏!

**CONTEXT:这是一个域模型,代表用户输入的数据,应该在某个网页上捕获。它也在 WebAPI 中公开。我的团队考虑了一种键/值对方法,但认为这会使未来的 BI 应用程序更难使用这些数据,这些应用程序会访问 WebAPI。

【问题讨论】:

  • Reflection 被认为是一种可能的 hack。循环遍历属性并将它们平均分配到表中。我不知道是否有“正确”的方法来做到这一点。
  • 我将成为那个人并“问”。
  • 如果可以,您可能需要查看文档存储数据库(以 mongoDB 为例)。当每个对象都可以有一个独立的模式时,它们就很有效。
  • 我会说任何具有 1000 个属性的对象肯定不是 POCO。它是某种需要重构的疯狂对象。使用它会很痛苦。 Intellisense 在尝试将属性加载到缓存时必须死掉。
  • 尽情享受你即将创造的地狱

标签: c# sql-server entity-framework entity-framework-6


【解决方案1】:

您对我们说“不要问”。但你最大的问题是你对自己说。如果您的模型有超过 50 个字段,那么您需要询问发生了什么。我想知道是否不值得喘口气并重新审视计算中的一些更抽象的概念。我将从Database Normalization 开始。 1K 动态属性告诉我你急需这个。

顺便说一句,“数据库规范化”概念本身并不特定于 SQL 数据库。您应该尽可能地标准化您的 POCO 模型。当然,OO 语言中有一些非关系概念。但是你描述的极端情况没有任何借口。

另一个范式可能是Domain Driven Design。我自己对此不太流利,所以这就是为什么我不说你应该从那里开始。但从我开始实施它开始,我会说学习曲线是值得的。

我应该小心不要居高临下。我自己并不完全拥有最高范式的所有表格。但我会告诉你,在我不知道的地方,头痛更厉害。

【讨论】:

  • 你已经得到我的投票了 但你最大的问题是你自己说的。我也认为 OP 不是在质疑设计,而是试图解决它,我相信所有这些列很可能是可以保存为只有几列的行的属性。
  • 为原始帖子添加了一些上下文。有了它,我很想知道您是否仍然强烈认为需要重新设计......
  • 不,不是。我唯一想说的是,如果它实际上已经在生产环境中,那么您当然会在一段时间内使用该设计,并且必须谨慎进行转移。此外,键值对方法还有其他缺点,甚至超出了您的团队考虑的范围。它的代价是您的 IDE 没有告诉您一项更改不会与其他一些依赖对象很好地配合。所以不要养成使用太随意的习惯。
  • 另外,您提到 POCO“可能会发生变化”。这就是为什么我说它具有“动态”属性。那可能是矫枉过正。但是您似乎在隐式使用动态属性(例如,我想您的 POCO 被使用的次数有一半,它的许多属性与目的无关)。键值方法只是使它的“动态性”变得明确。如果可能,任何动态方法,无论是显式的还是隐式的,都应转换为强类型方法。这可能比你想象的更有可能。
【解决方案2】:

想出了一个办法。我不得不使用 Linq 表达式和“动态”关键字:

    private static void SplitIntoTables<T>(DbModelBuilder modelBuilder, IReadOnlyCollection<PropertyInfo> properties, int columnLimit) where T : class
    {
        var numberOfTables = Math.Ceiling((properties.Count + (double)columnLimit / 2) / columnLimit);
        var paramExp = Expression.Parameter(typeof(T));

        var tableIndex = 0;
        foreach (var tableGroup in properties.GroupBy(p => p.Name.GetHashCode() % numberOfTables))
        {
            var expressions = tableGroup.Select(p => Expression.Lambda(
                typeof(Func<,>).MakeGenericType(typeof(T), p.PropertyType),
                Expression.Property(paramExp, p), paramExp));

            modelBuilder.Entity<T>().Map(m =>
            {
                foreach (var exp in expressions)
                {
                    m.Property((dynamic) exp);
                }
                m.ToTable($"{typeof(T).Name}_{++tableIndex}");
            });
        }
    }

【讨论】:

  • 在我之前的 cmets 中没有看到您的回复。当我在那里描述“动态”时,我指的不是关键字。以下是有关使用 dynamic 关键字的最佳做法的一些信息。
  • 这很漂亮。谢谢分享:-)
  • p.Name.GetHashCode() % numberOfTables 你为什么在这里使用哈希码?这是另一个技巧吗?
  • @t3chb0t 谢谢!哈希码使得属性进入哪个表仅由其名称(而不是其在集合中的位置)确定。这样可以更轻松地更改模型,而不会对表格产生太大影响。可以将一个添加到中间(例如),而不会导致所有属性移动一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 2016-10-16
  • 1970-01-01
相关资源
最近更新 更多