繁体   English   中英

自引用表与 Ef Core Fluent 的唯一且不同的密钥对 API

[英]Self referencing table Unique and distinct key pair with Ef Core Fluent API

我有一个多对多的自引用表,我希望有一个由两个外键组成的索引(或键)。 我还想避免相同的键重复和重新排序的键,比如

UserParentId xxx UserChildId

0 1 => 好的

0 2 => 好的

1 1 => 不行

2 0 => 不行,因为这个链接已经存在(见第二行)

最重要的是,我想使用无序提供的两个键在此索引中进行搜索。 就像是

_dbContext.UserToUsers.Find(userId1, userId2)

不知道 userId1 代表UserParentId还是UserChildId

我读过这个解决方案,它可能是一个很好的通用方法

model 应该让UserparentId始终严格低于UserChildId ,控制器应该都知道这一点。

尽管如此,我仍然想知道是否已经在 EF CORE 中实现了类似的功能。 Index() 或 HasAlternateKey() 似乎没有提供我想要的。 任何的想法?

我的代码:

Model:

public class User
{
    public int Id { get; set; }

    public virtual ICollection<UserToUser> ChildrenUsers { get; set; }
    public virtual ICollection<UserToUser> ParentUsers { get; set; }
}
public class UserToUser
{
    public int Id { get; set; }
    public int UserParentId { get; set; }
    public int UserChildId { get; set; }

    public virtual User ChildUser { get; set; }
    public virtual User ParentUser { get; set; }
}

然后是我的 Fluent API 代码:

 modelBuilder.Entity<UserToUser>().HasOne(d => d.ChildUser)
            .WithMany(p => p.ParentUsers)
            .HasForeignKey(d => d.UserParentId); 

        modelBuilder.Entity<UserToUser>().HasOne(d => d.ParentUser)
            .WithMany(p => p.ChildrenUsers)
            .HasForeignKey(d => d.UserChildId);

        modelBuilder.Entity<UserToUser>()
            .HasAlternateKey(x => new {x.UserChildId, x.UserParentId});

关于使用查找:

DbSet 上的 Find 方法使用键值来尝试查找上下文跟踪的实体。 如果在上下文中找不到该实体,则将向数据库发送查询以在其中查找该实体。 如果在上下文或数据库中找不到该实体,则返回 Null。

Find不支持通过替代键进行缓存查找,尽管尚未实现,但在未来将不再需要,因为查询缓存的替代方法将会发展,这意味着您可以编写一个名为Find的扩展方法来专门执行您的要求。 看看这个问题和与之相关的问题: DbSet.Find by alternate key or predicate (你不是第一个提出这种请求的人)

因此,您要求 EF 在表中提供多个PrimaryKeys ,但也强制执行一旦用户是另一个用户的孩子,他们就不能成为同一用户的级,因此所有这些组合的值必须是唯一的:

  1. ID
  2. UserChildId, UserParentId
  3. UserParentId, UserChildId

解决这个问题的另一种方法是认识到对于指定的UserId您可以在每个User上使用 Find ,特别是如果您无论如何都想要两个用户(因为链接表中没有其他信息,并且两个用户对象都可以包含链接引用)

var user1 = _dbContext.Users.Find(userId1);
var user2 = _dbContext.Users.Find(userId2);
var link = user1.ChildrenUsers.FirstOrDefault(x => x.UserChildId == userId2) 
        ?? user2.ChildrenUsers.FirstOrDefault(x => x.UserChildId == userId1)
if (link != null)
{
    // process when the link exists
} 

这将绕过本地缓存,但可以完成工作:

_dbContext.UserToUsers.Where(x => UserParentId == userId1 && UserChildId == userId2
                                  || UserParentId == userId2 && UserChildId == userId1);

如果您只是进行存在性检查,则将Where更改为Any ,这样在服务器上运行同样有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM