Regarding to the title, this could have also been written as : “Why isn’t my IQueryable updated with values from another object” or “IQueryable properties not updating”.
In short, lets say you’re working with data coming from two separate databases and you need to join the data into one Data Transfer Object for displaying purposes.
First off: you should create a view on SQL-level that joins these two tables and expose it in your EF model.
However there are some requirements for this :
- The databases must be within reach of eachother ( on the same machine, for instance)
- You need to have permissions to create the view
In my case, the latter could not be done, neither do I have sufficient permissions, nor am I allowed to update the DB-schema.
So I created two EF models (Model-First) :
=> MyApplicationDatabase.edmx
==> MyUser Table
==> MyTicket Table
=> MyCompanyDatabase.edmx
==> Employee Table
==> Department Table
==> Etc
In My Application I needed to display all the business information for the current logged in user.
So this meant going through the MyCompanyDatabase model and loading this additional information.
Eventually. my code looked something like this :
public DTO.MyTicket[] GetLatestTickets(int top = 10)
{
var topTickets = (from ticket in myApplicationDbContext.MyTickets
select new DTO.MyTicket{
TicketOwner = ticket.OwnerId,
Created = ticket.Created,
Modified = ticket.Modified,
Price = ticket.Price
}).Take(top);
//at this point in time, the topTickets array is not yet been executed
foreach(var topTicket in topTickets)
{
topTicket.PriceInEuro = topTicket.Price * 1.386;
}
return topTickets.ToArray();//Now the query gets executed and returns the ticket, without filling in the PriceInEuro
}
This is due to “deferred execution”, you can find numerous examples of this on the web.
In order to make sure the missing references and/or properties are filled in, you need to execute the IQueryable so that you have a reference to an actual collection of items.
This is easy, you just move the ToArray() to the first LINQ statement;
This will execute the db query and put the results in the topTickets object, you can then alter any item in the array and return the modified array from the method.
It’s a typical ‘gotcha’ when dealing with EF, I’ve thought my inner objects where blank due to JSON serialization on WCF, instead I made this classic mistake =)
Here’s the solution :
public DTO.MyTicket[] GetLatestTickets(int top = 10)
{
var topTickets = (from ticket in myApplicationDbContext.MyTickets
select new DTO.MyTicket{
TicketOwner = ticket.OwnerId,
Created = ticket.Created,
Modified = ticket.Modified,
Price = ticket.Price
}).Take(top).ToArray();
foreach(var topTicket in topTickets)
{
topTicket.PriceInEuro = topTicket.Price * 1.386;
}
return topTickets;
}
Cheers,
Maarten