3

我这辈子都想不通。MSDN上的文章不清楚,似乎已经过时了。我正在使用 EF 5,并且正在尝试为以下内容设置单向关系。这就是我到目前为止所拥有的。

public sealed class Capture {
    /// <summary>
    /// Get and Set Capture's Unique Identifier.
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Get and Set Capture's Operating System.
    /// </summary>
    public OperatingSystem OperatingSystem { get; set; }
}

public sealed class OperatingSystem {
    /// <summary>
    /// Operating System's Unique Identifier.
    /// </summary>
    public int Id { get; set; }
}

internal sealed class EntityCaptureConfiguration : EntityTypeConfiguration<Capture> {
    /// <summary>
    /// Create an Entity Capture Configuration.
    /// </summary>
    public EntityCaptureConfiguration() {
        this.ToTable("Capture");
        this.HasKey(m => m.Id);

        this.Property(m => m.Id).HasColumnName("Id");

        this.HasRequired(m => m.OperatingSystem).WithRequiredDependent().Map(m => {
            m.ToTable("OperatingSystem");
            m.MapKey("OperatingSystemId");
        });
    }
}

编译并运行,我得到以下运行时异常:“错误 3034:从第 16 行、第 46 行开始映射片段时出现问题:具有可能不同键的两个实体映射到同一行。确保这两个映射片段映射关联集的两端到相应的列。

我不禁注意到,实际上没有明确的方法表明我想使用 2 个特定键加入这 2 个表。我的意思是我在哪里指定子表中的键以用于父表中的连接?

以下是我需要帮助的内容:

  1. 我不想在Capture类型上公开一个名为“ OperatingSystemId ”的属性。“ OperatingSystem ” 属性绰绰有余。

  2. 我不希望CaptureOperatingSystem类型之间存在双向关系。只需要从CaptureOperatingSystem的单向关系。

  3. 我不想在名为“ CaptureId ”的操作系统表上的数据库中创建一列。我什至不知道为什么需要这样做,但我在某处读到一篇文章建议这样做。

  4. 生成的 SQL 连接应该是 INNER JOIN 而不是 OUTER JOIN。

  5. 我真的需要将 Capture 类型上的 OperatingSystem 属性定义为虚拟吗?我不希望这个属性被延迟加载。

  6. 奖金指向可以向我解释这个例外的人,因为我完全迷路了。谷歌搜索对于 Fluent API 是没有希望的。如果我使用设计器,我会得到很多点击,但我认为它不相关,因为所有结果都建议重新生成模型,我认为这不适用于这里。

更新: 我可能不清楚这一点。我已经在 SQL Server 中设置了数据库模型。我试图了解如何在使用设计器的情况下使用 Fluent API 为现有数据库模型配置 EF 上下文。我知道如果我使用设计器,我可以简单地要求它从数据库中生成我的上下文。那不是我想要的。对于那些好奇的人来说——因为我想了解如何使用 Fluent API 来做到这一点。

Update2: 所以,看起来就像我设置的那样,如果在我的查询中我在导航属性上指定了一个过滤器,EF 会正确生成连接。如果不这样做,则不会在查询中生成连接。无论如何,导航属性仍然​​没有被填充!

提前感谢一堆。

4

1 回答 1

10

因此,如果您没有像我一样使用过 ORM,并且来自使用 MyBatis 或类似的数据映射器的背景,那么解决方案就是这个,给出我问题中的示例。

首先,您不能将实体类型声明为密封,并且任何参与关联的属性都需要声明为virtual。这是因为 EF 将在运行时使用动态代理扩展类型,并使用动态代理覆盖您的属性以启用延迟加载:

public class Capture {
    /// <summary>
    /// Get and Set Capture's Unique Identifier.
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Get and Set Capture's Operating System.
    /// </summary>
    public virtual OperatingSystem OperatingSystem { get; set; }
}

public class OperatingSystem {
    /// <summary>
    /// Operating System's Unique Identifier.
    /// </summary>
    public int Id { get; set; }
}

如果您不这样做,则不会引发错误,但 EF 将始终将您的关联属性设置为 null。

其次,除非您的关联属性尚未配置为映射,否则不要将其映射到特定表,因为 EF 会抱怨同一个表实际上被映射了两次。在我的示例中,我已经自己映射了OperatingSystem类型,并且在设置关联时,我再次映射它:

internal sealed class EntityCaptureConfiguration : EntityTypeConfiguration<Capture> {
    /// <summary>
    /// Create an Entity Capture Configuration.
    /// </summary>
    public EntityCaptureConfiguration() {
        this.ToTable("Capture");
        this.HasKey(m => m.Id);

        this.Property(m => m.Id).HasColumnName("Id");

        this.HasRequired(m => m.OperatingSystem).WithRequiredDependent().Map(m => m.MapKey("OperatingSystemId"));
    }
}

最后,如果您必须绝对将您的实体类型声明为密封并且您不想将任何属性声明为虚拟 - 在密封类型上无论如何都不能 - 在您的上下文配置中,禁用代理和延迟加载:

public sealed class EntityDefaultContext : DbContext {    
    /// <summary>
    /// Model Creating Event Handler.
    /// </summary>
    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        var entityCaptureConfiguration = new EntityCaptureConfiguration();
        var entityOperatingSystemConfiguration = new EntityOperatingSystemConfiguration();

        modelBuilder.Configurations.Add(entityOperatingSystemConfiguration);
        modelBuilder.Configurations.Add(entityCaptureConfiguration);

        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }
}

这就是所有人。我希望这对将来像我这样的人有所帮助。

于 2012-10-21T22:46:17.460 回答