Filtering IEnumerable child property - c#

I'm attempting to bring back a list of objects. This object has an IEnumerable property of a second class. I'm attempting to filter this child list based on a condition.
There are the classes:
public class Parent
{
public int Id { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual IEnumerable<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public int OtherId { get; set; }
public bool Active { get; set; }
public virtual Parent Parent { get; set; }
}
Here is the EF code where I am attempting to get the parents and filter the children:
public IEnumerable<ParentViewModel> GetParents(int otherId)
{
var parents = _databaseContext.Parents
.Include(i => i.Children.Where(child => child.OtherId == otherId));
return parents;
}
When I call this method, I get an ArgumentException, with the message:
The Include path expression must refer to a navigation property defined on the type.
Use dotted paths for reference navigation properties and the Select operator for
collection navigation properties.
Given that the exception mentions using Select, I've tried doing that too:
public IEnumerable<ParentViewModel> GetParents(int otherId)
{
var parents = _databaseContext.Parents
.Where(parent => parent.Active == true)
.Include(parent => parent.Children);
.Select(parent => new
{
Active = parent.Active,
Id = parent.Id,
Children = parent.Children
.Where(child => child.OtherId == propertyId)
.Select(child => new
{
Active = child.Active,
Id = child.Id,
ParentId = child.ParentId,
OtherId = child.OtherId,
Title = child.Title
},
Title = parent.Title
});
return parents;
}
This also blows up, giving me the exception:
The specified type member 'Children' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
And that's where I'm all out of ideas! I don't know what I'm doing wrong, but this doesn't feel like it should be as hard as it has been, so I'm guessing that I'm missing something pretty fundamental to Entity Framework.

Ah, ok! I am not familiar with Code First (I am using a generated model in my Project) but I am pretty sure that EF doesn't recognize that these Entities are related. The error Message: " ... and entity navigation properties are supported" tells me that you should define a navigation property (a Relation between these Entities).
public class Parent
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public int OtherId { get; set; }
public bool Active { get; set; }
public int ParentId [get;set;}
[ForeignKey("ParentId")]
public virtual Parent Parent { get; set; }
}

Related

EF include list is always null

By some reason EF wont load the included list properly so it ends up being null all the time.
Here is the entities i'm using:
[Table("searchprofilepush")]
public class SearchProfilePush
{
public int Id { get; set; }
public int AccountId { get; set; }
public bool Push { get; set; }
public int UserPushId { get; set; }
public UserPush UserPush { get; set; }
public int SearchProfileId { get; set; }
public SearchProfile SearchProfile { get; set; }
public ICollection<SearchProfileMediaTypePush> SearchProfileMediaTypePush { get; set; }
}
[Table("searchprofilemediatypepush")]
public class SearchProfileMediaTypePush
{
public int Id { get; set; }
public MediaTypeType MediaType { get; set; }
public bool Push { get; set; }
public int SearchProfilePushId { get; set; }
public SearchProfilePush SearchProfilePush { get; set; }
}
Then when i'm trying to do this:
var searchProfilePush = _dataContext.SearchProfilePush.Include(w => w.SearchProfileMediaTypePush).FirstOrDefault(w => w.AccountId == accountId && w.SearchProfileId == searchProfileId);
My included list is always null.
I guess it's some obvious reason why this doesn't work but i just can't figure it out.
Thanks!
EDIT:
Here is the sql query:
SELECT \"Extent1\".\"id\", \"Extent1\".\"accountid\", \"Extent1\".\"push\", \"Extent1\".\"userpushid\", \"Extent1\".\"searchprofileid\" FROM \"public\".\"searchprofilepush\" AS \"Extent1\" WHERE \"Extent1\".\"accountid\" = #p__linq__0 AND #p__linq__0 IS NOT NULL AND (\"Extent1\".\"searchprofileid\" = #p__linq__1 AND #p__linq__1 IS NOT NULL) LIMIT 1
EDIT 2:
I have now mapped my entities both way and the list is still always null.
Edit 3:
This is how i created my database tables.
The documentation I read for loading related entities has some differences with the sample code and your code. https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
First, when you define your ICollection, there is no keyword virtual:
public virtual ICollection<SearchProfileMediaTypePush> SearchProfileMediaTypePush { get; set; }
Next, in the example close to yours, where they load related items using a query, the first or default is not using a boolean expression. The selective expression is in a where clause:
// Load one blogs and its related posts
var blog1 = context.Blogs
.Where(b => b.Name == "ADO.NET Blog")
.Include(b => b.Posts)
.FirstOrDefault();
So you can try:
var searchProfilePush = _dataContext.SearchProfilePush
.Where(w => w.AccountId == accountId && w.SearchProfileId == searchProfileId)
.Include(w => w.SearchProfileMediaTypePush)
.FirstOrDefault();
Can you make these two changes and try again?
A few things will be an issue here. You have no keys defined or FKs for the relationship:
[Table("searchprofilepush")]
public class SearchProfilePush
{
[Key]
public int Id { get; set; }
public int AccountId { get; set; }
public bool Push { get; set; }
public int UserPushId { get; set; }
public UserPush UserPush { get; set; }
public int SearchProfileId { get; set; }
public SearchProfile SearchProfile { get; set; }
public ICollection<SearchProfileMediaTypePush> SearchProfileMediaTypePush { get; set; }
}
[Table("searchprofilemediatypepush")]
public class SearchProfileMediaTypePush
{
[Key]
public int Id { get; set; }
public MediaTypeType MediaType { get; set; }
public bool Push { get; set; }
public int SearchProfilePushId { get; set; }
[ForeignKey("SearchProfilePushId")]
public SearchProfilePush SearchProfilePush { get; set; }
}
Personally I prefer to explicitly map out the relationships using EntityTypeConfiguration classes, but alternatively they can be set up in the Context's OnModelCreating. As a starting point have a look at http://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx for basic EF relationship configuration.
for a SearchProfilePush configuration:
modelBuilder.Entity<SearchProfilePush>()
.HasMany(x => x.SearchProfileMediaTypePush)
.WithRequired(x => x.SearchProfilePush)
.HasForeignKey(x => x.SearchProfilePushId);

Why can't I get this model to map to this viewmodel?

I have this entity model for a recursively structured category tree:
public class ProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public ICollection<ProductInCategory> ProductInCategory { get; set; }
public ICollection<FrontPageProduct> FrontPageProduct { get; set; } // Nav.prop. to front page product
// Recursive sorting:
public void RecursiveOrder()
{
Children = Children.OrderBy(o => o.SortOrder).ToList();
Children.ToList().ForEach(r => r.RecursiveOrder());
}
}
... and this, supposedly, matching ViewModel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public bool Checked { get; set; } // Used for assigning a product to multiple categories in Product/Edit
// Nav.props:
public ViewModelProductCategory ParentCategory { get; set; } // Nav.prop. to parent
public ICollection<ViewModelProductCategory> Children { get; set; } // Nav.prop. to children
public IEnumerable<ViewModelProduct> Products { get; set; } // Products in this category
public IEnumerable<ViewModelFrontPageProduct> FrontPageProducts { get; set; }
public string ProductCountInfo { get { return Products?.Count().ToString() ?? "0"; } }
}
When I try to populate the viewmodel, like this:
List<ProductCategory> DbM =
await _context.ProductCategories
.Include(c => c.Children)
.Where(x => x.ParentId == null)
.OrderBy(o => o.SortOrder)
.ToListAsync();
foreach (var item in DbM)
{
VMSelectCategories.Add(
new ViewModelProductCategory{
Id = item.Id,
Children = item.Children,
Title = item.Title
});
}
VisualStudio screams at me that it can't implicitly convert ProductCategory to ViewModelCategory. This happens at Children = item.Children.
Why isn't it working? Can't I have additional properties in the viewmodel that I use unrelated to the original entity model? Like Checked and ProductCountInfo?
In this line:
Children = item.Children,
Children is the ViewModelProductCategory.Children property, which is type ICollection<ViewModelProductCategory>, while item.Children is the ProductCategory.Children property, which is type ICollection<ProductCategory>. They are different types and neither inherits or implements the other so why would you expect to be able to assign an object of one type to a property of the other type? Would you expect this to work:
var list1 = new List<int> {1, 2, 3};
List<string> list2 = list1;
Of course not (I hope) because assigning a List<int> object to a List<string> variable would be silly. What you're trying to do is exactly the same. You need to provide some way to convert from one type to the other and then implement that in your code. An option for that might be like this:
Children = item.Children.Select(pc => MapToViewModel(pc)).ToList(),
where MapToViewModel is a method that you write to create a ViewModelProductCategory and populate its properties from a ProductCategory parameter.
You might also look at using something like AutoMapper.

