如何删除返回的重复记录



我有3个表:用户、帖子和点赞。我想做一个lambda表达式来返回一个数组,该数组包含username、postText和like(true或false(

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表达式,但我不知道我的问题在哪里。

我提前感谢你们!

之所以会出现这种情况,是因为与连接表的SQL查询一样,您正在为所有组合构建笛卡尔坐标。作为一个有2个赞的Post的简单例子,我们运行一个带有内部联接的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 and 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中解析关系有四个选项:Convention、Attributes、EntityTypeConfiguration和使用OnModelCreating的modelBuilder。以下示例通过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关系并解析任何Joins和生成的笛卡尔乘积。问题只是变成了你想要从域模型中得到什么数据?

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将自动构建具有所需联接的查询,并处理返回的数据以填充您期望看到的内容。

最新更新