prefetching members of inherited entities via a LINQ-to-Entities query - linq

I think the simplest way I can ask this question is with an example: Suppose that I have an Entity Framework model with an "Order" entity that has an "OrderLines" collection. The "OrderLines" collection is ostensibly a collection of OrderLine objects, but I am using inheritance here, so the actual type of an object in the collection is going to be NoteOrderLine, ItemOrderLine, etc. Furthermore, the ItemOrderLine entity has an associated "Item" entity.
What I want to be able to do is created a LINQ query based on the "Order" entity, prefetching the "OrderLines" collection, as well as prefetching the "Item" entity in the case that the "OrderLine" entity is actually of type "ItemOrderLine". Has anyone figured this out?
Thanks much.

You can do it with projection:
var q = from o in Context.Orders
select new
{
Customer = o.CustomerName,
Lines = from l in o.Lines
let i = l as ItemOrderLine
select new
{
Quantity = l.Quantity,
Item = i.Item.Name,
ItemNo = (int?) i.Item.Number // Note below
}
};
i will be null when l is of type NoteOrderLine. Since int is non-nullable, we must cast it to int? so that the null i can be coalesced when setting ItemNo.
You can do this with entity types, too, but it's different. Since you give no example of the sort of code you're trying to write, I guessed.

Related

Entity Framework, Table Per Type and Linq - Getting the "Type"

I have an Abstract type called Product, and five "Types" that inherit from Product in a table per type hierarchy fashion as below:
I want to get all of the information for all of the Products, including a smattering of properties from the different objects that inherit from products to project them into a new class for use in an MVC web page. My linq query is below:
//Return the required products
var model = from p in Product.Products
where p.archive == false && ((Prod_ID == 0) || (p.ID == Prod_ID))
select new SearchViewModel
{
ID = p.ID,
lend_name = p.Lender.lend_name,
pDes_rate = p.pDes_rate,
pDes_details = p.pDes_details,
pDes_totTerm = p.pDes_totTerm,
pDes_APR = p.pDes_APR,
pDes_revDesc = p.pDes_revDesc,
pMax_desc = p.pMax_desc,
dDipNeeded = p.dDipNeeded,
dAppNeeded = p.dAppNeeded,
CalcFields = new DAL.SearchCalcFields
{
pDes_type = p.pDes_type,
pDes_rate = p.pDes_rate,
pTFi_fixedRate = p.pTFi_fixedRate
}
}
The problem I have is accessing the p.pTFi_fixedRate, this is not returned with the Products collection of entities as it is in the super type of Fixed. How do I return the "super" type of Products (Fixed) properties using Linq and the Entity Framework. I actually need to return some fields from all the different supertypes (Disc, Track, etc) for use in calculations. Should I return these as separate Linq queries checking the type of "Product" that is returned?
This is a really good question. I've had a look in the Julie Lerman book and scouted around the internet and I can't see an elegant answer.
If it were me I would create a data transfer object will all the properties of the types and then have a separate query for each type and then union them all up. I would insert blanks into the DTO properies where the properties aren't relevant to that type. Then I would hope that the EF engine makes a reasonable stab at creating decent SQL.
Example
var results = (from p in context.Products.OfType<Disc>
select new ProductDTO {basefield1 = p.val1, discField=p.val2, fixedField=""})
.Union(
from p in context.Products.OfType<Fixed>
select new ProductDTO {basefield1 = p.val1, discField="", fixedField=p.val2});
But that can't be the best answer can it. Is there any others?
So Fixed is inherited from Product? If so, you should probably be querying for Fixed instead, and the Product properties will be pulled into it.
If you are just doing calculations and getting some totals or something, you might want to look at using a stored procedure. It will amount to fewer database calls and allow for much faster execution.
Well it depends on your model, but usually you need to do something like:
var model = from p in Product.Products.Include("SomeNavProperty")
.... (rest of query)
Where SomeNavProperty is the entity type that loads pTFi_fixedRate.

Can LINQ ToArray return a strongly-typed array in this example?

