Prevent webapi from returning all data from associated tables - entity-framework

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.

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.

DbSet property of type class returns null

I'm creating an API for an app. The DbContext I have trouble with looks like this:
public class SchoolPlannerDbContext : DbContext
{
public SchoolPlannerDbContext(DbContextOptions<SchoolPlannerDbContext> options) : base(options) { }
public DbSet<Activity> Activities { get; set; }
public DbSet<Room> Rooms { get; set; }
public DbSet<Subject> Subjects { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Group> Groups { get; set; }
}
The Activity class is as follows:
public class Activity
{
public int ID { get; set; }
[Required]
public Teacher Teacher { get; set; }
[Required]
public Room Room { get; set; }
[Required]
public Subject Subject { get; set; }
[Required]
public Group Group { get; set; }
[Required]
public int Slot { get; set; }
[Required]
public int Day { get; set; }
}
All the other properties contain an int ID and a string Name.
My controller looks like this:
public class SqlPlannerData : ISchoolPlannerData
{
private readonly SchoolPlannerDbContext db;
public SqlPlannerData(SchoolPlannerDbContext db)
{
this.db = db;
}
public IEnumerable<Activity> GetActivities()
{
return db.Activities;
}
public IEnumerable<Group> GetGroups()
{
return db.Groups;
}
}
GetGroups() works as intended and returns an IEnumerable with properties set correctly.
My problem is that when I'm trying to access db.Activities, the properties of type, say, Teacher (non-basic types like int) are set to null:
Debugger screenshot.
However, there is a row in the database that looks like this. I.e. the columns exist in the database.
What do I do to make GetActivities() return an IEnumerable with correctly set properties?
Some properties are null because of lazy loading you need to include them
return db.Activities
.Include(i => i.Teacher)
.Include(i => i.Room)
.Include(i => i.Subject)
.Include(i => i.Group)
.ToList()
Each propety Id can be configured by EF5+ as shadows. But I usually prefer to add all Ids explicitely. This way I have much less problem when I am using db context in the project. But is is optional and you can leave it as is
public class Activity
{
public int ID { get; set; }
[Required]
public int? TeacherId { get; set; }
[Required]
public int? RoomId { get; set; }
[Required]
public int? SubjectId { get; set; }
[Required]
public int? GroupId { get; set; }
public virtual Teacher Teacher { get; set; }
public virtual Room Room { get; set; }
public virtual Subject Subject { get; set; }
public virtual Group Group { get; set; }
[Required]
public int Slot { get; set; }
[Required]
public int Day { get; set; }
}
and in order to get list activities you have to add ToList() or ToArray() at least
public IEnumerable<Activity> GetActivities()
{
return db.Activities.ToArray();
}
and by the way, you can' t using not nullabe Id as required becaue it is relevant
[Required]
public int TeacherId { get; set; }
since int by default is 0 and it is a valid value and required will not be working

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

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

LINQ query throw exception on FirstOrDefault method

I'm using EF core, and I have a many-to-many relationship between two entity
IotaProject <--> User
Here's entities & dto related to the question
public class IotaProject
{
[Key]
public int Id { get; set; }
[Required]
public string ProjectName { get; set; }
[Required]
public DateTime Create { get; set; }
public ICollection<ProjectOwnerJoint> Owners { get; set; } = new List<ProjectOwnerJoint>();
}
public class ProjectOwnerJoint
{
public int IotaProjectId { get; set; }
public IotaProject IotaProject { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string ShortName { get; set; }
[Required]
public string Email { get; set; }
public ICollection<ProjectOwnerJoint> OwnedProjects { get; set; } = new List<ProjectOwnerJoint>();
}
public class ApplicationDbContext : DbContext
{
public DbSet<IotaProject> IotaProjects { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProjectOwnerJoint> ProjectOwnerJoint { get; set; }
}
public class IotaProjectDisplayDto
{
public int Id { get; set; }
public string ProjectName { get; set; }
public DateTime Create { get; set; }
public UserMinDto Owner { get; set; }
public int Count { get; set; }
public IEnumerable<UserMinDto> Reviewers { get; set; }
}
public class UserMinDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string ShortName { get; set; }
}
Following LINQ is the problem, the LINQ purpose is to convert IotaProject to IotaProjectDisplayDto, and key part is that Owners property of IotaProject is ICollection and Owner property in IotaProjectDisplayDto is just one single element UserMinDto, so I only need to get the first element of IotaProject's Owners and that's FirstOrDefault() comes.
IEnumerable<IotaProjectDisplayDto> results = _db.IotaProjects.Select(x => new IotaProjectDisplayDto
{
Id = x.Id,
ProjectName = x.ProjectName,
Create = x.Create,
Owner = x.Owners.Select(y => y.User).Select(z => new UserMinDto { Id = z.Id, FullName = z.FullName, ShortName = z.ShortName }).FirstOrDefault()
});
return results;
it throws run-time exception
Expression of type 'System.Collections.Generic.List`1[ToolHub.Shared.iota.UserMinDto]' cannot be used for parameter
of type 'System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto]'
of method 'ToolHub.Shared.iota.UserMinDto FirstOrDefault[UserMinDto](System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto])' (Parameter 'arg0')
I'm guessing it's probably related to deferred execution, but after read some posts, I still can't resolve it.
Any tips would be appreciated.
Right now, the only way I can get this work is I change type of Owner property in IotaProjectDisplayDto into IEnumrable, which will no longer need FirstOrDefault() to immediate execution. And later on, I manually get the first element in the client to display.
This issue happened in Microsoft.EntityFrameworkCore.SqlServer 3.0.0-preview7.19362.6
I end up downgrade to EF core stable 2.2.6 as Ivan suggested in comment, and everything works fine.