Linq to sql query with Parent child relationship

I have following model for posting the question and replies to the question
Model
public partial class LMS_Question
{
[Key]
public int ClassDiscussionID { get; set; }
public int? ParentClassDiscussionID { get; set; }
public string Discussion { get; set; }
public string DiscussionTitle { get; set; }
}
When user will post new question the 'ParentClassDiscussionID' will be null and if any reply added to the question then 'ClassDiscussionID' will be updated to 'ParentClassDiscussionID'.
Basically ClassDiscussionID and ParentClassDiscussionID columns have parent-child relationship.
However, I want to show the data like DiscussionTitle,ReplyCount as follows
DiscussionTitle ReplyCount
-------------------------
Question1 2 Reponses
Question2 3 Reponses
Question3 5 Reponses
So, how can I achieve this using linq to sql query ?
Thanks for the help !
First of all you need to change your model to something like this
public partial class LMS_ClassDiscussion
{
public LMS_ClassDiscussion()
{
LMS_ClassDiscussionchild = new List<LMS_ClassDiscussion>();
}
public int ClassDiscussionID { get; set; }
public int? ParentClassDiscussionID { get; set; }
public string Discussion { get; set; }
public string DiscussionTitle { get; set; }
public LMS_ClassDiscussion _LMS_ClassDiscussion { get; set; }
public List<LMS_ClassDiscussion> LMS_ClassDiscussionchild { get; set; }
}
then you need method to get all children of parents
public List<LMS_ClassDiscussion> GetChildren(IList<LMS_ClassDiscussion> source, int? parentId)
{
var children = source.Where(x => x.ParentClassDiscussionID == parentId).ToList();
//GetChildren is called recursively again for every child found
//and this process repeats until no childs are found for given node,
//in which case an empty list is returned
children.ForEach(x => x.LMS_ClassDiscussionchild = GetChildren(source, x._LMS_ClassDiscussion.ClassDiscussionID));
return children.ToList();
}
call method this way
var LMS_ClassDiscussions = GetChildren(query, null);
foreach (var item in LMS_ClassDiscussions)
{
Console.WriteLine(item._LMS_ClassDiscussion.ClassDiscussionID + "="+item.LMS_ClassDiscussionchild.Count);
}

