【问题标题】:How to remove duplicate records being returned?如何删除返回的重复记录?
【发布时间】:2022-01-16 14:39:10
【问题描述】:

我有 3 个表:users、posts 和 likes。我想做一个 lambda 表达式来返回一个包含用户名、postText 和喜欢(真或假)的数组

 var myList = _context.Posts.Join(_context.Users,
            post => post.UserID_FK,
            user => user.ID,
            (post, user) => new { Post = post, User = user })
            .Join(
            _context.Likes,
            u => u.User.ID,
            likes => likes.UserID,
            (u, likes) => new PostDTO
            {
                ID = u.Post.ID,
                username = u.Patient.UserName,
                Text = u.Post.Text,
                Likes = u.Post.Likes,
                liked = (likes.PostID == u.Post.ID && likes.UserID == userModel.ID)}
          .OrderByDescending(d => d.Date);

        return myList;

我的问题是我的代码得到了我想要的一切,但我得到了重复的记录。我想了解为什么我会收到重复的记录?我已经搜索了 lambda 表达式,但我无法弄清楚我的问题出在哪里。

提前谢谢你们!

【问题讨论】:

  • 请输入样本数据和所需输出
  • 发布您的模型。可能你不需要在这里加入。

标签: c# entity-framework linq lambda linq-to-sql


【解决方案1】:

发生这种情况是因为与连接表的 SQL 查询一样,您正在为所有组合构建笛卡尔坐标。作为一个带有 2 个赞的帖子的简单示例,我们在其中运行带有内部连接的 SQL 语句:

SELECT p.PostId, l.LikeId FROM Posts p INNER JOIN Likes l ON p.PostId == l.PostId WHERE p.PostId = 1

我们会回来的:

PostId     LikeId
  1            1
  1            2

显然,从 Post 和 Like 返回更多数据以填充有意义的视图,但原始数据看起来会有很多重复。这与更多的一对多关系复合,因此您需要手动解释数据以解决您收到的帖子有两个赞。

当您正确利用导航属性来映射实体之间的关系时,EF 将完成繁重的工作,将这些笛卡尔组合重新转换为有意义的模型结构。

以如下模型结构为例:

public class Post
{
    public int PostId { get; set; }
    public DateTime Date { get; set; }
    // ... Other Post-related data fields.

    public virtual User Patient { get; set; }
    public virtual ICollection<Like> Likes { get; set; } = new List<Like>();
}

public class Like
{
    public int LikeId { get; set; }
    public virtual Post Post { get; set; } 
    public virtual User User { get; set; }
}

public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }
}

EF 可能会通过约定自动解决所有这些关系,但我建议您熟悉显式配置,因为在您想要或需要使用不同命名约定的特定情况下,约定总是会失败。 EF 中有四种解决关系的选项:约定、属性、EntityTypeConfiguration 和使用OnModelCreating 的模型构建器。下面的示例通过 EF6 概述了 EntityTypeConfiguration。 EF 代码使用一个名为 IEntityTypeConfiguration 的接口,它以类似的方式工作,但您实现了一个配置方法,而不是在构造函数中调用基类方法。属性和约定通常是最少的工作量,但我通常会遇到它们没有映射的情况。 (使用 EF Core 似乎更可靠)通过 modelBuilder 进行配置是较小应用程序的一种选择,但它很快就会变得混乱。

public class PostConfiguration : EntityTypeConfiguration<Post>
{
    public PostConfiguration()
    {
        ToTable("Posts");
        HasKey(x => x.PostId)
            .Property(x => x.PostId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    
        HasRequired(x => x.Patient)
            .WithMany()
            .Map(x => x.MapKey("PatientUserId"));
 
        HasMany(x => x.Likes)
            .WithRequired(x => x.Post)
            .Map(x => x.MapKey("PostId"));
    }
}

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        ToTable("Users");
        HasKey(x => x.UserId)
            .Property(x => x.UserId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

public class LikeConfiguration : EntityTypeConfiguration<Like>
{
    public LikeConfiguration()
    {
        ToTable("Likes");
        HasKey(x => x.LikeId)
            .Property(x => x.LikeId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    
        HasRequired(x => x.User)
            .WithMany()
            .Map(x => x.MapKey("UserId"));
    }
}

// In your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Configurations.AddFromAssembly(GetType().Assembly);
}

现在,当您要查询数据时,EF 将管理 FK 关系并解析任何联接和生成的笛卡尔积。问题就变成了你想从领域模型中得到什么数据?

var viewData = _context.Posts
    .Select(p => new PostDTO
    {
        ID = p.PostID,
        UserName = p.Patient.UserName,
        Text = p.Text,
        Likes = p.Likes
            .Select(l => new LikeDTO
            {
                ID = l.LikeId
                UserName = l.User.UserName
            }).ToList(),
        Liked = p.Likes.Any(l => l.User.UserId == userModel.UserId)
    }).OrderByDescending(p => p.Date)
    .ToList();

这是基于您的原始查询的粗略猜测,您希望在哪里发布帖子、患者姓名、喜欢的内容以及当前用户是否喜欢帖子的指示符。请注意,没有显式连接,甚至没有急切加载。 (Include) EF 将仅使用填充 DTO 所需的列和关联数据所需的 ID 构建必要的语句。

您还应避免在返回数据时将 DTO 与实体混合。在上面的示例中,我为 Like 和 Post 引入了 DTO,因为我们想从我们的域中返回有关 Likes 的一些详细信息。我们不会传回对实体的引用,因为当它们被序列化时,序列化程序会尝试触摸每个可能导致延迟加载被触发的属性,并且总体上会返回比我们的消费者需要或应该看到的更多信息。通过导航属性映射和表达关系后,EF 将自动构建具有所需连接的查询,并处理返回的数据以填充您希望看到的内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-31
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多