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

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();

Related

Entity Framework Db context issue in .net core related to Models

Am Trying to create Two Tables like bellow got some EF error.
public class Student : ModelsBase
{
public string AdharNumber { get; set; }
public byte Religion { get; set; }
public int CategoryID { get; set; }
public string Cast { get; set; }
public string SubCast { get; set; }
public string Photo { get; set; }
public DateTime DateOfJoining { get; set; } = DateTime.Now;
[Required]
public ICollection<Address> TemporaryAddress { get; set; }
[Required]
public ICollection<Address> PermanentAddress { get; set; }
}
public class Address : ModelsBase
{
public string DoorNo { get; set; }
public string StreetLocality { get; set; }
public string Landmark { get; set; }
public string City { get; set; }
public int Taluk { get; set; }
public int District { get; set; }
public int State { get; set; }
public string Pincode { get; set; }
public bool IsPermanent { get; set; } = true;
public bool IsDefault { get; set; } = true;
[ForeignKey("Student")]
public Guid StudentId { get; set; }
}
Getting the bellow error while trying to Run the "Add-Migration command"
Both relationships between 'Address' and 'Student.PermanentAddress' and between 'Address' and 'Student.TemporaryAddress' could use {'StudentId'} as the foreign key. To resolve this, configure the foreign key properties explicitly in 'OnModelCreating' on at least one of the relationships
Please help. Thanks!
Your issue is that from the Address side of things you have a Many-to-1 with a single Student, but from the Student side of things you want 2x 1-to-Many relationships.
Since The relationship is really just a 1-to-Many from the student that you want to discriminate between temporary and permanent addresses:
public class Student : ModelsBase
{
public string AdharNumber { get; set; }
public byte Religion { get; set; }
public int CategoryID { get; set; }
public string Cast { get; set; }
public string SubCast { get; set; }
public string Photo { get; set; }
public DateTime DateOfJoining { get; set; } = DateTime.Now;
[Required]
public ICollection<Address> Addresses { get; set; } = new List<Address>();
[NotMapped]
public ICollection<Address> TemporaryAddresses => Addresses.Where(x => !x.IsPermanent).ToList();
[NotMapped]
public ICollection<Address> PermanentAddresses => Addresses.Where(x => x.IsPermanent).ToList();
}
With 1-to-many collections I recommend initializing them to an empty list to avoid null reference exceptions especially if lazy loading is disabled.
The caveat here is that from EF's perspective, Student only has the Addresses collection, do not attempt to use either TemporaryAddresses or PermanentAddresses in a query expression as these are unmapped accessors. If you want to filter based on a permanent address you will have to do it through Addresses and include the condition on IsPermanent in the query.
For example:
// Not valid...
var studentsInDetroit = context.Students.Where(x => x.PermanentAddresses.Any(a => a.City == "Detroit")).ToList();
// Valid...
var studentsInDetroit = context.Students.Where(x => x.Addresses.Any(a => a.IsPermanent && a.City == "Detroit")).ToList();
Normally I don't recommend using unmapped accessors in entities because of this. It is generally better to leave entities representing pure domain/data state and project that down to view models which can be more concerned about splitting the data into a more palatable form for consumption.

Load related entities with a single .Include() in Entity Framework?

Is there a better way to load all the related entities?
Below is the ScholarshipRequest class which also has Scholarship, Status, Student, Program and User.
public class ScholarshipRequest
{
public int Id { get; set; }
public int Year { get; set; }
public Status Status { get; set; }
public DateTime ApplicationDate { get; set; }
public DateTime ActionDate { get; set; }
public Scholarship Scholarship { get; set; }
public Program Program { get; set; }
public Student Student { get; set; }
public User User { get; set; }
}
I am just posting Scholarship class here, rest are similar.
public class Scholarship
{
public int Id { get; set; }
public string Name { get; set; }
}
The below code works fine but is there a better way where i can use a single .Include() to load them all or may be some other way?
ScholarshipRequestRepository repo = new ScholarshipRequestRepository(dBContext);
List<ScholarshipRequest> stdList = repo.Collection()
.Include("Status").Include("Student").Include("User").Include("Scholarship")
.Where(x => x.User.Id == userId).ToList();

