问题描述
(我是C#的初学者) 我想使用2个列表(用户列表和帖子列表)创建Person类的列表,这里是类结构的外观。问题:在使用LINQ创建对象时,我希望不创建同一个人对象(即,具有相同名称,userId,Id)。使用LINQ甚至可能吗? (我正在使用System.linq)
class User
{
public string Id { get; set; }
public string Name { get; set; }
}
class Post
{
public string Id { get; set; }
public string UserId { get; set; }
public string Text { get; set; }
}
class Person
{
public string Name { get; set; }
public string Id { get; set; }
public string UserId { get; set; }
public List<string> listofPosts { get; set; }
public Person(string Id,string Name,string UserId)
{
this.Name = Name;
this.Id = Id;
this.UserId = Id;
}
}
class Test
{
static void Main()
{
List<User> userList = UserRepository.GetUsers(); // sample data
List<Post> postList = PostRepository.GetPosts(); // sample data
/*
* i want to create List of Person where person will contain UserId,ID,* name,listofPost(list<string> - containg all the post of user)
* this is the code is wrote for creating new person,but how will i populate
* person's listofPost ?
* what changes do i need to make in person class ?
* what changes should i do so that same person object is not created (same userId,Id,name) and contins List of post?
*
* one approach what i thought is - to remove duplicates by merging objects using loops.
* can i do this using LINQ ?
* Or Is it even possible ?
*
*/
List<Person> personList = (from u in userList
from p in postList
where u.Id == p.UserId
select (new Person(u.Id,u.Name,p.UserId))
).ToList<Person>();
// if i go by this logic then this creates duplicate objects ( same name,id,UserId,but with diff text ),// ( after modifying constructor and passing p.Text )
}
}
我尝试编写逻辑,将其进行谷歌搜索,查看其他stackOverflow问题,但无法找到/理解如何解决此问题。 我是C#的初学者,还不到(经验丰富的20天)。
解决方法
您的Linq查询可以简化。我从您的描述中得到的是,您想要加载所有User
,将它们选择到Person
中,然后将所有Post
对象添加到其中。
请考虑以下内容:
var persons = users.Select(u =>
{
var person = new Person(u.Name,u.Id);
person.Posts = posts.Where(p => p.UserId == u.Id).ToList();
return person;
}).ToList();
首先,我假设您不需要p.UserId
Ctor中的Person
,因为它与u.Id
相同。
我的发言的第二项内容是遍历所有用户,并从中找出人。在此期间,我加载了所有具有匹配Post
的{{1}}对象,并将它们分配给“帖子列表”。
您可以使用group
语句
List<Person> personList = (from u in userList
from grp in from p in postList
group p by p.UserId
where u.Id == grp.Key
select new Person(u.Id,u.Name,grp.Key)
{
listOfPosts = grp.Select(x => x.Text).ToList()
}).ToList<Person>();
grp(由UserId分组)包含该用户的帖子列表,您可以将其查询为IEnumerable 在MSDN
中有更多相关信息或者您可以对Person
构造函数进行以下更改,并将grp作为第四个参数传递:
public Person(string Id,string Name,string UserId,IEnumerable<Post> posts)
{
this.Name = Name;
this.Id = Id;
this.UserId = Id;
this.listOfPosts = posts.Select(p => p.Text).ToList();
}
,
因此,您有一个Users
序列和一个Posts
序列。每个User
都具有零个或多个Posts
,每个Post
就是正好一个User
的Post,即外键UserId
所指的用户。一个直接的一对多关系。
实际上,可以使用LINQ来获取“带有帖子的用户”。您可以将一个“用户”中的每个“用户”转换为“用户”的所有帖子,并将其放入属性ListOfPosts
中。
还有改进的空间
首先有一些建议:
此外:您的人员有一个Id
和一个UserId
。有什么不同?不是此人创建的用户的ID?
如果创建要由LINQ语句填充的类,通常(仅)使该类成为默认构造函数会很容易。
class Person
{
public string Id { get; set; }
public string Name{ get; set; }
public ICollection<string> PostTexts { get; set; }
}
这样的类通常称为POCO(普通的老式c#对象):仅具有setter和getter的类,而没有添加的功能。
如果您真的无法更改,请说服项目负责人相信使用默认构造函数会更好,您可以使用它,但是请确保非默认构造函数至少构造一个正确的对象:为什么要命名,但没有帖子列表?
顺便说一句,我可以自由更改您的ListOfPosts。首先,它是一个字符串列表,而不是帖子列表,此外,它不包含帖子,而仅包含帖子的文本。建议:尽可能使用正确描述属性含义的属性名称。这种方式使用将不必查找字符串中的内容,因为它们不是字符串,所以他们知道这不是帖子。
此外:您可以定义Post[4]
的值吗?您是否希望用户要求Pos [4]?建议:如果您希望用户不需要这些函数,则不要向类的调用者公开这些函数。只会使您更难以更改内部结构。
因此,如果您认为无法解释列表功能,但仍然希望列表中的所有内容(除索引之外):添加/删除/计数/枚举,请考虑创建一个ICollection<...>
,这将为您提供内部拥有列表以外的其他东西的自由,例如数组,HashSet甚至是Dictionary。
回到您的问题
因此,根据给定的用户和帖子序列,您想要创建一个人序列。
只要您与一对多关系,并且想要“带有零个或多个子项目的项目”,例如“带有订单的客户”,“带有学生的学校”和“带有帖子的用户” ,请考虑使用Enumerable.GroupJoin
的重载之一如果您要指定与“原始项目及其原始子项目”不同的结果,请使用带有参数resultSelector的重载:
IEnumerable<User> users = ...
IEnumerable<Post> posts = ...
var persons = users.GroupJoin(posts // GroupJoin users and posts
user => user.Id,// from every user take the Id
post => post.UserId,// from every Post take the foreign key UserId
// parameter resultSelector:
// From every User,with his zero or more Posts make one Person
(user,postsOfThisUser) => new Person
{
Id = user.Id,Name = user.Name,PostTexts = postsOfThisUser.Select(post => post.Text).ToList(),})
因此,即使PostTexts属性包含列表,您以后也可以自由更改它,而不必更改语句的用法。