nhibernate QueryOver select one table after join - c#

So I am having issues selecting just the Parent entities as this will also return the Child entities.
Currently I have the following entities:
public class Parent
{
public virtual string KeyPart1 { get; set; }
public virtual string KeyPart2 { get; set; }
public virtual string KeyPart3 { get; set; }
public virtual string ParentProperty { get; set; }
public virtual ISet<Child> Children { get; set; } = new HashSet<Child>();
}
public class Child
{
public virtual string KeyPart1 { get; set; }
public virtual string KeyPart2 { get; set; }
public virtual string KeyPart3 { get; set; }
public virtual string KeyPart4 { get; set; }
public virtual string ChildProperty { get; set; }
}
And the actual Query:
Parent parentAlias = null;
Child childAlias = null;
var query = Session.QueryOver(() => parentAlias)
.Left.JoinAlias(x => x.Children, () => childAlias)
.Where(whereClause);
This generates the a sql statement very similar to this:
SELECT
this_.KeyPart1 as KeyPart11_5_4_,
this_.KeyPart2 as KeyPart22_5_4_,
this_.KeyPart3 as KeyPart33_5_4_,
this_.ParentProperty as ParentProperty4_5_4_,
childalias1_.KeyPart1 as KeyPart11_6_6_,
childalias1_.KeyPart2 as KeyPart22_6_6_,
childalias1_.KeyPart3 as KeyPart33_6_6_,
childalias1_.KeyPart4 as KeyPart44_6_6_,
childalias1_.ChildProperty as ChildProperty5_6_6_,
FROM
Parent this_
left outer join
Child childalias1_
on this_.KeyPart1=childalias1_.KeyPart1
and this_.KeyPart2=childalias1_.KeyPart2
and this_.KeyPart3=childalias1_.KeyPart3
WHERE
(SomeWhereClause)
Notice that this will return both Parent and Child tables. When
query.List()
is run, it will retrieves all of the Parents + all of the children of each parent, creating duplicate parent entries in the end result list. I just want to retrieve all of the Parents or generate the a sql statement similar to the following:
SELECT
this_.KeyPart1 as KeyPart11_5_4_,
this_.KeyPart2 as KeyPart22_5_4_,
this_.KeyPart3 as KeyPart33_5_4_,
this_.ParentProperty as ParentProperty4_5_4_,
FROM
Parent this_
left outer join
Child childalias1_
on this_.KeyPart1=childalias1_.KeyPart1
and this_.KeyPart2=childalias1_.KeyPart2
and this_.KeyPart3=childalias1_.KeyPart3
WHERE
(SomeWhereClause)
Does anyone have any tips on doing this using nhibernate's QueryOver API?

One way would be, to use Transformers.DistinctRootEntity For example:
var query = Session.QueryOver(() => parentAlias)
.Left.JoinAlias(x => x.Children, () => childAlias)
.Where(whereClause)
// Here is the trick
.TransformUsing(Transformers.DistinctRootEntity)
See more in this Andrew Whitaker post:
QueryOver Series - Part 4: Transforming
The other way...
Well, I would not go, in fact I do go, do not use this approach. My really proved way is to:
Create query from Child and join parent (star schema)
If we have to query parent
for filtering through child use subselects
for loading children in batches use mapping setting batch-size
The first is obvious, and because we query star schema, we can easily use paging
The second would need some adjustments for filtering parent by child:
Fetching Paginated Entity with Collection
For effective loading of child collection, we can profit from batch-size mapping. Check these:
How to Eager Load Associations without duplication in NHibernate?
How to implement batch fetching with Fluent NHibernate when working with Oracle?

Related

Loading Parent Child data using Entity Framework stored procedure