Incorrect Domain model with ef

I have a little problem with Entity Framework when trying to model the real life
problem.
I have 2 entity like this :
public class Person
{
public int Id { get; set; }
public ICollection<Task> Tasks{ get; set; }
}
public class Task
{
public int Id { get; set; }
public Person Assignee{ get; set; }
public Person Assigner{ get; set; }
}
but if I want to use Entity framework,it forces me to change my model like this that it is different from real life !!
public class Person
{
public int Id { get; set; }
public ICollection<Task> AssigneesTasks{ get; set; }
public ICollection<Task> AssignerTasks{ get; set; }
}
(i just have single one-to-many relation in fact)
what is the solution to keep my model according to real life model?
Well you might want to know what tasks a person has assigned to them, and what tasks they have assigned to others. If you don't want both Navigation properties you don't need them in EF. But you do need to tell EF which relationship the Navigation Property is for. EG:
public class Person
{
public int Id { get; set; }
[InverseProperty("Assignee")]
public ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int Id { get; set; }
public Person Assignee { get; set; }
public Person Assigner { get; set; }
}

Map many to many objects using Entity Framework

For example we have profile and organisation. Both have articles.
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
}
public class Profile
{
public int Id { get; set; }
public string Email { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
public class Organisation
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
In this way Article should have two kinds of parent so it should have something like parent type to be able to access a parent when you select articles directly.
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public ArticleParentType Parent { get; set; }
}
Is it possible to map it using Entity Framework?
Is it a good idea to do it?
What is the best practice for storing this kind of data?
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public ArticleParentType Parent { get; set; }
}
Is it possible to map it using Entity Framework?
Is it a good idea to do it?
Possible yes but not a good idea. The underlying Database can't use a foreign key for Parentid. It would be slow.
What is the best practice for storing this kind of data?
A simple approach, with 2 Nullable parents and without CascadeOnDelete:
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public virtual Profile Profile { get; set; }
public virtual Organisation Organisation { get; set; }
}
Alternatively you could use inheritance for Article, ie class OrganisationArticle : Article {}

Prevent webapi from returning all data from associated tables

I have a table called MemberCompany which has a record for each company a member has. the model is below. When i query it via a webapi method passing in the memberid, i can see in debug mode that it returns the one company for that member, however when i run it in the browser i can see it returns the entire list of members also. Is it possible to just return a collection of membercompany records without the two referenced tables? I commented out the initial code to include these two tables but they appear to still be being included in the response.
public partial class MemberCompany
{
public int id { get; set; }
public int membership_id { get; set; }
public string company_name { get; set; }
public string company_address1 { get; set; }
public string company_address2 { get; set; }
public string company_town_city { get; set; }
public Nullable<int> company_county { get; set; }
public string company_postcode { get; set; }
public string company_tel { get; set; }
public string company_fax { get; set; }
public string company_email { get; set; }
public string company_contact { get; set; }
public string company_web { get; set; }
public string company_country { get; set; }
public Nullable<System.DateTime> last_updated { get; set; }
public Nullable<decimal> latitude { get; set; }
public Nullable<decimal> longitude { get; set; }
public virtual counties counties { get; set; }
public virtual members members { get; set; }
}
WebAPI
[HttpGet("admin/api/membercompany/member/{member_id}")]
public IEnumerable<MemberCompany> GetByMember(int member_id)
{
var Companies = db.MemberCompanies
// .Include(t => t.counties)
//.Include(t => t.members)
.Where(m => m.membership_id == member_id);
return Companies.AsEnumerable();
}
Turn off lazy loading for the context. My best guess is it's on and the entities are loaded when the graph is serialized...
Note: that's actually a good idea in a web app and I'd recommend you do it globally, so that you don't get bitten by performance issues due to lazy loading later, and always know precisely what you'll return.