LINQ to Objects question - linq

I am writing a method that is passed a List<AssetMovements> where AssetMovements looks something like
public class AssetMovements
{
public string Description { get; set; }
public List<DateRange> Movements { get; set; }
}
I want to be able to flatten out these objects into a list of all Movements regardless of Description and am trying to figure out the LINQ query I need to do this. I thought that
from l in list select l.Movements
would do it and return IEnumerable<DateRange> but instead it returns IEnumerable<List<DateRange>> and I'm not really sure how to correct this. Any suggestions?

This one's been asked before. You want the SelectMany() method, which flattens out a list of lists. So:
var movements = list.SelectMany(l => l.Movements);

Related

Net Core - .Include() results in loop causing Visual Studio debug to crash

My database has a one-to-many relation between "UsageRecord" and "Dimension"
This is modelled as follows (using a Database-First approach):
public partial class Dimension
{
...
public virtual ICollection<UsageRecord> UsageRecord { get; set; }
}
Usage Record class:
public partial class UsageRecord
{
public long Id { get; set; }
...
public long DimensionId { get; set; }
public virtual Dimension Dimension { get; set; }
}
So, if i query the list of UsageRecords (EagerLoading):
_context.Set<UsageRecord>.Where(x => x.ProductId == productId).ToList()
i get a list of UsageRecord objects I can navigate through during debug:
Please notice that the Dimension object is null, and this is correct since i haven't included it in the query.
Now, If i try to include it, the application crashes:
_context.Set<UsageRecord>.Where(x => x.ProductId == productId).Include(p => p.Dimension).ToList();
Postman exits with a 502 error, and the VS Debug first shows a list of question marks "?" before crashing.
I think this is due to the fact that by Including the Dimension object, this loops through the list of UsageRecords attached and then the Dimension again and again.
How can I avoid it?
In order to retrieve your result from LINQ query you can solve your issue in these ways:
Configure your serializer to ignore loops
Create a view model for your controller's action
Use anonymous type from Select result in your controller's action

Find all items whose collection property contains items in another list

I have an collection of object Foo with an ICollection property containing a list of People objects.
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Person> People { get; set; }
}
I have another list of Person.
ICollection<Person> OtherPeople
I need to find all objects Foo where People contains any Person from OtherPeople.
Is there a version of .Contains that accepts a collection?
Something like:
var result = from f in FooCollection
where f.People.Contains(otherPeople)
select f;
I am using this with Entity Framework if that matters.
Your referring to using C# Linq's Any method.
The Any method basically states if Any of the elements within that collection (Enumerable) satisfy a condition, in your case the condition is if another collection contains one of the elements.
ex.
public bool HasPeople(ICollection<Person> original, ICollection<Person> otherPeople)
{
return original.Any(p => otherPeople.Contains(p));
}
However the Any method returns a boolean to state if the collection has Any elements that satisfy a condition - this doesn't give us which elements.
Another method in Linq that's worth noting is Where giving us all the elements that satisfy a condition.
ex.
public IEnumerable<Person> GetPeople(ICollection<Person> original, ICollection<Person> otherPeople)
{
return original.Where(p => otherPeople.Contains(p));
}
I hope that gets you going in the right direction. Entity Framework shouldn't matter because they are Enumerable. Almost forgot to mention that the Linq methods are rather simple so theirs really no need for these to be in their own methods.
I've ended up implementing a helper method like this:
public static bool HasElement<T>(ICollection<T> original, ICollection<T> otherCollection)
{
return original.Any(otherCollection.Contains);
}
Hope it helps!

2 models in 1 view [duplicate]