I've contrived this example because it's an easily digested version of the actual problem I'm trying to solve. Here are the classes and their relationships.
First we have a Country class that contains a Dictionary of State objects indexed by a string (their name or abbreviation for example). The contents of the State class are irrelevant:
class Country
{
Dictionary<string, State> states;
}
class State { ... }
We also have a Company class which contains a Dictionary of zero or more BranchOffice objects also indexed by state names or abbreviations.
class Company
{
Dictionary<string, BranchOffice> branches;
}
class BranchOffice { ... }
The instances we're working with are one Country object and an array of Company objects:
Country usa;
Company companies[];
What I want is an array of the State objects which contain a branch. The LINQ I wrote is below. First it grabs all the companies which actually contain a branch, then joins to the list of states by comparing the keys of both lists.
The problem is that ToArray returns an anonymous type. I understand why anonymous types can't be cast to strong types. I'm trying to figure out whether I could change something to get back a strongly typed array. (And I'm open to suggestions about better ways to write the LINQ overall.)
I've tried casting to BranchOffice all over the place (up front, at list2, at the final select, and other less-likely candidates).
BranchOffice[] offices =
(from cm in companies
where cm.branches.Count > 0
select new {
list2 =
(from br in cm.branches
join st in usa.states on br.Key equals st.Key
select st.Value
)
}
).ToArray();
You can do:
select new MyClassOfSomeType {
..
)
For selection, you can give it a custom class type. You can also then use ToList. With ArrayList, if you need to keep it loosely typed, you can then make it strongly typed later using Cast<>, though only for any select result that doesn't generate an anonymous class.
HTH.
If i understand the problem correctly, the you want just the states that have office brances in them, not the branches too. If so, one posible linq is the following:
State[] offices =
(from cm in companies
where cm.branches.Count > 0
from br in cm.branches
join st in usa.states on br.Key equals st.Key
select st.Value
).Distinct().ToArray();
If you want both the states and the branches, then you will have to do a group by, and the result will be an IEnumerable>, which you can process after.
var statesAndBranches =
from cm in companies
where cm.branches.Count > 0
from br in cm.branches
join st in usa.states on br.Key equals st.Key
group br.Value by st.Value into g
select g;
Just one more thing, even though you have countries and branches declared as dictionaries, they are used as IEnumerable (from keyValuePair in dictionary) so you will not get any perf benefit form them.

linq with Include and criteria

How would I translate this into LINQ?
Say I have A parent table (Say, customers), and child (addresses).
I want to return all of the Parents who have addresses in California, and just the california address. (but I want to do it in LINQ and get an object graph of Entity objects)
Here's the old fashioned way:
SELECT c.blah, a.blah
FROM Customer c
INNER JOIN Address a on c.CustomerId = a.CustomerId
where a.State = 'CA'
The problem I'm having with LINQ is that i need an object graph of concrete Entity types (and it can't be lazy loaded.
Here's what I've tried so far:
Edit: added context instantiation as requested
// this one doesn't filter the addresses -- I get the right customers, but I get all of their addresses, and not just the CA address object.
var ctx = new CustomersContext() // dbContext -- using EF 4.1
from c in ctx.Customer.Include(c => c.Addresses)
where c.Addresses.Any(a => a.State == "CA")
select c
// this one seems to work, but the Addresses collection on Customers is always null
var ctx = new CustomersContext() // dbContext -- using EF 4.1
from c in ctx.Customer.Include(c => c.Addresses)
from a in c.Addresses
where a.State == "CA"
select c;
Any ideas?
Based on the code above, it appears that you already have a rehydrated collection of Customer objects in the Customer variable.
You will need to call the Include function when you fill your Customer collection above. This way, the framework will rehydrate the included objects at the time of retrieval from your data context.
The actual query code appears good though.

How do you query an object set and in that same query filter an attached entity collection?

I am using Entity Framework for the first time and noticed that the entities object returns entity collections.
DBEntities db = new DBEntities();
db.Users; //Users is an ObjectSet<User>
User user = db.Users.Where(x => x.Username == "test").First(); //Is this getting executed in the SQL or in memory?
user.Posts; //Posts is an EntityCollection<Post>
Post post = user.Posts.Where(x => x.PostID == "123").First(); //Is this getting executed in the SQL or in memory?
Do both ObjectSet and EntityCollection implement IQueryable? I am hoping they do so that I know the queries are getting executed at the data source and not in memory.
EDIT: So apparently EntityCollection does not while ObjectSet does. Does that mean I would be better off using this code?
DBEntities db = new DBEntities();
User user = db.Users.Where(x => x.Username == "test").First(); //Is this getting executed in the SQL or in memory?
Post post = db.Posts.Where(x => (x.PostID == "123")&&(x.Username == user.Username)).First(); // Querying the object set instead of the entity collection.
Also, what is the difference between ObjectSet and EntityCollection? Shouldn't they be the same?
Thanks in advance!
EDIT: Sorry, I'm new to this. I'm trying to understand. Attached EntityCollections are lazy loaded, so if I access them then memory is populated with them. Rather than doing two querys to the object sets like in my last edit, I am curious if this query would be more what I was after:
DBEntities db = new DBEntities();
User user = (from x in db.Users
from y in x.Posts
where x.Username == "test"
where y.PostID == 123
select x).First();
ObjectSet<T> does implement IQueryable<T>, but EntityCollection<T> does not.
The difference is that ObjectSet<T> is meant to be used for querying directly (which is why it does implement the interface). EntityCollection<T>, on the other hand, is used for the "many" end of a result set, typically returned in a query done on an ObjectSet<T>. As such, it impelments IEnumerable<T>, but not IQueryable<T> (as it's already the populated results of a query).
I was almost ready to say yes, they both do. Luckily I check the documentation first.
EntityCollection does not implement IQueryable.
As for the difference, ObjectSet<TEntity> represents the the objects generated from a table in a database. EntityCollection<TEntity> represents a collection of entity objects on the 'Many' side of One to Many or Many to Many relationship.

Entity Framework - LinQ projection problem

I want to create an Entity Object from a LinQ statement, but I don't want to load all its columns.
My ORDERS object has a lot of columns, but I just want to retrieve the REFERENCE and OPERATION columns so the SQL statement and result will be smaller.
This LinQ statement works properly and loads all my object attributes:
var orders = (from order in context.ORDERS
select order);
However the following statement fails to load only two properties of my object
var orders = (from order in context.ORDERS
select new ORDERS
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION
});
The error thrown is:
The entity or complex type
'ModelContextName.ORDERS' cannot be
constructed in a LINQ to Entities
query.
What is the problem? Isn't it possible to partially load an object this way?
Thank you in advance for your answers.
ANSWER
Ok I should thank you both Yakimych and Dean because I use both of your answers, and now I have:
var orders = (from order in context.ORDERS
select new
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION,
})
.AsEnumerable()
.Select(o =>
(ORDERS)new ORDERS
{
REFERENCE = o.REFERENCE,
OPERATION = o.OPERATION
}
).ToList().AsQueryable();
And I get exactly what I want, the SQL Statement is not perfect but it returns only the 2 columns I need (and another column which contains for every row "1" but I don't know why for the moment) –
I also tried to construct sub objects with this method and it works well.
No, you can't project onto a mapped object. You can use an anonymous type instead:
var orders = (from order in context.ORDERS
select new
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION
});
The problem with the above solution is that from the moment you call AsEnumerable(), the query will get executed on the database. In most of the cases, it will be fine. But if you work with some large database, fetching the whole table(or view) is probably not what you want. So, if we remove the AsEnumerable, we are back to square 1 with the following error:
The entity or complex type 'ModelContextName.ORDERS' cannot be constructed in a LINQ to Entities query.
I have been struggling with this problem for a whole day and here is what I found. I created an empty class inheriting from my entity class and performed the projection using this class.
public sealed class ProjectedORDERS : ORDERS {}
The projected query (using covariance feature):
IQueryable<ORDERS> orders = (from order in context.ORDERS
select new ProjectedORDERS
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION,
});
Voilà! You now have a projected query that will map to an entity and that will get executed only when you want to.
I think the issue is creating new entities within the query itself, so how about trying this:
context.ORDERS.ToList().Select(o => new ORDERS
{
REFERENCE = o.REFERENCE,
OPERATION = o.OPERATION
});

Resources