简体   繁体   English

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

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

I have a many to many table for self referencing for which I'd like to have an index (or a key) made of two foreign keys.我有一个多对多的自引用表,我希望有一个由两个外键组成的索引(或键)。 I'd also like to avoid same key repetition and reordered keys like我还想避免相同的键重复和重新排序的键,比如

UserParentId xxx UserChildId UserParentId xxx UserChildId

0 1 => ok 0 1 => 好的

0 2 => ok 0 2 => 好的

1 1 => not ok 1 1 => 不行

2 0 => not ok because this link already exists (see second line) 2 0 => 不行,因为这个链接已经存在(见第二行)

On top of that I would like to search within this index with the two keys provided disorderly.最重要的是,我想使用无序提供的两个键在此索引中进行搜索。 Something like就像是

_dbContext.UserToUsers.Find(userId1, userId2) _dbContext.UserToUsers.Find(userId1, userId2)

without knowing if userId1 represent UserParentId or UserChildId .不知道 userId1 代表UserParentId还是UserChildId

I've read this solution which could be a good general approach :我读过这个解决方案,它可能是一个很好的通用方法

The model should keep UserparentId always strictly inferior to UserChildId and controllers should all be aware of that. model 应该让UserparentId始终严格低于UserChildId ,控制器应该都知道这一点。

Nevertheless, I am still wondering if something like this have already been implemented in EF CORE.尽管如此,我仍然想知道是否已经在 EF CORE 中实现了类似的功能。 Index() or HasAlternateKey() does not seem to provide what I'd like. Index() 或 HasAlternateKey() 似乎没有提供我想要的。 Any idea?任何的想法?

My Code:我的代码:

Model: 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; }
}

Then my Fluent API code:然后是我的 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});

In regard to using Find:关于使用查找:

The Find method on DbSet uses the primary key value to attempt to find an entity tracked by the context. DbSet 上的 Find 方法使用键值来尝试查找上下文跟踪的实体。 If the entity is not found in the context then a query will be sent to the database to find the entity there.如果在上下文中找不到该实体,则将向数据库发送查询以在其中查找该实体。 Null is returned if the entity is not found in the context or in the database.如果在上下文或数据库中找不到该实体,则返回 Null。

Find does not support cache lookup via alternate keys, although not yet implemented, in the future it will not be necessary as alternate ways to query the cache will evolve, meaning you could write an extension method called Find to do specifically what you ask. Find不支持通过替代键进行缓存查找,尽管尚未实现,但在未来将不再需要,因为查询缓存的替代方法将会发展,这意味着您可以编写一个名为Find的扩展方法来专门执行您的要求。 Have a look at this issue and those related to it: DbSet.Find by alternate key or predicate (you are not the first to ask this sort of request)看看这个问题和与之相关的问题: DbSet.Find by alternate key or predicate (你不是第一个提出这种请求的人)

So you are asking EF to provide multiple PrimaryKeys on a table, but to also enforce that once a user is a child of another user that they cannot be a parent to the same user, so the values from all these combinations must be unique:因此,您要求 EF 在表中提供多个PrimaryKeys ,但也强制执行一旦用户是另一个用户的孩子,他们就不能成为同一用户的级,因此所有这些组合的值必须是唯一的:

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

A different way around this is recognizing that for the specified UserId s you can use Find on each User , especially if you wanted both users anyway (as there is no other information in the linking table, and both user objects can contain the link reference)解决这个问题的另一种方法是认识到对于指定的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
} 

This will bypass the local cache, but will do the job:这将绕过本地缓存,但可以完成工作:

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

If you are merely doing an existence check, then change the Where to Any and this will be just as efficient to run on the server.如果您只是进行存在性检查,则将Where更改为Any ,这样在服务器上运行同样有效。

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

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