I have two stored procedures, one to load parent data and other to load child data. Is there anyway to do this in a better way? Please see code below.
public partial class Product
{
public long Id { get; set; }
public string ProductId { get; set; }
public string Name { get; set; }
public string ProductionNo { get; set; }
public string UniqueIdentificationNo { get; set; }
public virtual ICollection<Process> Processes { get; set; }
}
public List<GetProductsBetween_Result> GetProductsBetween(DateTime? startDate, DateTime? endDate)
{
var products = DbContext.GetProductsBetween(startDate, endDate).ToList();
foreach(var product in products)
{
product.Processes = DbContext.GetWaitingTime(product.Id).ToList();
}
return products;
}
This entire scenario can be rewritten using Linq(Language Intergrated Query).
You can apply eager loading to fetch the Processes of a product. This is similar
to sql joins which Linq provides, such a state that code is optimised and foreachs
and hits to the server can be reduced
var products = from u in dbContext.Product.Include(u => u.Processes)
select new ProductView
{
item1 = XXX,
item2 = yyy
};
var newdata = user.ToList().where("some lambda expressions")
with conditions to filter date
I had the same issue with a really complex search that had to return the full tree of an object.
What you need to do is:
Make a stored procedure that returns in only one call multiple result sets for the root object and for all its dependent objects:
i.e. (add your own conditions)
SELECT p.* FROM Products p WHERE p.Name LIKE '%test%';
SELECT DISTINCT r.* FROM Processes r INNER JOIN Products p ON p.id = r.idProduct WHERE p.Name LIKE '%test%';
After the return, re-attach the parent objects with its childs as described in this article:
https://blogs.infosupport.com/ado-net-entity-framework-advanced-scenarios-working-with-stored-procedures-that-return-multiple-resultsets/
You will need to iterate each Product from the first result set and attach its Processes.
This should be much faster since you only make one round-trip to the database and less queries.

Creating LINQ statement against an EF Context with no relationships

I cannot wrap my head around how to write a linq query against my EF context to get what I want.
1) What I have
Database with no foreign keys assigned, and a reverse engineered code first entity framework project. I tried manually adding virtual classes so EF might create implied foreign keys in the DBcontext, but I get errors on my .Include statements still.
Without the include the only thing I can think of is to use left joins, but I haven't gotten it down yet. In the end there will be 21 tables I have to get data from, but the following table outline encapsulates the majority of issues i'm facing.
Sample data structure:
Table Human: HumanId, LastFoodEatenId, FavoriteFoodId, CurrentlyDesiredFoodId
Table Food: FoodId, FoodName, FoodStuff
Table Toys: HumanOwnerId, ToyId, ToyName
Table Pets: HumanOwnerId, PetId, PetName, PetType
Table PetSurgery: PetId, SurgeryId, SurgeryPerformed
2) What I want
Given a HumanID, I want a compsite class or something like it from a single query.
Public Class QueryResult
{
public Human human {get;set;}
public Food LastEatenFood {get;set;}
public Food FavoriteFood {get;set;}
public Food CurrentlyDesiredFood {get;set;}
public IEnumerable<Toy> Toys {get;set;}
public IEnumerable<Pet> Pets {get;set;} //Includes surgeries if any
}
Is it even possible to write a single query to get this kind of information in a single db hit? I'd be fine is someone simply confirmed it is't possible. Then I can at least request we add relationships to our database.
Thanks in advance,
You can use linq to query multiple, non-related tables.
I'm going to assume a LOT about your entities, but here we go...
int humanId = 1234;
using (var context = new MyContext())
{
var human = (from h in context.Humans
join lf in context.Foods on h.LastFoodEatenId equals lf.foodId into lfg
from lf in lfg.DefaultIfEmpty() // left join
join ff in context.Foods on h.FavoriteFoodId equals lf.foodId into ffg
from ff in ffg.DefaultIfEmpty() // left join
join cf in context.Foods on h.CurrentlyDesiredFoodId equals lf.foodId into cfg
from cf in cfg.DefaultIfEmpty() // left join
join p in context.Pets on h.humanId equals p.humanId into pg // group
join t in context.Toys on h.humanId equals t.humanId into tg // group
where h.humanId = humanId
select new QueryResult { human = h, LastEatenFood = lf, FavoriteFood = ff, CurrentlyDesiredFood = cf, Toys = tg, Pets = pg }
).SingleOrDefault();
}
Note: I'm doing this from memory without a syntax checker, so ymmv. Adding surgeries should be possible as well, but may require a subquery.
I tried manually adding virtual classes
I assume you mean virtual collections. You can define relationships in a "code-first" model if they are not in the database. The only condition is that foreign keys must refer to properties that EF knows as primary keys. So you should be able to do LINQ queries using navigation properties in stead of these verbose joins by a model like this (reduced to the essentials):
class Human
{
public int HumanId { get; set; }
public int LastFoodEatenId { get; set; }
public virtual Food LastEatenFood { get; set; }
public int FavoriteFoodId { get; set; }
public virtual Food FavoriteFood { get; set; }
public int CurrentlyDesiredFoodId { get; set; }
public virtual Food CurrentlyDesiredFood { get; set; }
public virtual ICollection<Toy> Toys { get; set; }
public virtual ICollection<Pet> Pets { get; set; }
}
class Food
{
public int FoodId { get; set; }
}
class Pet
{
public int PetId { get; set; }
public int HumanOwnerId { get; set; }
}
class Toy
{
public int ToyId { get; set; }
public int HumanOwnerId { get; set; }
}
And a mapping:
class HumanMapping : EntityTypeConfiguration<Human>
{
public HumanMapping()
{
HasOptional(h => h.LastEatenFood).WithMany()
.HasForeignKey(h => h.LastFoodEatenId);
HasOptional(h => h.FavoriteFood).WithMany()
.HasForeignKey(h => h.FavoriteFoodId);
HasOptional(h => h.CurrentlyDesiredFood).WithMany()
.HasForeignKey(h => h.CurrentlyDesiredFoodId);
HasMany(h => h.Toys).WithOptional().HasForeignKey(t => t.HumanOwnerId);
HasMany(h => h.Pets).WithOptional().HasForeignKey(t => t.HumanOwnerId);
}
}
EF will infer the primary keys by name conventions.
Now you will be able to execute a LINQ statement like:
context.Humans.Where(h => h.HumanId == id)
.Include(h => h.LastEatenFood)
.Include(h => h.FavoriteFood)
.Include(h => h.CurrentlyDesiredFood)
.Include(h => h.Toys)
.Include(h => h.Pets)
From your description I understand that PetSurgery should be a junction class between Pet and another class (Surgery?). Anyway, I think you will manage creating the correct mappings, seeing this example.

