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.
Related
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?
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.
my EF Poco classes structure as below and what I try to achieve is to get all CategoryProducts Including Products and ProductName but only ProductNames having languageid=1
I dont want to filter Root objects. I need not all productnames are loaded but only productname with languageid=1
I dont know how to achieve this. for example, I tried query below
var products = db.CategoryProduct.Include("Product.ProductName").
Where(p=>p.Product.ProductName.Any(a=>a.LanguageId==1)).ToList();
But this one filters all categoryProducts which have ProductName with languageid=1. this is not what I want because all products have names in 5 different languages. I just dont want to load eagerly for each product 5 times but only 1 time for languageid=1
public partial class CategoryProduct
{
[Key]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
public partial class Product
{
public virtual ICollection<ProductName> ProductName { get; set; }
}
public partial class ProductName
{
public int ProductId { get; set; }
public int LanguageId { get; set; }
public string Name { get; set; }
public virtual Product Product { get; set; }
}
I'm afraid that using eager loading you can't filter the related entities unless you project your query in an anonymous type or a DTO:
var products = db.CategoryProduct.Include(c=>c.Product.ProductName)
.Select(c=> new CategoryProductDTO()
{
//...
ProductNames= c.Product.ProductName.Where(a=>a.LanguageId==1)
})
.ToList();
If you don't want to project your query and you want to load specific related entities, then I suggest you to use Explicit Loading:
var catproduct = db.CategoryProduct.Include(c=>c.Product).FirstOrDefault();// This is just an example, select the category product that you need to load the related entities
context.Entry(catproduct.Product)
.Collection(b => b.ProductName)
.Query()
.Where(pn => pn.LanguageId==1)
.Load();
But IMHO the first variant is the way to go
this is not easily doable, but something like the following may do it:
from cp in db.CategoryProduct.Include(x => x.Product)
from pn in cp.Product.ProductName.Where(x => x.LanguageId == 1).DefaultIfEmpty()
select new {
Cat = cp,
Name1 = pn.Name
}
then you have you product in Cat.Product, and the name in Name1.
The basic idea is to set a LEFT JOIN on ProductName.
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.
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