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
Related
I have three tables Car Makes, Car Model and CarsandModel. I have the Carsand Model table because a model could be built by multiple manufactures. I want to return a list of car makes that has a list of car models. The longic I have now is not filtering the list on car makes for the car model.
I have tried to add a where statement but I am still not getting the correct return
public class CarMake
{
public int CarMakeId { get; set; }
public string CarMakeName { get; set; }
public List<CarModel> CarModel { get; set; }
}
public class CarModel
{
public int CarModelId { get; set; }
public string CarModelName { get; set; }
}
public class CarsandModel
{
public int CarMakeId { get; set; }
public int CarModelId { get; set; }
}
var CarModel = (from cmake in db.CarModel
select new CarModel
{
CarModelId = cmake.CarMakeId,
CarModelName = cmake.CarMakeName,
CarMake= (from cmake in db.Carmake
join cam in db.CarsandModel on cmake.CarMakeId equals cam.CarMakeId
where (camodel.CarMakeId == cmake.CarMakeId)
select new Asset
{
CarMakeId = cmodel.CarMakeId,
CarMakeName = cmodel.CarMakeName
}).ToList()
}
).ToList();
When using entity framework people tend to (group-)join the tables themselves, while it is much easier to use the ICollections instead.
First of all: stick to the Entity Framework Code-First Conventions!
For example:
Every CarMake has zero or more CarModels. You decided to declare this as a List<CarModel>. Are you sure that CarModel[4] has a defined meaning? And I wonder what CarModel.Insert(4, new CarModel()) would mean in your code. Better stick to the interface that your database can really handle: ICollection<CarModel>.
The relation between CarMakes and CarModels seems to be a many-to-many: every CarMake has zero or more CarModels, Every CarModel is made by zero or more CarMakes.
In relational databases a many-to-many relationship is implemented using a junction table. Your class CarsAndModel represents a row in this junction table. However, when using entity framework you don't need to mention the junction table. When you design your classes properly, entity framework recognizes the relations between your tables and creates the proper junction table for it.
class CarMake
{
public int CarMakeId { get; set; }
public string CarMakeName { get; set; }
// every CarMake makes zero or more CarModels (many-to-many)
public virtual ICollection<CarModel> CarModels { get; set; }
}
class CarModel
{
public int CarModelId { get; set; }
public string CarModelName { get; set; }
// every CarModel is made by zero or more CarMakes (many-to-many)
public virtual ICollection<CarMake> CarMakes {get; set;}
}
In entity framework the columns of the tables are represented by non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
For completeness the DbContext:
class CarContext : DbContext
{
public DbSet<CarMake> CarMakes {get; set;}
public DbSet<CarModel> CarModels {get; set;}
}
This is all that entity framework needs to know to detect the many-to-many relationship. Entity framework will create the junction table for you and will keep this table up-to-date as needed.
Only if you have good reasons to deviate from the code-first conventions (and you can convince your project leader) you'll need attributes or fluent API to inform entity framework about your deviations.
But how am I supposed to join the tables if I can't access the junction table?
Answer: don't do a (group-)join, use the ICollections!
Requirement: Give me the CarModels, each with their CarMakes
var result = dbContext.CarModels
.Where(carModel => ...) // only if you don't want all CarModels
.Select(carModel => new
{
// Select only the properties you actually plan to use!
Id = carModel.CarModelId,
Name = carModel.CarModelName,
...
CarMakes = carModel.CarMakes
.Where(carMake => ...) // only if you don't want all CarMakes of this model
.Select(carMake => new
{
// again: select only the properties you plan to use
Id = carMake.CarMakeId,
Name = carMake.Name,
...
})
.ToList(),
}
Entity Framework knows your many-to-many relation and will create the proper (group-)join for you.
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 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 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!
I have parent and child objects that are derived from LINQ to SQL entities. I want to map these onto some domain-friendlier DTOs. My SQL entity classes look somewhat like this:
public class SqlEntityParent
{
public int ParentId { get; set; }
public string Name { get; set; }
public EntitySet<SqlEntityChild> Children { get; set; }
}
public class SqlEntityChild
{
public int ChildId { get; set; }
public int ParentId { get; set; }
public int Position { get; set; }
public string CategoryName { get; set; }
public string CategoryValue { get; set; }
}
In this model, it's a standard one-to-many relationship between SqlEntityParent and SqlEntityChild. Some representative data would be...
Parent:
ParentId Name
-------- -------
1 Parent1
Child:
ChildId ParentId Position CategoryName CategoryValue
------- -------- -------- ------------ -------------
1 1 1 Contents Things
2 1 1 Group GroupOne
3 1 2 Contents Things
4 1 2 Group GroupTwo
Now I want to map these data into my domain objects, which look somewhat like this:
public class DomainParent
{
public int ParentId { get; set; }
public string Name { get; set; }
public List<DomainChild> Children { get; set; }
}
public class DomainChild
{
public int Position { get; set; }
public string Contents { get; set; }
public string Group { get; set; }
}
In this structure, a single DomainChild object is made up of data from two SqlEntityChild objects, and the grouping is determined by the Position value of the child entity. So, these sample data represent a single DomainParent object with a list of two DomainChild objects. The first child should have a Position of 1, a Contents value of "Things", and a Group value of "GroupOne". The second child should have a Position of 2, a Contents of "Things", and a Group of "GroupTwo".
I am comfortable with getting one-to-one custom mapping set up in AutoMapper using ValueResolvers, but I am not sure how best to handle this. I created the below resolver and associated mapping for the parent entities which maps the whole list of child entities in one pass, but it seems goofy because I have to do the whole mapping of child objects manually in this resolver class.
Mapper.CreateMap<SqlEntityParent, DomainParent>()
.ForMember(dto => dto.Children, opt => opt.ResolveUsing<MyResolver>());
public class MyResolver: ValueResolver<SqlEntityParent, List<DomainChild>>
{
private MyDataContext db;
public MyResolver()
{
db = new MyDataContext();
}
protected override List<DomainChild> ResolveCore(SqlEntityParent source)
{
// In here:
// 1. custom LINQ queries
// 2. manual creation of DomainChild objects
// 3. manual mapping of SqlEntityChild to DomainChild
}
}
So, my main question is this: is this the best I can do with AutoMapper in this situation, or is there some other more effective method that I can use?
Typically in these cases we map straight from SqlEntityChild to DomainChild, as lists, arrays and such are automatically supported. You just need to set up mappings for the element types, given that there is no extra logic besides iterating over the original Children collection.