Querying and mapping complex objects using Linq-to-SQL

I have the following two tables:
ChildTable
ID ManyColumns ParentID
1 XXXX 1
2 YYYY 1
3 ZZZZ 4
ParentTable
ID Name
1 aaaa
2 bbbb
3 cccc
4 dddd
I have the following classes representing the above tables:
public class Child
{
public string ID { get; set; }
public string ManyColumns { get; set; }
public string ParentID { get; set; }
}
public class Parent
{
public string ID { get; set; }
public string Name { get; set; }
}
BUT for data transfer, I have the respective classes:
public class ChildDTO
{
public string ID { get; set; }
public string ManyColumns { get; set; }
public ParentDTO Parent { get; set; } //Here is the only IMPORTANT difference
}
public class ParentDTO
{
public string ID { get; set; }
public string Name { get; set; }
}
How can I use LINQ-to-SQL in way I can resolve Child to ChildDTO with:
Minimum queries
Minimum Mappings
I know I could use this option:
List<ChildDTO> ChildDTOs = (from C in context.Childs
join P in context.Parents on C.ParentId equals P.Id
select new ChildDTO(){
ID = C.ID,
ManyColumns = C.ManyColumns,
Parent = P});
But, I am trying to avoid this multiple mappings I have to do on Select statement.
Also, Child Class is constantly changing in the current Beta Phase. So, if I use the options above, I have to constantly update those mappings.
To make it easier for coding, I am using AutoMapper in this way
AutoMapper.Mapper.CreateMap<Child, ChildDTO>()
.ForMember(dst => dst.Parent, opt => opt.ResolveUsing<Resolver_ParentId_to_Parent>().FromMember(src => src.ParentId))
public class Resolver_ParentId_to_Parent : ValueResolver<string, ChildDTO>
{
protected override ChildDTO ResolveCore(string source)
{
return (from P in context.Parents
Where P.Id = source.ToString()
select item).FirstOrDefault();
}
}
Then, I can simply Map it:
List<Child> Childs = (from C in context.Childs select C);
List<ChildDTO> newChildDTOs = AutoMapper.Mapper.Map<List<ChildDTO>>(Childs);
This is good because:
Fast
Clean
Minimum code effort... EVEN when Child Table has changed, by adding or deleting a column. You are always good to go as long as Class Child and Table Child are up-to-date with same properties/column names. AutoMapper does the trick.
The downside:
I am hammering SQL with multiples "ResolveCore" for each new ChildDTO.
What you guys think? Is there a magic way to resolve Child to ChildDTO in 1 shot only, without manual mappings, without hammering SQL?
Just dreaming:
List<ChildDTO> ChildDTOs = (from C in context.Childs
join P in context.Parents on C.ParentId equals P.Id
select SuperConversor(new ChildDTO())).ToList;
I would go with your approach using the automapper with a modification. Add a Parent member to your Child class. Then you can load it in the query directly (Loading Related Entities) so you will have it in the Child instance without using the automapper resolver and without having multiple queries for a single child.
Also, you could consider other mappers. We switched from using automapper to emitmapper, since we found it was performing ~10 times faster. But that requires some testing, we had complex classes going down several levels.