This question already has answers here:
Closed 10 years ago.
I know my question is stupid, but I dont know solution of my problem and can understand similar questions on stackoverflow.
I doing simple blog.
And when I go to one post in this blog I must see text of post and comments for him. They there are in my datebase, but I dont know how display both.
Please help me
You can create a custom ViewModel for this particular View. Something like this:
public class BlogReaderViewModel
{
// various fields which exist on either the post or the comments
}
Then you'd bind to that ViewModel for the View. The Controller action would get the Models it needs and build an instance of the ViewModel to pass to the View.
Another option would be to use a Tuple. It's a generic class which acts as a strongly-typed container for multiple other types. So the View's Model would be something like this:
Tuple<Post, Comments>
From an overall design perspective, my biggest recommendation would be to consider how your Models relate to one another and find your "aggregate root." In the case of a blog post with comments, it sounds like the post should be the aggregate root. The Model itself should have the comments within it. Something like this:
public class BlogPost
{
public string Title { get; set; }
public string Body { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
The idea is that the aggregate root is the parent object and internally knows about its child objects. You shouldn't have to manually compose those hierarchies of objects every time you want to use them.
You have to create a ViewModel to represent this View or the data that this view need, for example:
public class OrderViewModel {
public int Id { get; set; }
public DateTime DateOrder { get; set; }
public decimal Total { get; set; }
public string CustomerName { get; set; }
public List<Item> Items { get; set; }
// other properties
}
And you shoul use this ViewModel to type your view, for sample (using razor):
#model Models.ViewModels.OrderViewModel
It depends on the relationship of the comments in the model. Usually comments should be a child collection of post. So in the view you should be able to render the comments with something like this (Razor):
#foreach (var comment in Model.Comments) {
// comments display goes here
}
Be sure when you pass the model to the view from the controller that you don't produce an inefficient query. Make sure that the query gets the comments with the blog, depending on how you are getting your model in the DB. If you are using EF that would be the "Include" directive, e.g.
.Include(p => p.Comment);
One option is to Create a composite model that represents both groups of data required to render the view, and pass the off each sub model to editor templates on the view itself.

List vs IEnumerable vs IQueryable when defining Navigation property

I want to create a new model object named Movie_Type in my ASP.NET MVC web application. What will be the differences if I define the navigation proprty of this class to be either List, ICollection or IQueryable as following?
public partial class Movie_Type
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Movie> Movies { get; set; }
}
OR
public partial class Movie_Type
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IQueryable<Movie> Movies { get; set; }
}
OR
public partial class Movie_Type
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Movie> Movies { get; set; }
}
Edit:-
#Tomas Petricek.
thanks for your reply. in my case i am using the database first approach and then i use DbContext template to map my tables, which automatically created ICollection for all the navigation properties, So my questions are:-
1. Does this mean that it is not always the best choice to use Icollection. And i should change the automatically generated classes to best fit my case.
2. Secondly i can manage to choose between lazy or Eager loading by defining .include such as
var courses = db.Courses.Include(c => c.Department);
Regardless of what i am using to define the navigation properties. So i can not understand ur point.
3. i did not ever find any examples or tutorials that use IQuerable to define the navigation properties ,, so what might be the reason?
BR
You cannot use a navigation property of type IQueryable<T>. You must use ICollection<T> or some collection type which implements ICollection<T> - like List<T>. (IQueryable<T> does not implement ICollection<T>.)
The navigation property is simply an object or a collection of objects in memory or it is null or the collection is empty.
It is never loaded from the database when you load the parent object which contains the navigation property from the database.
You either have to explicitely say that you want to load the navigation property together with the parent which is eager loading:
var movieTypes = context.Movie_Types.Include(m => m.Movies).ToList();
// no option to filter or sort the movies collection here.
// It will always load the full collection into memory
Or it will be loaded by lazy loading (which is enabled by default if your navigation property is virtual):
var movieTypes = context.Movie_Types.ToList();
foreach (var mt in movieTypes)
{
// one new database query as soon as you access properties of mt.Movies
foreach (var m in mt.Movies)
{
Console.WriteLine(m.Title);
}
}
The last option is explicit loading which comes closest to your intention I guess:
var movieTypes = context.Movie_Types.ToList();
foreach (var mt in movieTypes)
{
IQueryable<Movie> mq = context.Entry(mt).Collection(m => m.Movies).Query();
// You can use this IQueryable now to apply more filters
// to the collection or sorting, for example:
mq.Where(m => m.Title.StartWith("A")) // filter by title
.OrderBy(m => m.PublishDate) // sort by date
.Take(10) // take only the first ten of result
.Load(); // populate now the nav. property
// again this was a database query
foreach (var m in mt.Movies) // contains only the filtered movies now
{
Console.WriteLine(m.Title);
}
}
There are two possible ways of looking at things:
Is the result stored in memory as part of the object instance?
If you choose ICollection, the result will be stored in memory - this may not be a good idea if the data set is very large or if you don't always need to get the data. On the other hand, when you store the data in memory, you will be able to modify the data set from your program.
Can you refine the query that gets sent to the SQL server?
This means that you would be able to use LINQ over the returned property and the additional LINQ operators would be translated to SQL - if you don't choose this option, additional LINQ processing will run in memory.
If you want to store data in memory, then you can use ICollection. If you want to be able to refine the query, then you need to use IQueryable. Here is a summary table:
| | Refine query | Don't change query |
|-----------------|--------------|--------------------|
| In-memory | N/A | ICollection |
| Lazy execution | IQueryable | IEnumerable |
More of a standard is IEnumerable as it is the least common denominator.
Iqueryable can be returned if you want extra querying functionality to the caller without having 10 repository methods to handle varying querying scenarios.
A downside is ienumerable could 'count()' slowly but if the object implements ICollection then this interface is checked for this value first without having to enumerate all items.
Also be aware if you return iqueryable to an untrusted caller they can do some casting and method calls on the iqueryable and get access to the context, connection, connection string, run queries, etc
Also note nhibernate for example has a query object you can pass to a repository to specify options. With entity framework you need to return IQueryable to enhance querying criteria
The collection that entity framework actually creates for you if you use virtual navigation properties implements ICollection, but not IQueryable, so you cannot use IQueryable for your navigation properties, as Slauma says.
You are free to define your properties as IEnumerable, as ICollection extends IEnumerable, but if you do this then you will lose your ability to add new child items to these navigation properties.

