【问题标题】:EF Code First: Relationships with differing key namesEF Code First:具有不同键名的关系
【发布时间】:2014-10-02 22:33:23
【问题描述】:

我有两张桌子:

DocumentSet:
setId (int)
name (varchar(50))

DocIdentifier:
docIdId (int)
docSetId (int) <-- (setId from DocumentSet)
customId (char(9))

在两个表上完成的典型查询在 SQL 中如下所示:

SELECT ds.name
FROM DocumentSet ds
INNER JOIN DocIdentifiers di ON ds.setId = di.docSetId
WHERE di.customId == 'someId'

注意命名差异:setIddocSetId 是用于外键的列。当我尝试在实体框架中复制它时,问题就来了。我有明显的模型:

class DocumentSet {
    public int Id {get;set;}
    public string Name {get;set;}
    public virtual ICollection<DocumentIdentifier> Identifiers {get;set;}
}

class DocumentIdentifier {
    public int Id {get;set;}
    public int SetId {get;set;}
    public string CustomId {get;set;}
    public DocumentSet DocSet {get;set;}
}

再次,为了代码的清晰起见,一些命名差异。所以我为两者创建了EntityTypeConfigurations:

internal class DocumentSetTypeConfiguration : EntityTypeConfiguration<DocumentSet> {
    public DocumentSetTypeConfiguration () {
        HasKey(f => f.Id);
        Property(f => f.Id).HasColumnName("setId");
        HasMany(f => f.DocumentIdentifiers);
    }
}

internal class DocumentIdentifierTypeConfiguration : EntityTypeConfiguration<DocumentIdentifier> {
    public DocumentIdentifierTypeConfiguration () {
        ToTable("DocIdentifier");
        HasKey(f => f.Id);
        Property(f => f.Id).HasColumnName("docIdId");
        Property(f => f.SetId).HasColumnName("docSetId");
    }
}

问题是:当我发送这样的 LINQ 查询时:

_context.Set<DocumentSet>().Where(d => d.DocumentIdentifiers.Any(pt=>pt.CustomId == customId)).Single();

生成的内容有些拙劣:

SELECT TOP (2) 
    [Extent1].[SetId] AS [SetId], 
    [Extent1].[Name] AS [Name]
    FROM [dbo].[DocumentSet] AS [Extent1]
    WHERE  EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[DocIdentifier] AS [Extent2]
        WHERE ([Extent1].[SetId] = [Extent2].[DocumentSet_Id]) AND (([Extent2].CustomId = @p__linq__0) OR (([Extent2].[CustomId] IS NULL) AND (@p__linq__0 IS NULL)))
    )

我猜问题在于setIddocSetId 是不同的名称,但我似乎无法弄清楚如何让EF 看到[Extent2].[DocumentSet_Id] 应该是[Extent2].[docSet_Id]。我在DocumentIdentifierTypeConfiguration 中遗漏了什么可以帮助 EF 理解这一点吗?我以为Property(f =&gt; f.SetId).HasColumnName("docSetId"); 行会这样做,但显然不是。

总之,怎么说呢

`DocumentSet has many DocumentIdentifiers with a foreign key relationship of DocumentSet.setId = DocIdentifier.docSetId`?

【问题讨论】:

    标签: c# sql linq entity-framework entity-framework-6.1


    【解决方案1】:

    尝试添加WithRequiredHasForeignKey

    HasMany(f => f.Identifiers).WithRequired().HasForeignKey(x => x.SetId);
    

    完整代码

    public class AppContext : DbContext
    {
        public DbSet<DocumentSet> DocumentSets { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new DocumentSetTypeConfiguration());
            modelBuilder.Configurations.Add(new DocumentIdentifierTypeConfiguration());
        }
    }
    public class DocumentSetTypeConfiguration : EntityTypeConfiguration<DocumentSet>
    {
        public DocumentSetTypeConfiguration()
        {
            HasKey(f => f.Id);
            Property(f => f.Id).HasColumnName("setId");
            HasMany(f => f.DocumentIdentifiers).WithRequired().HasForeignKey(x => x.SetId);
        }
    }
    public class DocumentIdentifierTypeConfiguration : EntityTypeConfiguration<DocumentIdentifier>
    {
        public DocumentIdentifierTypeConfiguration()
        {
            ToTable("DocIdentifier");
            HasKey(f => f.Id);
            Property(f => f.Id).HasColumnName("docIdId");
            Property(f => f.SetId).HasColumnName("docSetId");
        }
    }
    public class DocumentSet
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<DocumentIdentifier> DocumentIdentifiers { get; set; }
    }
    public class DocumentIdentifier
    {
        public int Id { get; set; }
        public int SetId { get; set; }
        public string CustomId { get; set; }
    }
    

    数据库结果

    查询结果

    SELECT TOP (2) 
        [Extent1].[setId] AS [setId], 
        [Extent1].[Name] AS [Name]
    FROM [dbo].[DocumentSets] AS [Extent1]
    WHERE  EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[DocIdentifier] AS [Extent2]
        WHERE ([Extent1].[setId] = [Extent2].[docSetId])
        AND   (([Extent2].[CustomId] = @p__linq__0) 
              OR (([Extent2].[CustomId] IS NULL) AND (@p__linq__0 IS NULL)))
    )
    

    【讨论】:

    • 这工作正常(有一些修改,请参阅我的答案)。现在我必须弄清楚为什么 EF 突然不再连接到数据库了......
    • @StephenCollins,您不必添加DocSet(但如果您愿意,可以),只需尝试添加上面的代码,它应该可以工作,WithRequired 可以在指定后接受空参数HasMany,如果使用MapKey,则public int SetId {get;set;}类中的外键值不会作为外键连接
    • 你的建议听起来是对的,但问题又是命名。你的建议给我一个ModelValidationException,而MapKey 方法 正确生成模型/查询,但问题出在:HasMany(ds =&gt; ds.Identifiers).WithRequired().Map(di =&gt; di.MapKey("docSetId"));。这失败了,因为并告诉我`类型中的每个属性名称都必须是唯一的。属性名称“docSetId”已定义。`
    • @StephenCollins,我发布了完整的代码和结果,你能指出缺少什么吗?
    • 您的代码在新项目中运行良好。我的代码中一定还有其他问题。我还有更多的挖掘工作要做。谢谢。
    猜你喜欢
    • 2017-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多