How to join Multiple tables using Repository Pattern & Entity Framework?

I need to join multiple tables using repository pattern & Entity Framework (using C#). Is this possible? If so, please let me know how to do the same.
In EF, joining tables is done through the use of Navigation Properties. Basically, EF does it for you. When implementing in your Repositories, may it be Generic or not, you can call the Include method when building your query expression to tell EF to populate the navigation properties for you.
Let's say we have these POCO class:
public class Dog
{
public int DogId { get; set; }
public string Name { get; set; }
public int OwnerId { get; set;}
public Owner Owner { get; set; } // the navigation property
}
public class Owner
{
public int OwnerId { get; set; }
public string Name { get; set; }
// another navigation property
// all the dogs that are related or owned by this specific owner
public ICollection<Dog> DogList { get; set; }
public ICollection<Cat> CatList { get; set; }
}
Here's a sample code snippet using Include:
public virtual IEnumerable<Dog> Retrieve()
{
var _query = context.Dog.Include(a => a.Owner);
...
...// rest of your code
}
And for multiple tables you can nest the include method like so:
public virtual IEnumerable<Owner> Retrieve()
{
// you can nest as many as you want if there are more nav properties
var _query = context.Owner
.Include(a => a.DogList)
.Include(a => a.CatList);
...
...// rest of your code
}
Once you include nav properties then that is basically joining those other tables. Just look at the SQL being generated by the query. Hope this helps!

How to load only the ids for an associated entities

I have N-Tier architecture and in service layer I need a way to get only the ids for associated entities or the full entities. So on one session I only need the ids and in other session I may need the full entities.
I have two entities:
public class ParentEntity
{
public virtual long Id { get; set; }
public virtual IList<ChildEntity> Children { get; set; }
public virtual string Name { get; set; }
// ... other fields
}
public class ChildEntity
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
// ... other fields
}
I some times I need to load the full ChildEntity (in verification layer) but some times I need only to load the ids for it like this in my service layer:
ParentEntity parent = repository.GetById(someId);
SendChildIds(parent.Children.Select(x => x.Id));
But doing this will load the ChildEntity fully I only want to load the ids like this
SELECT parentchild0_.ParentId as ParentId0_,
parentchild0_.ChildId as ChildId0_
FROM ParentChildEntities parentchild0_
WHERE parentchild0_.ParentId0_= 447 /* #p0 */
but he do something like this
SELECT pce.ParentId, ce.* FROM ChildEntities ce INNER JOIN ParentChildEntities pce on pce.ChildId = ce.Id WHERE pce.ParentId = 447
I use FluentNHibernate to configure the mappings.
You could either use projections or map to another entity which would only return the said requirements.
i.e.
ParentEntity {..., IList<ChildEntity> Children}
ChildEntity {...}
ParentEntityLite {...} //mapped w.r.t requirements i.e. no mappings for children
or via projections
from parent in _session.Query<ParentEntity>()
select new {parent.Id, ...}; //projection --> parent.Id
from parent in _session.Query<ParentEntity>()
select new ParentEntity(){Id = parent.Id}; // projection --> parent.Id

Categories

Resources