error in fetching first element from collection - c#

I'm trying to fetch first element from the collection like this
List<Entity> data = session.Query<Entity>()
.Fetch(x => x.Photos.First())
.ToList();
and I'm getting this error.
A fetch request must be a simple member access expression; '[100002]' is a SubQueryExpression instead. Parameter name: relatedObjectSelector.
Now I'm using .Fetch(x => x.Photos.First()) cause I know that first element will always be populated, and it is. I do need just first element from the collection to reduce loading time, so this is exact solution I need, but I'm getting this error.

Fetch will fetch the entire collection, you can't tell it to just fetch the first element using "fetch", however you can probably get the desired effect using projections, or something like:
var subQ = QueryOver.Of<Entity>()
.SelectList(x => x.SelectMin(y => y.Photos)); // sub query selecting min photo id from entity
List<Photo> data = session.QueryOver<Photo>()
.Fetch(x => x.Entity).Eager // eager load the entity
.WithSubquery.WhereProperty(x => x.Id).In(subQ) // restrict the photo to those that are the first ones for the entity
.List() // execute query from database
.ToList(); // convert to List
Although I'm not sure why you'd want a List instead of IList.. generally it's preferable to use the interface

List<Entity> data = session.Query<Entity>()
.Fetch(x=>x.Photos.FirstOrDefualt())//here You need get only first element
.ToList();

Related

Group by Linq setting properties