C# Linq search inside object linked property

i'm trying to do some search inside some attributes of my object set but i'm getting some trouble on the right way to mount my linq query, i have my VT_Video class which has its attributes and some linked objects
public partial class VT_Video
{
public int ID { get; set; }
public string title { get; set; }
public string description { get; set; }
public virtual ICollection<VT_VideoTag> VT_VideoTag { get; set; }
}
public partial class VT_VideoTag
{
public int ID { get; set; }
public int tagID { get; set; }
public int videoID { get; set; }
public virtual VT_Tag VT_Tag { get; set; }
public virtual VT_Video VT_Video { get; set; }
}
public partial class VT_Tag
{
public int ID { get; set; }
public string name { get; set; }
public virtual ICollection<VT_VideoTag> VT_VideoTag { get; set; }
}
What i want to accomplish is search a user given word inside my Video collection by VT_Video.title, VT_Video.description and also by VT_Video.VT_VideoTag.VT_Tag.name, what i managed to do so far is only search the title and description:
var myVideos = db.VT_Video.Include("VT_VideoTag")
.Include("VT_VideoTag.VT_Tag")
.Where(vid =>
vid.descricao.Contains(strBusca) ||
vid.titulo.Contains(strBusca)).ToList();
Now, i know i can do what i want with some foreach and extra code but i wondered if it would be possible to do it using linq and also keep my code clean.
Thanks.
I have not worked with LINQ to SQL much, but it seems like .Any() would satisfy your requirement:
var myVideos = db.VT_Video.Include("VT_VideoTag")
.Include("VT_VideoTag.VT_Tag")
.Where(vid =>
vid.descricao.Contains(strBusca) ||
vid.titulo.Contains(strBusca) ||
vid.VT_VideoTag.Any(tag => tag.name.Contains(strBusca))).ToList();
Notice I added this clause:
vid.VT_VideoTag.Any(tag => tag.name.Contains(strBusca))
Which returns true if any tag in the collection has a name that contains your search string.

Fluent NHibernate automappings with self-reference

I have a simple class that looks like this...
public class Item {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentId { get; set; }
public virtual IList<Item> Children { get; private set; }
public Item() {
Children = new List<Item>();
}
}
... where the Id is the primary key and ParentId is the foreign key. When I run this code I get Invalid object name 'ItemToItem'. exception and I can't figure out what's wrong? I seems like NHibernate tries to select from a table called ItemToItem or something like that?
Correct way to self reference
// Class
public class Item
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Item Parent { get; private set; }
public virtual IList<Item> Children { get; set; }
public Item() {
Children = new List<Item>();
}
}
// Map
References(x => x.Parent).Column("ParentId");
HasMany(x => x.Children).Cascade.All().KeyColumn("ParentId");
// Add Item
session.Save(new Item { Description = "Electronics",
Children = {
new Item { Description = "PS2" },
new Item { Description = "XBox" }
}});
// Get Item
var items =
(from c in session.Linq<Item>()
where c.Parent == null
select c).ToList();
Yes. fluent nhibernate is seeing this as a many-many relationship. I dont know off the top of my head how to create the type of relationship you want. you'll probably at least want to set up a member:
public virtual Item Parent{ get; set; }

Categories

Resources