Consider two entities Person which has a one-to-many collection Vehicles
public class Person
{
public IList<Vehicle> Vehicles { get; set;}
}
public class Vehicle
{
public string Name { get; set;}
public Person Owner { get; set; }
}
I display a grid of Persons having vehicle and show the name of the first vehicle in the grid. The grid is paginated. I use the following criteria to fetch the data
I have a criteria for loading data for a grid view as
var criteria = DetachedCriteria.For<Person>()
.CreateAlias("Vehicles","vehicle", JoinType.InnerJoin)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.SetMaxResults(pageSize)
.SetFirstResult((page - 1) * pageSize)
criteria.Add(Restrictions.Eq("vehicle.Name", "super"));
where page and pageSize are calculated bits.
The problem is since max results and first results are calculated in the database and distinct root is done outside, the number of rows do not match.
Is there a way to resolve this issue ?
This kind of queries should always use subquery instead of any type of JOIN. That also means, that the colleciton item has reference to parent (as in our case).
So, here we create the inner select for Vehicle:
var vehicles = DetachedCriteria.For<Vehicle>();
// add any amount or kind of WHERE parts
vehicles.Add(Restrictions.Eq("vehicle.Name", "super"))
// and essential SELECT Person ID
vehicles.SetProjection( Projections.Property("Owner.ID"));
Now, we can adjust the above query, to work only on a root/parent level:
var criteria = DetachedCriteria.For<Person>()
// instead of this
// .CreateAlias("Vehicles","vehicle", JoinType.InnerJoin)
// we will use subquery
.Add(Subqueries.PropertyIn("ID", vehicles));
// Wrong to use this approach at all
//.SetResultTransformer(new DistinctRootEntityResultTransformer())
.SetMaxResults(pageSize)
.SetFirstResult((page - 1) * pageSize)
That will create SELECT like this:
SELECT p....
FROM Person AS p
WHERE p.ID IN (
SELECT v.OwnerId
FROM Vehcile AS v
WHERE v.Name = 'super' ...
)
See also:
Query on HasMany reference
In NHibernate, using a Disjunction gives double results
And how to fetch the collection of Vehicles (until now just used for filtering)? The best (if not only) way is to use 1+1 SELECT statements. The easy and built-in solution is batch-size setting. Just mark the collection of Vehicles with this setting (e.g. batch-size="25") and with few more SELECT statements all data will be effectively loaded. See:
19.1.5. Using batch fetching
How to Eager Load Associations without duplication in NHibernate?
Related
My class structure is as follows. I'm trying to include the User field for the Order result. But I don't want to get the Orders property of the User class.
public class Order{
public int OrderId { get; set; }
public virtual User User { get; set; }
}
public class User{
public int UserId {get; set;}
public string Name {get; set;}
public string SurName {get; set;}
public virtual ICollection<Order> Orders { get; set; }
}
I wrote this code.
var orders = context.Set<Order>()
.Include(t => new { Name = t.User.Name, Surname = t.User.SurName })
.ToList();
But I get an error that
The expression 'new <>f__AnonymousType20`2(Name = (t As Order).User.Name, Surname = (t As Order).User.SurName)' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393."
I'm trying to include the User field for the Order result.
So you want to query a sequence of Orders, every Order with several properties of the User of this Order.
var orders = dbContext.Orders
.Where(order => ...) // if you don't want all Orders
.Select(order => new
{
// Select only the Order properties that you plan to use
Id = order.Id,
Total = order.Total,
...
// The User of this Order
User = new
{
// Select only the User properties that you plan to use
Id = order.User.Id,
Name = order.User.Name,
...
},
});
I use anonymous type here. If you need to return the fetched data from a procedure you need to put the data in a predefined class.
Some versions of entity framework won't support using the virtual properties. In that case you'll have to do the (Group-)Join yourself. If you start at the "one" side in a one-to-many relation, do a GroupJoin; if you start at the "many" side, a standard join will suffice. Use the overload that has a parameter resultSelector to precisely request what you want.
var orders = dbContext.Orders
.Where(order => ...)
.Join(dbContext.Users,
order => order.UserId, // from every Order take the foreign key to the user
user => user.UserId, // from every User take the primary key
// Parameter resultSelector: get every Order with its one and only user
// to make one new
(order, userOfThisOrder) => new
{
Id = order.Id,
TotalPrice = order.TotalPrice,
User = new
{
Id = userOfThisOrder.UserId,
Name = userOfThisOrder.Name,
...
},
});
Why not use include?
Include will fetch the complete row of the table, inclusive the properties that you won't use, or properties of which you already know the value.
Suppose you have a School database with Schools and Students, and a one-to-many relation with a foreign key: every School has zero or more Students, every Student attends exactly one School, namely the School that the foreign key SchoolId refers to.
Now if you fetch School [10], and use Include to fetch all its 2000 Students, then you'll fetch the foreign key SchoolId once for every fetched Student. You already know that this foreign key will have a value 10. You will be transferring this value over 2000 times. What a waste of processing power.
Another reason not to fetch complete rows, and not use Include is the following. The DbContext holds a ChangeTracker. When you fetch a complete row, the data is stored in the ChangeTracker, together with a Clone. You get the reference to the Original. Whenever you change values of properties of the retrieved data, you change the values in the Original. If you update the changed data using SaveChanges, then the original is compared by value with the clone in the ChangeTracker. Only the changed properties will be sent to the database.
So if you fetch School [10] with all its 2000 Students, and you don't plan to update the fetched data, then fetching the complete rows will fetch one School and 2000 Students. Every fetched item will be copied. And if you later fetch Student [42] to change his Address, and call SaveChanges, all fetched 2000 Students will be compared with their Clones, value by value. What a waste of processing power, if you didn't plan to update any of these 2000 Students.
When using entity framework always use Select and select only the properties that you actually plan to use. Only fetch complete rows, only use Include, if you plan to update the fetched data.
I have a query below which is supposed to group the result by Id, EntityName, DocType, Jurisdiction. For each group the query also returns the ProductList items.
At the moment if the group contains one or more than one product, Then i can see the result giving out a group with a combination of Id,EntityName,DocType,Jurisdiction and ProductList, However if the result doesnt contain products for a particular group i do not see the group at all. What i would like to do is show the groups even if does not have any products in its group. So if the count of ProductList is zero, i would like to set
ProductList= new List NettingAgreementProductDto. Any input would be highly appreciated.
var result = from nae in nettingAgreementEntities.Result
join no in nettingOpinions.Result
on nae.EntityId equals no.EntityId
join np in nettingProducts.Result
on no.ProductId equals np.Id
group np by new
{ nae.EntityId,
nae.EntityName,
nae.DocType,
nae.Jurisdiction
} into g
select new NettingAgreementEntityDto
{
Id = g.Key.EntityId,
EntityName = g.Key.EntityName,
DocType = g.Key.DocType,
Jurisdiction = g.Key.Jurisdiction,
ProductList = g.Select(x => new
NettingAgreementProductDto
{
Id = x.Id,
Name = x.Name
}).ToList()
};
To recap from the comments, currently your query is using Inner Join for associating NettingAgreementEntity with NettingAgreementProducts. This not only multiplies the result set (and thus requires you to use GroupBy after), but also filters out the NettingAgreementEntity without NettingAgreementProducts.
You can achieve the goal by switching to Group Join (or Left Outer Join + GroupBy).
But why entering all these complications. EF navigation properties allow you to almost forget about manual joins, and also allow you to easily see the multiplicity, thus whether you need to group the result or not.
So what I would suggest is to add the currently missing collection navigation property to your NettingAgreementEntity class:
public class NettingAgreementEntity
{
// ...
public virtual ICollection<NettingOpinion> Opinions { get; set; }
}
Optionally do the same for NettingAgreementProduct in case in the future you need something similar for products (it's a many-to-many relationship and should be able to be queried from both sides).
Also I would rename the NettingOpinion class navigation properties NettingAgreementProductNavigation and NettingAgreementEntityNavigation to something shorter, for instance Product and Entity. These names (as well as the names of the collection navigation properties) do not affect the database schema, but IMHO provide better readability.
Once you have that, you'll see that the desired LINQ query is a matter of simple Selects which convert entity class to DTO and let EF query translator produce the necessary joins for you:
var result = db.Set<NettingAgreementEntity>()
.Selec(nae => new NettingAgreementEntityDto
{
Id = nae.EntityId,
EntityName = nae.EntityName,
DocType = nae.DocType,
Jurisdiction = nae.Jurisdiction,
ProductList = nae.Opinions
.Select(no => new NettingAgreementProductDto
{
no.Product.Id,
no.Product.Name,
}).ToList(),
});
I have a simple question but didn't find an answers.
If I do so
var result = _db.Table.Include(t => t.Child).Where(t => t.Id == id).Single();
when join is calling?
After it found my entity or it includes every child during SQL looking for the row?
Lets see at the example based on simple db model:
public class Head
{
//... columns
public virtual Child {get; set;}
public Guid? ChildId {get; set;}
}
void main()
{
//The first version of code
var child = _db.Head.Include(h => h.Child)
.FirstOrDefault(//boring staff but we don't need child here)
?.Child;
if (child != null)
foo(child);
//The second one
var head = _db.Head.FirstOrDefault(//boring staff);
if (head != null && head.ChildId.HasValue)
foo(head.Child); // I know here we make a new request to our db
}
Which of two options are more productive?
I'm worry about "extra childs loading by SQL" when I need only one object based on filters query of parent table.
Thanks in advance!
It will evaluate the where condition first. Not in C# but in SQL which gets generated.
This will generate a SQL something like
SELECT top 1 .... FROM Table t
JOIN Child c ....
WHERE t.Id = id
Your database server will create a execution plan which will look for the item in the index and get corresponding child.
Without Include the loading of Child objects is deferred until you need them. Hence, if you were to iterate parent/child groups like this
foreach (var parent in _db.Table.Include(t => t.Child).Where(p => p.Name.StartsWith("Q")))
foreach (var child in parent.Child)
Console.WriteLine($"{child}, child of {parent}");
the number of round-trips would be equal to the number of parents plus one.
If you use Include, all Child objects are loaded along with the parent object, without making a separate round-trip for each parent. Hence, the number of database round-trips for the above code would be equal to 1.
In a case with Single, which could be rewritten as follows
var result = _db.Table.Include(t => t.Child).Single(t => t.Id == id);
the number of round-trips would be 1 with Include and 2 without Include.
EDIT: forgot to say I'm using Fluent NHibernate, even though the tag could hint about it anyway.
I have these entity classes:
class OuterLevel
{
ICollection<MidLevel> mid_items;
... other properties
}
class MidLevel
{
OuterLevel parent;
Inner1 inner1;
Inner2 inner2;
... other properties
}
class Inner1
{
int id;
string description;
}
class Inner2
{
int id;
string description;
}
I need to build a Linq query that returns a list of OuterLevel objects with all children populated properly.
Supposing all mappings are correct and working, the hard part I'm finding here is that the resulting query should be something like
SELECT * FROM OuterLevelTable OLT INNER JOIN MidLevelTable MLT ON (MLT.parentID = OLT.ID) INNER JOIN
Inner1Table ON (MLT.Inner1ID = Inner1Table.ID) INNER JOIN
Inner2Table ON (MLT.Inner2ID = Inner2Table.ID)
WHERE (Inner1Table.someproperty1 = somevalue1) AND (Inner2Table.someproperty2 = somevalue2)
The main problem is that two joins start from MidLevel object downward the hierarchy, so I cannot figure out which Fetch and FetchMany combination can be used without having the resulting query join two times the MidLevelTable, such as the following does:
return All().FetchMany(x => x.mid_items).ThenFetch(x => x.inner1).FetchMany(x => x.mid_items).ThenFetch(x => x.inner2);
I would like to return a IQueryable that can be further filtered, so I would prefer avoiding Query and QueryOver.
Thanks in advance,
Mario
what you want is not possible because when you filter on the joined tables the resulting records are not enough to populate the collections anyway. You better construct the query in one place to further tune it or set the collection batch size to get down SELECT N+1.
I have 5 tables in a L2S Classes dbml : Global >> Categories >> ItemType >> Item >> ItemData. For the below example I have only gone as far as itemtype.
//cdc is my datacontext
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Global>(p => p.Category);
options.AssociateWith<Global>(p => p.Category.OrderBy(o => o.SortOrder));
options.LoadWith<Category>(p => p.ItemTypes);
options.AssociateWith<Category>(p => p.ItemTypes.OrderBy(o => o.SortOrder));
cdc.LoadOptions = options;
TraceTextWriter traceWriter = new TraceTextWriter();
cdc.Log = traceWriter;
var query =
from g in cdc.Global
where g.active == true && g.globalid == 41
select g;
var globalList = query.ToList();
// In this case I have hardcoded an id while I figure this out
// but intend on trying to figure out a way to include something like globalid in (#,#,#)
foreach (var g in globalList)
{
// I only have one result set, but if I had multiple globals this would run however many times and execute multiple queries like it does farther down in the hierarchy
List<Category> categoryList = g.category.ToList<Category>();
// Doing some processing that sticks parent record into a hierarchical collection
var categories = (from comp in categoryList
where comp.Type == i
select comp).ToList<Category>();
foreach (var c in categories)
{
// Doing some processing that stick child records into a hierarchical collection
// Here is where multiple queries are run for each type collection in the category
// I want to somehow run this above the loop once where I can get all the Items for the categories
// And just do a filter
List<ItemType> typeList = c.ItemTypes.ToList<ItemType>();
var itemTypes = (from cat in TypeList
where cat.itemLevel == 2
select cat).ToList<ItemType>();
foreach (var t in itemTypes)
{
// Doing some processing that stick child records into a hierarchical collection
}
}
}
"List typeList = c.ItemTypes.ToList();"
This line gets executed numerous times in the foreach, and a query is executed to fetch the results, and I understand why to an extent, but I thought it would eager load on Loadwith as an option, as in fetch everything with one query.
So basically I would have expected L2S behind the scenes to fetch the "global" records in one query, take any primary key values, get the "category" children using one one query. Take those results and stick them into collections linked to the global. Then take all the category keys and excute one query to fetch the itemtype children and link those into their associated collections. Something on the order of (Select * from ItemTypes Where CategoryID in ( select categoryID from Categories where GlobalID in ( #,#,# ))
I would like to know how to properly eager load associated children with minimal queries and possibly how to accomplish my routine generically not knowing how far down I need to build the hierarchy, but given a parent entity, grab all the associated child collections and then do what I need to do.
Linq to SQL has some limitations with respect to eager loading.
So Eager Load in Linq To SQL is only
eager loading for one level at a time.
As it is for lazy loading, with Load
Options we will still issue one query
per row (or object) at the root level
and this is something we really want
to avoid to spare the database. Which
is kind of the point with eager
loading, to spare the database. The
way LINQ to SQL issues queries for the
hierarchy will decrease the
performance by log(n) where n is the
number of root objects. Calling ToList
won't change the behavior but it will
control when in time all the queries
will be issued to the database.
For details see:
http://www.cnblogs.com/cw_volcano/archive/2012/07/31/2616729.html
I am sure this could be done better, but I got my code working with minimal queries. One per level. This is obviously not really eager loading using L2S, but if someone knows the right way I would like to know for future reference.
var query =
from g in cdc.Global
where g.active == true && g.globalId == 41
select g;
var globalList = query.ToList();
List<Category> categoryList = g.category.ToList<Category>();
var categoryIds = from c in cdc.Category
where c.globalId == g.globalId
select c.categoryId;
var types = from t in cdc.ItemTypes
where categoryIds.Any(i => i == t.categoryId)
select t;
List<ItemType> TypeList = types.ToList<ItemType>();
var items = from i in cdc.Items
from d in cdc.ItemData
where i.ItemId == d.ItemId && d.labelId == 1
where types.Any(i => i == r.ItemTypes)
select new
{
i.Id,
// A Bunch of more fields shortened for berevity
d.Data
};
var ItemList = items.ToList();
// Keep on going down the hierarchy if you need more child results
// Do your processing psuedocode
for each item in list
filter child list
for each item in child list
.....
//
Wouldn't mind knowing how to do this all using generics and a recursive method given the top level table