I'm working on a groupby query using Linq, but I want to set the value for a new property in combination with another list. This is my code:
var result = list1.GroupBy(f => f.Name)
.ToList()
.Select(b => new Obj
{
ClientName = b.Name,
Status = (AnotherClass.List().Where(a=>a.state_id=b.????).First()).Status
})
I know I'm using a group by, but I'm not sure of how to access the value inside my bcollection to compare it with a.state_id.
This snippet:
Status = (AnotherClass.List().Where(a=>a.state_id=b.????).First()).Status
I've done that before but months ago I don't remember the syntax, when I put a dot behind b I have acces only to Key and the Linq Methods... What should be the syntax?`
Issue in your code is happening here:
a=>a.state_id=b.????
Why ?
Check type of b here, it would be IGrouping<TKey,TValue>, which is because, post GroupBy on an IEnumerable, you get result as IEnumerable<IGrouping<TKey,TValue>>
What does that mean?
Think of Grouping operation in the database, where when you GroupBy on a given Key, then remaining columns that are selected need an aggregation operation,since there could be more than one record per key and that needs to be represented
How it is represented in your code
Let's assume list1 has Type T objects
You grouped the data by Name property, which is part of Type T
There's no data projection so for a given key, it will aggregate the remaining data as IEnumerable<T>, as grouped values
Result is in the format IEnumerable<IGrouping<TK, TV>>, where TK is Name and TV represent IEnumerable<T>
Let's check out some code, break your original code in following parts
var result = list1.GroupBy(f => f.Name) - result will be of type IEnumerable<IGrouping<string,T>>, where list1 is IEnumerable<T>
On doing result.Select(b => ...), b is of type IGrouping<string,T>
Further you can run Linq queries on b, as follows:
b.Key, will give access to Name Key, there's no b.Value, for that your options could be following or any other relevant Linq operations:
a=>b.Any(x => a.state_id == x.state_id) or // Suuggests if an Id match in the Collection
a=>a.state_id == b.FirstOrDefault(x => x.state_id) //Selects First or default Value
Thus you can create a final result, from the IGrouping<string,T>, as per the logical requirement / use case

How to use First Method in querying data with Entity Datasource?

I have the following code:
var orders = context.Orders
.Include("Clients")
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault());
I want to get only the last order made by each client. The basis for the code I got from here Remove duplicates in the list using linq, from Freddy's answer. (I'm including "Clients" because Orders has ClientId but not client name and the results are to be displayed in a grid including the client name which I'm getting from Clients).
This works properly.
MY QUESTION:
Is it possible to do this using an asp.net Entity Datasource control?
Is it possible to use FirstOrDefault in some way in the asp.net Entity Datasource control?
If you move the Include to the end of the query ...
var orders = context.Orders
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault())
.Include("Clients");
... you'll get Orders with their clients included.
With the original Include the query shape changes after the Include was applied. This always makes Include ineffective. You can move it to the end of the query, because even after the grouping, the query still return Orders, so the Include is applicable.
Note however that this is a tremendous overkill. Entire Client records are queried from the database, entire Client objects are materialized and in the end you only display their names. It's much better to project the required data to a DTO that exactly contains the data you want to display. For example:
var orders = context.Orders
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault())
.Select(o => new OrderDto
{
o.OrderNumber,
o. ... // More Order properties
Client = o.Clients.Name
});
The DTO should be a class containing these properties.
I don't know the Entity Datasource control. From what I see in the MSDN documentation it seems too restricted to even shape the query sufficiently to get the last orders of each client. And it expects an entity set, no DTOs.
Instead of calling OrderbyDescending try using the max operated as explained here
I found here that with the EntityDataSource you can use:
Select="top(1) it.[OrderDate]"
However if you want to Order by DESC the top will be executed before the Order by DESC.
If you want the Order by executed before the top, in other words to get the last Item, instead of top do this in Code behind:
protected void entityDataSource_Selecting(object sender,EntityDataSourceSelectingEventArgs e)
{
e.SelectArguments.MaximumRows = 1;
}
All of this I got from that link in the Qustion and Answer.
I found that I can use the EntityDataSource's QueryCreated event as demonstrated in Filter with EntityDatasource in ASP.NET webforms in the question and answers.
In the case of this question I wrote
protected void EntityDataSource1_QueryCreated(object sender, QueryCreatedEventArgs e)
{
var ordersQuery = e.Query.OfType<Orders>();
e.Query = ordersQuery.Include("Clients")
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault());
}

Lazy Loading, Collections and Single or First

I have the following model:
A User has a collection of Photos. In the Photo model, there is a property called IsProfilePhoto.
When I do the following, the results are not as expected.
var user = dbContext.Users.SingleOrDefault(u => u.Id == 1);
var profilePhoto = user.Photos.SingleOrDefault(p => p.IsProfilePhoto);
With lazy loading on, this performs two queries.
The first one gets the user by id as expected.
The second one however, gets the collection of photos by user id and then in memory does the match on IsProfilePhoto.
I was hoping that with lazy loading on it would add the SingleOrDefault to the query as well.
Is this just not possible and I must always do the inverse? E.g.
var profilePhoto = dbContext.Photos.SingleOrDefault(p => p.UserId == 1 && p.IsProfilePhoto);
var user = profilePhoto.User;
I get the reasoning, there are just certain reasons why it's more convenient to go from the User to get the profile photo.
You can get the result with a single database query by using a projection:
var userWithProfilePhoto = dbContext.Users
.Where(u => u.Id == 1)
.Select(u => new
{
User = u,
ProfilePhoto = u.Photos.Where(p => p.IsProfilePhoto).FirstOrDefault()
})
.SingleOrDefault();
userWithProfilePhoto.User and userWithProfilePhoto.ProfilePhoto are the two entities you are looking for.
You have to use Eagerly loading to load multiple levels. Lazy load, loads the level when you access this.
var user = dbContext.Users.Include(u => u.Photos).SingleOrDefault(u => u.Id == 1);
var profilePhoto = user.Photos.SingleOrDefault(p => p.IsProfilePhoto);
This is the subtle difference in LINQ methods.
You can do the filtering as part of the query as:
var profilePhoto = user.Photos.Where(p => p.IsProfilePhoto).SingleOrDefault();
Due to this behavior in LINQ to Entities, I try to always use the Where method for the condition and the parameterless overloads for First, Single, FirstOrDefault, SingleOrDefault, Any, and Count.
Edit:
My bad, MSDN mentions it directly, but any reference to a navigation property (when lazy loading is enabled) loads all of the related records.
My best suggestion then is to a) accept extra database access, or b) query as you did in your alternative example, with the first table being the 'many' of the 'one-to-many' relationship.
First, and FirstOrDefault are lazy loaded, Single, and SingleOrDefault eagerly loaded. If you don't need a an exception thrown in case of several items returned by the query, you can change it to FirstOrDefault.

Unknown Select(?) of System Data Entity DbSet

Is something like this possible? I am getting the below error.
db.SomeTable.Add(new SomeTable()
{
GuidId = Guid.NewGuid(),
Name = db.AnotherTable.Select(x => x.Name.Where(x.Id == localVariable.Id))
}
);
db.SaveChanges();
Unknown Select(?) of System Data Entity DbSet
Select returns an IEnumerable, not an individual record. You would needed to add a .First() call to grab just one record. Entity Framework thinks you're trying to put a list into a single field.
Furthermore, your use of Where() is incorrect. Where also returns an IEnumerable, and can only be applied on to an IEnumerable. Think of it as a way to filter a list.
Here's how to do what I think you're asking for:
Name = db.AnotherTable.First(x => x.id == someId).Name
I think what you want is this:
Name = db.AnotherTable
.First(x => x.Id == localVariable.Id)
.Name;
The steps of this are:
Go into list of items in AnotherTable
Find the first item where the Id of the item is equal to localVariable.Id
Set your variable equal to the Name property of the item you found
You can also use FirstOrDefault(), Single(), and SingleOrDefault().

Foreign key object not getting filled in EF4

I would think this is very basic stuff, but I'm just not getting it to work.
I try to get a list of objects using lambda expressions like this :
List<LocalizationGlobalText> list = _entities.LocalizationGlobalTexts.Where(l => l.Language.Id == _currentlanguage).ToList<LocalizationGlobalText>();
The list is fetched, but the foreign key objects are all null.
I also tried using LINQ to entities but this results in the same problem :
IEnumerable<LocalizationGlobalText> bla = (from lgt in _entities.LocalizationGlobalTexts
join lg in _entities.LocalizationGlobals on lgt.IdLocalizationGlobal equals lg.Id
where lgt.IdLanguage == _currentlanguage
select lgt);
By default, Entity Framework only brings in the collection that you specify, without any foreign objects. If you have lazy loading enabled, accessing the foreign properties will cause them to be lazily initialized. If not, you'll need to tell entity framework to eagerly load the properties you want with the first batch.
There are two ways to do this. The first is the "official" way, but I don't like it because it uses magic strings:
var list = _entities.LocalizationGlobalTexts.Include("ForeignProp")
.Where(l => l.Language.Id == _currentlanguage)
.ToList<LocalizationGlobalText>();
(Replace "ForeignProp" with the name of the property you want it to eagerly load)
The second way is to set up your selector so that it will be forced to pull this extra data in:
var list = _entities.LocalizationGlobalTexts
.Where(l => l.Language.Id == _currentlanguage)
.Select(l => new {l, l.ForeignProp})
.ToList();
foreach(var item in list)
{
Console.WriteLine(item.l.Name + item.ForeignProp.Title);
}
Since Entity Framework is smart enough to have made the appropriate connections, you could throw on one more selector and avoid using the anonymous type afterward:
var list = _entities.LocalizationGlobalTexts
.Where(l => l.Language.Id == _currentlanguage)
.Select(l => new {l, l.ForeignProp})
.AsEnumerable() // tells EF to load now. The rest is LINQ to Objects
.Select(i => i.l)
.ToList();
foreach(var localization in list)
{
Console.WriteLine(localization.Name + localization.ForeignProp.Title);
}

Categories

Resources