Eager loading of derived class in Entity Framework - entity-framework

I have a model like this:
public abstract class Point
{
public int Id { set; get; }
public string Name_F { set; get; }
public byte Side { set; get; }
}
public class Place : Point
{
public bool IsATM { set; get; }
public bool Is24h { set; get; }
public string Tel { set; get; }
public virtual Category Category { set; get; }
}
public class Street : Point
{
public bool IsWalkway { set; get; }
}
I want to load all of the Point table records including records for Place and Street which are derived from Point class.
I used this but I didn't get any data:
var points = Context.Points
.OfType<Place>()
.Include(p => p.SubCategory)
.Concat<Points>(Context.Points.OfType<Street>());
I would like to get Places and Streets in the same query.
Any idea?

Related

EF Core: How to organize models/tables and use DBQuery when I have 2 different tables relating to the same common one

Let's say I have a bike shops that sell various types of bikes: pro, kids, youth, leisure and any mixture. So I have a table of shops that refers/relates to a table of possible types. Now these shops also host events with the same types: events for pros, kids etc again any mixture. And so I have another table of events that also need to refer/relate to the same table of types:
I need to be able in a single quick query get a list of all bike types for a shop or event.
So I figured I'd have 3 main tables: Shops, Events, BikeTypes and two intermediate to link shops and events to bike types:
And I organized my models as:
public class BikeShop
{
public long Id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
}
public class BikeEvent
{
public long Id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime date { get; set; }
public string location { get; set; }
}
public class BikeType
{
public long Id { get; set; }
public string name { get; set; }
public string code { get; set; }
}
public class ShopBikeTypes
{
public long Id { get; set; }
public BikeShop shop { get; set; }
public BikeType biketype { get; set; }
}
public class EventBikeTypes
{
public long Id { get; set; }
public BikeEvent bikeevent { get; set; }
public BikeType biketype { get; set; }
}
With DataCotext:
public class DataContext : DbContext
{
public DbSet<BikeShop> Shops { get; set; }
public DbSet<BikeEvent> Events { get; set; }
public DbSet<BikeType> BikeTypes { get; set; }
public DbSet<ShopBikeTypes> ShopBikeTypes { get; set; }
public DbSet<EventBikeTypes> EventBikeTypes { get; set; }
}
Migration creates correct database structure just as my diagram. Great!
Now how do I make a straight forward query:
get list of all bike types for a shop
get list of all bike types for an event
Is my structure even correct?
Do I need some List<> in the main object models BikeShop and BikeEvent?
EF's include and theninclude seem to require some list?
This feels like such a typical scenario. What's the right way of doing this?
Thank you.
Those are the linq queries that you are asked but when i look at that your class models, i can say they are wrong. U need to define first which relation theyre having. if all of that relation has based on one-to-one, u wont gonna need any List<> in your class models. but if u have one-to-many relation,u gonna need them.
1- get list of all bike types for a shop
return DbContext.Shops
.Include(x>=x.ShopBikeTypes)
.ThenInclude(x=>x.BikeTypes).ToList();
2- get list of all bike types for an event
return DbContext.Events
.Include(x=>x.EventBikeTypes)
.ThenInclude(x=>x.BikeTypes).ToList();
3- Get all data in that relation
return DbContext.BikeTypes
.Include(x>=x.EventBikeTypes)
.ThenInclude(x=>x.Events).AsSplitQuery()
.Include(x=>x.ShopBikeTypes)
.ThenInclude(x>=x.Shops).AsSplitQuery()
.ToList();
it can be a tough query, do not try to use AsNoTracking() because it can cause Cartesian Explosion.
#BerkGarip: thank you for your help. I ended up with this models structure:
public class AShop
{
public long Id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
public List<AShopType> aTypes { get; set; }
}
public class AEvent
{
public long Id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime date { get; set; }
public string location { get; set; }
public List<AEventType> aTypes { get; set; }
}
public class AType
{
public long Id { get; set; }
public string name { get; set; }
public string code { get; set; }
}
public class AShopType
{
public long Id { get; set; }
public AType aType { get; set; }
}
public class AEventType
{
public long Id { get; set; }
public AType aType { get; set; }
}
In order to achieve what I needed using answer from #BerkGarip I figured out that the trick there was to have lists in the 'shop' and 'event' models to the intermediate objects which in turn have a single reference to 'type'. This way database layout is the same and it is many-to-many relationship and I can use 'include' and 'thenInclude' exactly as expected:
return await _context.AShops.Where(x => x.name == "Z")
.Include(x => x.aTypes)
.ThenInclude(y => y.aType)
.ToListAsync();