Dynamic Linq Search Expression on Navigation Properties

We are building dynamic search expressions using the Dynamic Linq library. We have run into an issue with how to construct a lamba expression using the dynamic linq library for navigation properties that have a one to many relationship.
We have the following that we are using with a contains statement-
Person.Names.Select(FamilyName).FirstOrDefault()
It works but there are two problems.
It of course only selects the FirstOrDefault() name. We want it to use all the names for each person.
If there are no names for a person the Select throws an exception.
It is not that difficult with a regular query because we can do two from statements, but the lambda expression is more challenging.
Any recommendations would be appreciated.
EDIT-
Additional code information...a non dynamic linq expression would look something like this.
var results = persons.Where(p => p.Names.Select(n => n.FamilyName).FirstOrDefault().Contains("Smith")).ToList();
and the class looks like the following-
public class Person
{
public bool IsActive { get; set;}
public virtual ICollection<Name> Names {get; set;}
}
public class Name
{
public string GivenName { get; set; }
public string FamilyName { get; set; }
public virtual Person Person { get; set;}
}
We hashed it out and made it, but it was quite challenging. Below are the various methods on how we progressed to the final result. Now we just have to rethink how our SearchExpression class is built...but that is another story.
1. Equivalent Query Syntax
var results = from person in persons
from name in person.names
where name.FamilyName.Contains("Smith")
select person;
2. Equivalent Lambda Syntax
var results = persons.SelectMany(person => person.Names)
.Where(name => name.FamilyName.Contains("Smith"))
.Select(personName => personName.Person);
3. Equivalent Lambda Syntax with Dynamic Linq
var results = persons.AsQueryable().SelectMany("Names")
.Where("FamilyName.Contains(#0)", "Smith")
.Select("Person");
Notes - You will have to add a Contains method to the Dynamic Linq library.
EDIT - Alternatively use just a select...much more simple...but it require the Contains method addition as noted above.
var results = persons.AsQueryable().Where("Names.Select(FamilyName)
.Contains(#0", "Smith)
We originally tried this, but ran into the dreaded 'No applicable aggregate method Contains exists.' error. I a round about way we resolved the problem when trying to get the SelectMany working...therefore just went back to the Select method.

Resources