Override lazy loading on anonymous type with LINQ to entity - linq

I have three entities generated by Entity Framework. One is event and this contains navigation properties called frogs and user_bookings. I posted a related question before about performing a sub-query which seems to work but it prevents me overriding lazy loading of a property.
var evts = from evt in context.events.Include("frogs")
where evt.event_id < 10
select evt;
This works - the navigation property frogs gets loaded.
However, when I alter the LINQ to this:
var evts = from evt in context.events.Include("frogs")
where evt.event_id < 10
select new
{
Event = evt,
HasBooked = evt.user_bookings.Any(x => x.user_id == 1)
};
I get an error trying to access the frogs because the ObjectContext no longer exists. I tried removing virtual from the class definition for the event class, but this just results in an empty list of frogs, when they are definitely there!

This is by design. Include is ignored when the query result is a projection, even when the projection contains an entity that could contain the Included properties.
I don't know why EF implemented it this way. If the projection doesn't contain any entities, but is just some type (anonymous or not), there is no Include target, so ignoring it makes sense. But if the projection does contain an Include target (Event in your case) it seems to me that they could have decided to make that work. But, well, they didn't.
Maybe it's because the rules when Include actually has an effect are less obvious than you might expect. In your case, the shape of the query changes after the Include, so it's ignored.
You could work around this by also querying the frogs:
from evt in context.events.Include("frogs")
where evt.event_id < 10
select new
{
Event = evt,
Frogs = evt.frogs,
HasBooked = evt.user_bookings.Any(x => x.user_id == 1)
};
Now each Event will also have its frogs collection filled (because of relationship fixup). But there are two gotchas. The collections are not marked as loaded, so -
if lazy load can occur afterwards, it will occur, making the initial load useless.
if lazy loading can't occur any more (because the context is disposed) you will get an exception.
This means that to make this work you have to disable lazy loading.

Related

How do I loop through a collection of LINQ to Entities collection?

I have the following LINQ to Entities call to fill a collection variable:
var insuredFamily = db.Insureds.Where(x => x.ssn.Split('-')[0] == tmp[MemberId])
.OrderBy(x => x.fk_relation);
How would I go about looping through the items in the collection printing out both the item header and the item value for each row?
I'm new to LINQ to Entities, so all I'm trying to do is loop through a built collection and output the headers and rows to a log file so that I can quickly see what is being returned in the collection without resorting to stepping through each row in the debugger. Is this possible?
If a generic loop is not possible, is it possible to set the data source of a data grid to the collection variable and view the collection in a grid?
For starters, this can't be a LINQ-to_Entities statement. There's no way to translate x.ssn.Split('-')[0] == tmp[MemberId] into SQL. Unless this is EF-Core, that automatically switches to client-side evaluation when the expression contain not-supported part. You're not explicit about that.
Anyway, it's overkill to add logging just to see the content of a collection while debugging. You can use the immediate window in Visual Studio to output data when the code stops at a break point.
To make this really helpful, override the Insured class's ToString method. When I do this with just some Product class ...
public override string ToString()
{
return string.Format("{0}, {1}", this.Name, this.UnitPrice);
}
... and break in the debugger right after a line like ...
var prod = context.Products;
.. then in the immediate window I can type ...
?prod.ToList()
... which gives me:
Count = 6
[0]: {Nike Air, 150.00}
[1]: {Web cam, 23.00}
[2]: {Mouse, 7.00}
[3]: {Cool pack, 4.50}
[4]: {Keyboard, 47.30}
[5]: {Action cam, 73.00}
(and some SQL logging)
Note that this is invasive debugging. By calling ToList, the query is forced to execute. This may change the state in a way that it affects the code's behavior. The same would hold for logging though.
Thank you for all the advice, but it looks like the following two lines of code will do what I need. I just wanted to visualize the results of my LINQ to SQL queries.
var nationalities = db.Nationalities.OrderBy(x => x.pk_Nationality);
dgvResults.DataSource = nationalities;
This code will simply put the collection into the data source of my data grid view allowing me to see all the contents.

update a property value during linq to sql select (involves join)