Returning Entity with its children

Hi I am trying to return all vehicles with their recorded mileage through an api using ASP.Net Core with the following code:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.Include(m=>m.Mileages).ToList();
}
However this only returns the first vehicle with its mileages and not the others (there are five dummy vehicles in the db all with an initial mileage).
If I change the code to:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.ToList();
}
it returns the full list of vehicles but no mileage.
My class files are:
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<Mileage> Mileages { get; set; }
}
and
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
}
thanks for looking!
Tuppers
you can have them auto-load (lazy loading) using proxies... but for that, your foreign entities and collections must be marked virtual in your POCOs:
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public virtual ICollection<Mileage> Mileages { get; set; }
}
The proxy creation and lazy loading turned on, but that's the default in EF6.
https://msdn.microsoft.com/en-us/data/jj574232.aspx
Let me know if this works.
Well after a lot of searching I managed to find a solution. I used the following:
[HttpGet]
public IEnumerable<VehicleDto> Get()
{
var query = _context.Vehicles.Select(v => new VehicleDto
{
Registration = v.Registration,
Make = v.Make,
Model = v.Model,
Marked = v.Marked,
Mileages = v.Mileages.Select(m => new MileageDto
{
MileageDate = m.MileageDate,
RecordedMileage = m.RecordedMileage
})
.ToList(),
})
.ToList();
return (IEnumerable<VehicleDto>) query.AsEnumerable();
this doesn't seem to be the most elegant way of doing this, if anyone could offer any advice but it does return what is required.
The DTO's look like:
public class VehicleDto
{
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<MileageDto> Mileages { get; set; }
}
and
public class MileageDto
{
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
}
Thanks for taking the time to look at this
Tuppers

Get multiple tables data through Entity Framework with Generic Repository and Unit Of work

I am working on Web-API project and using Entity Framework with Generic Repository and Unit Of work. Basically i follow a tutorial for this.
Here is my table architecture.
Entity
public class ProductEntity
{
public int ProductId { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public string ProductImgName { get; set; }
public bool IsActive { get; set; }
public int PrimaryCatId { get; set; }
public int SecondaryCatId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public System.DateTime CreateDate { get; set; }
public List<PrimaryProductEntity> objPrimaryProduct { get; set; }
public List<SecondaryProductEntity> objSecondaryProduct { get; set; }
}
public class PrimaryProductEntity
{
public int PrimaryCatId { get; set; }
public string PrimaryCatName { get; set; }
}
public class SecondaryProductEntity
{
public int SecondaryCatId { get; set; }
public string SecondaryCatName { get; set; }
public int PrimaryCatId { get; set; }
}
Services Code
public IEnumerable<BusinessEntities.ProductEntity> GetAllProducts()
{
var products = _unitOfWork.ProductRepository.GetAll().ToList();
var primaryProducts = _unitOfWork.PrimaryProductRepository.GetAll().ToList();
var secondaryProducts = _unitOfWork.SecondaryProductRepository.GetAll().ToList();
if (products.Any())
{
Mapper.CreateMap<tblProduct, ProductEntity>();
var proInfo = from P in products
join PP in primaryProducts on P.PrimaryCatId equals PP.PrimaryCatId
join SP in primaryProducts on P.SecondaryCatId equals SP.SecondaryCatId
select P;
var productsModel = Mapper.Map<List<tblProduct>, List<ProductEntity>>(proInfo);//getting error
return productsModel;
}
return null;
}
i know my implementation is wrong, i don't know what to write in code for fetch data from multiple tables. Please help me.
Required Data
ProductID,ProductName, PrimaryCatName, SecondaryCatName,Price, Quantity
Your Product Entity class Doesn't require a List<PrimaryProductEntity> and List<SecondaryProductEntity>. I suppose according to your class diagram Each Product is associated with one PrimaryProductEntity and one SecondaryProductEntity.
Once your model class is corrected, you would be able to access the properties of the navigation. I am not so good with writing a Query the way you want. But i hope you could get an idea of what you should be doing

Entity Framework : get related entities

I created a WCF service with Entity Framework.
I have 2 tables : Theaters and Locality. Locality as a foreign key in Theaters.
My method :
public theater[] GetTheaters()
{
using (Entities context = new Entities())
{
return context.theater.ToArray();
}
}
I have to remove the "virtual" keyword from "public virtual locality locality { get; set; }" in my theater class. Otherwise, I get a CommunicationException.
But when I do that, I get my list of theaters but the locality is null...
How can I get the locality ?
Thanks
My model class ( I also have other entities) :
public partial class locality
{
public locality()
{
this.theater = new HashSet<theater>();
}
public int idLocality { get; set; }
public int npa { get; set; }
public string locality1 { get; set; }
public ICollection<theater> theater { get; set; }
}
public partial class theater
{
public theater()
{
this.session = new HashSet<session>();
}
public int idTheater { get; set; }
public string name { get; set; }
public string address { get; set; }
public int idLocality { get; set; }
public double latitude { get; set; }
public double longitude { get; set; }
public int seats { get; set; }
public string phone { get; set; }
public string email { get; set; }
public bool threeD { get; set; }
public locality locality { get; set; }
public ICollection<session> session { get; set; }
}
Here is the error that I get :
"Object graph for type 'locality' contains cycles and cannot be serialized if reference tracking is disabled.
EDIT :
The solution that I found :
In my locality class, I had a Collection of theaters.
I had to add "private to the setter like this :
" public ICollection theater { get; private set; }"
So it works, but I still have a problem, I can't access to the theaters from the locality entity anymore. (no more bi-directional)
If you want to force related entities to load, you can use the Include method to do so. By default, related entities are loaded Lazily.
Your example would be:
public theater[] GetTheaters()
{
using (Entities context = new Entities())
{
return context.theater.Include(t=>t.Locality).ToArray();
}
}
You can use eager loading or explicit loading. With eager loading you use the Include extension method:
return context.Theater.Include(t => t.Locality).ToArray();
You're missing the correct annotations to create the relationships. See the code below. (or create the relationships yourself if using the FluentAPI)
Look for the [Key] and [ForeignKey] annotations, as well as the virtual keyword.
public partial class locality
{
public locality()
{
//this.theater = new HashSet<theater>();
}
[Key]
public int idLocality { get; set; }
public int npa { get; set; }
public string locality1 { get; set; }
public virtual ICollection<theater> theaters { get; set; }
}
public partial class theater
{
public theater()
{
//this.session = new HashSet<session>();
}
[Key]
public int idTheater { get; set; }
public string name { get; set; }
public string address { get; set; }
public int idLocality { get; set; }
public double latitude { get; set; }
public double longitude { get; set; }
public int seats { get; set; }
public string phone { get; set; }
public string email { get; set; }
public bool threeD { get; set; }
[ForeignKey("idLocality")]
public virtual locality locality { get; set; }
//public ICollection<session> session { get; set; }
}

Sum related properties in entity framework

I have a problem with sum of navigation properties using entity framework
Here is my example classes
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Call> Calls { get; set; }
[NotMapped]
public decimal TotalCallDuration { get { return Calls.Sum(c => c.Value); } }
}
public class Call
{
public int Id { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public decimal Value { get; set; }
public DateTime Date { get; set; }
}
This works well but when i have hundreds of records it is very slow
How can i make this faster but without losing functionality?
Thanks
what you want to do is:
customer.TotalCallDuration = context.Call.Sum(x => x.Value).Where(x => x.CustomerID == customer.Id);