【问题标题】:Map two different entities to the same table?将两个不同的实体映射到同一张表?
【发布时间】:2011-02-23 16:14:41
【问题描述】:

我的数据库中有一个包含很多字段的表。 大多数时候我需要所有这些字段。但是,在一种情况下,我只需要几个字段,并且要加载大量行。

我想做的是手动添加一个实体,然后简单地将它映射到原始表,但删除我不需要的列。我设置了这一切,但我得到了一个不言自明的错误:

映射片段的问题 ...EntitySets 'FmvHistoryTrimmed' 和 'FMVHistory' 都映射到 表'FMVHistory'。他们的主键 可能会发生碰撞。

我还有其他方法可以解决这个问题吗?同样,大部分时间所有列都被使用,所以我不想修剪原始实体并将“额外”字段放入复杂类型。

【问题讨论】:

  • 我有一个来自 Aducci 的解决方法,但如果有人有实际的 EF 方法以我最初寻找的方式完成此任务,我将不予回答

标签: entity-framework entity-framework-4


【解决方案1】:

您不能将两个常规实体映射到同一个表中。您有多种选择:

  1. 使用表拆分。
  2. 使用投影到非实体类型的自定义查询(如@Aducci 建议的那样)
  3. 使用查询视图
  4. 使用数据库视图或直接DefiningQuery

表格拆分

Table splitting 允许您以 1:1 的关系将一个表映射到两个实体。第一个实体将仅包含 PK 和您始终需要的字段子集。第二个实体将包含所有其他字段和 PK。两个实体都将包含彼此的导航属性。现在,如果您只需要字段的子集,您将查询第一个实体。如果您需要所有字段,您将查询第一个实体并将导航属性包含到第二个实体。如果需要,您还可以延迟加载第二个实体。

查询视图

QueryView 是直接在您的映射 (MSL) 中定义的 ESQL 查询,它被映射到新的只读实体类型。您可以使用 QueryView 定义将完整实体投影到子实体中。 QueryView 必须在 EDMX 中手动定义(在设计器中不可用)。据我所知,QueryView 在 Code first 中不可用,但它实际上与自定义投影到非实体类型相同。

定义查询

DefiningQuery 是直接在您的存储模型 (SSDL) 中定义的自定义查询。 DefiningQuery 通常在映射到数据库视图时使用,但您可以将其用于任何自定义 SQL SELECT。您将查询结果映射到只读实体类型。 DefiningQuery 必须在 EDMX 中手动定义(在设计器中不可用)。它也不直接在 Code first 中可用,但实际上与在 DbDatabase 上调用 SqlQuery 相同。 DefiningQuery 的问题在于,一旦您在 SSDL 中手动定义它,您就不能使用从数据库中更新模型,因为此操作会替换完整的 SSDL 并删除您的查询定义。

【讨论】:

  • 这是一个来自 MSDN 的非常简单的教程,介绍了实体拆分技术:msdn.microsoft.com/en-us/data/jj715646.aspx 但是,根据您的主键数据模型结构,您可能需要重塑模型。
  • 能在hibernate中实现分表吗?
  • 我尝试按照here 的描述实现表拆分。发生的事情是 EF 坚持将共享字段重命名为 XXXX1。例如,链接 {int LinkId; int DocIdRight; int DocIdLeft} 和 LinkPartial {int LinkId; int DocIdRight;} ==> 更新 LinkPartial 对象时,EF 尝试同时更新 DocIdRight 和 DocIdRight1。
  • Ladislav,你知道5.5年后有没有更新吗?代码优先方法的任何更简单的方法可能是?...谢谢。
【解决方案2】:

我会在数据库上创建一个仅包含您需要的数据的视图,并将该视图添加到您的实体数据模型中。

如果您不想修改数据库,您可以创建一个 Linq to 实体或 ESQL 语句,投影到 POCO 类,只包含您需要的信息。

public IQueryable<SimpleObject> GetView(DBContext context)
{
    return  (from obj in context.ComplexObjects
            select new SimpleObject() { Property1 = obj.Property1,
                                        Property1 = obj.Property2
                                      }); 
}

【讨论】:

  • 我绝对不想创建视图,但自定义 linq 表达式应该可以工作。那里也有一个 1..0|1 关系,但是一个简单的 linq 外连接也应该把它拉进去。我只是认为在 EF 设计器中映射它会更优雅一些。谢谢你的回答!
【解决方案3】:

我曾经有一个技巧可以让多个实体映射到一个表。

首先,您的实体必须派生自一个通用类型。此基本类型本身不会成为您的上下文的一部分。

给上下文一个假表名,可以解析得到原始表名。我使用了表名格式:$$$TableName$$$。它可以有 3 美元或更多。

然后截取命令,替换命令文本。

层次结构:

    class MyEntityBase
    {
        // Common properties here
    }

    [Table("MyTable")]
    class MyEntityV1 : MyEntityBase
    {
    }

    [Table("$$$MyTable$$$")]
    class MyEntityV2 : MyEntityBase
    {
    }

拦截器:

class EntityNameReplacerInterceptor: DbCommandInterceptor
{
        private static Regex regex = new Regex("([\\$]{3,})(.+)\\1", RegexOptions.Compiled);

        public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
        {
            ManipulateCommand(command);
            return base.ReaderExecuting(command, eventData, result);
        }

        public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
        {
            ManipulateCommand(command);
            return base.NonQueryExecuting(command, eventData, result);
        }

        public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
        {
            ManipulateCommand(command);
            return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
        }

        public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
        {
            ManipulateCommand(command);
            return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
        }

        public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
        {
            ManipulateCommand(command);
            return base.ScalarExecuting(command, eventData, result);
        }

        public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)
        {
            ManipulateCommand(command);
            return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
        }

        private void ManipulateCommand(DbCommand result)
        {
              result.CommandText = regex.Replace(result.CommandText, "$2");
        }
}

配置DbContext

DbSet<MyEntityV1> V1s { get; set; }
DbSet<MyEntityV2> V2s { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.AddInterceptors(new EntityNameReplacerInterceptor());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 2017-02-08
    • 2020-06-09
    • 1970-01-01
    相关资源
    最近更新 更多