Ok I have seen many questions that based on their text could be something like this but not quite. Say I have something like this
(from r in reports
join u in SECSqlClient.DataContext.GetTable<UserEntity>()
on r.StateUpdateReportUserID equals u.lngUserID
select r).
If reports have a bunch of say reportDTO class and I want to select from a list of that DTO but at the same time set one property to a property in userEntity how would I do that? Basically I want all other fields on the report maintained but set a user name from the user table. (There is a reason this is not done in one big query that gets a list of reports)
What I am looking for is something like Select r).Something(SOME LAMBDA TO SET ONE FIELD TO userEntity property).
There is a dirty way to do this, which is
var repQuery = from r in reports ... select new { r, u };
var reps = repQuery.Select(x => { x.r.Property1 = x.u.Property1; return x.r; };
However, When it comes to functional programming (which Linq is, arguably) I like to adhere to its principles, one of which to prevent side effects in functions. A side effect is a change in state outside the function body, in this case the property value.
On the other hand, this is a valid requirement, so I would either use the ForEach method after converting the query to list (ToList()). Foreach is expected to incur side effects. Or I would write a clearly named extension method on IEnumerable<T> (e.g. DoForAll) that does the same, but in a deferred way. See Why there is no ForEach extension method on IEnumerable?.

Changing values of an object in a LINQ-statement

I want to add some calculated properties to an EntityObject without loosing the possibility of querying it agains the database.
I created a partial class and added the fields I need in the object. Than I wrote a static function "AttachProperties" that should somehow add some calculated values. I cannot do this on clientside, since several other functions attach some filter-conditions to the query.
The functions should look like this:
return query.Select(o =>
{
o.HasCalculatedProperties = true;
o.Value = 2;
return o;
});
In my case the calculated value depends on several lookups and is not just a simple "2". This sample works with an IEnumerable but, of course, not with an IQueryable
I first created a new class with the EntityObject as property and added the other necessary fields but now I need this extended class to be of the same basetype.
First, in my opinion changing objects in a Select() is a bad idea, because it makes something else happen (state change) than the method name suggests (projection), which is always a recipe for trouble. Linq is rooted in a functional programming (stateless) paradigm, so this kind of usage is just not expected.
But you can extend your class with methods that return a calculation result, like:
partial class EntityObject
{
public int GetValue()
{
return this.MappedProp1 * this.MappedProp2;
}
}
It is a bit hard to tell from your question whether this will work for you. If generating a calculated value involves more than a simple calculation from an object's own properties it may be better to leave your entities alone and create a services that return calculation results from an object graph.
Try something like this:
return from o in collection
select new O()
{
OtherProperty = o.OtherProperty,
HasCalculatedProperties = true,
Value = 2
};
This will create a copy of the original object with the changes you require and avoid all the messiness that come with modifying an entity in a select clause.

NHibernate IQueryable doesn't seem to delay execution

I'm using NHibernate 3.2 and I have a repository method that looks like:
public IEnumerable<MyModel> GetActiveMyModel()
{
return from m in Session.Query<MyModel>()
where m.Active == true
select m;
}
Which works as expected. However, sometimes when I use this method I want to filter it further:
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.ID < 100
select new { m.Name };
Which produces the same SQL as the first one and the second filter and select must be done after the fact. I thought the whole point in LINQ is that it formed an expression tree that was unravelled when it's needed and therefore the correct SQL for the job could be created, saving my database requests.
If not, it means all of my repository methods have to return exactly what is needed and I can't make use of LINQ further down the chain without taking a penalty.
Have I got this wrong?
Updated
In response to the comment below: I omitted the line where I iterate over the results, which causes the initial SQL to be run (WHERE Active = 1) and the second filter (ID < 100) is obviously done in .NET.
Also, If I replace the second chunk of code with
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.Items.Count > 0
select new { m.Name };
It generates the initial SQL to retrieve the active records and then runs a separate SQL statement for each record to find out how many Items it has, rather than writing something like I'd expect:
SELECT Name
FROM MyModel m
WHERE Active = 1
AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0
You are returning IEnumerable<MyModel> from the method, which will cause in-memory evaluation from that point on, even if the underlying sequence is IQueryable<MyModel>.
If you want to allow code after GetActiveMyModel to add to the SQL query, return IQueryable<MyModel> instead.
You're running IEnumerable's extension method "Where" instead of IQueryable's. It will still evaluate lazily and give the same output, however it evaluates the IQueryable on entry and you're filtering the collection in memory instead of against the database.
When you later add an extra condition on another table (the count), it has to lazily fetch each and every one of the Items collections from the database since it has already evaluated the IQueryable before it knew about the condition.
(Yes, I would also like to be the extensive extension methods on IEnumerable to instead be virtual members, but, alas, they're not)

ADO.NET Data Services, LINQ

I have C# code to populate a dropdown list in Silverlight which works fine except when there are duplicates. I think because IEnumerable<Insurance.Claims> is a collection, it filters out duplicates. How would I code my LINQ query to accept duplicates?
My Sample Data looks like:
Code => CodeName
FGI Field General Initiative
SRI Static Resource Initiative
JFI Joint Field Initiative - This is "overwritten" in results
JFI Joint Friend Initiative
IEnumerable<Insurance.Claims> results;
// ADO.NET Data Service
var claim = (from c in DataEntities.Claims.Expand("Claimants").Expand("Policies")
where c.Claim_Number == claimNumber
select c);
DataServiceQuery<Insurance.Claims> dataServiceQuery =
claim as DataServiceQuery<Insurance.Claims>;
dataServiceQuery.BeginExecute((asyncResult) =>
{
results = dataServiceQuery.EndExecute(asyncResult);
if (results == null)
{
// Error
}
else
{
// Code to populate Silverlight form
}
});
(Not sure if you're still struggling with this but anyway...)
I'm pretty sure it's not the IEnumerable interface but the actual drop down that is causing this behaviour. The code is being used as the key, and so obviously each time the same code is encountered, the item is being overwritten.
I don't think you can override this unless you change the code, or use another identifier as the key field in the dropdown.
You may want to add a try-catch block around dataServiceQuery.EndExecute(asyncResult) to properly handle errors.

Resources