Entity Framework Populating Child Objects using Foreign Key - c#

I hope this isn't a duplicate as I have been looking but I was looking more at the reasoning behind this.
I have set up a user object.
public class User
{
public User()
{
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserID { get; set; }
public Guid UserGuid { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public int CompanyID { get; set; }
public int StatusID { get; set; }
[ForeignKey("StatusID")]
public Status Status { get; set; }
public int RoleID { get; set; }
[ForeignKey("RoleID")]
public UserRole UserRole { get; set; }
}
And the Child Objects
public class UserRole
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RoleId { get; set; }
public string Role { get; set; }
}
public class Status
{
[Key]
public int StatusId { get; set; }
public string Description { get; set; }
}
When I call
var testuser = dbContext.User.Where(u => u.CompanyID == 1).FirstOrDefault();
I get users.Status as being null, but users.StatusID = 1.
however, if I call
var status = dbContext.Status.ToList();
var role = dbContext.UserRole.ToList();
var testuser = dbContext.User.Where(u => u.CompanyID == 1).FirstOrDefault();
then call testuser.Status, I can see the correct object related to StatusId of 1.
Can someone explain why this would solve the issue and also how I can make it work without having to call the following beforehand.
var status = dbContext.Status.ToList();
Thanks

You can try as shown below.
Note : Use Eager loading with Include()
Eager loading is the process whereby a query for one type of entity
also loads related entities as part of the query
using System.Data.Entity;
var testuser = dbContext.User.Where(u => u.CompanyID == 1)
.Include(p => p.Status)
.FirstOrDefault();

Just use your property like this. Keyword virtual put its property to lazy loading and then you can access the whole object.
public virtual Status Status { get; set; }
And BTW, I edited your class. There are un-needed things, because you can access StatusID by property Status (like int statID = Status.StatusID;) and same to UserRole.
public class User
{
public User()
{
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserID { get; set; }
public Guid UserGuid { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public int CompanyID { get; set; }
public virtual Status Status { get; set; }
public virtual UserRole UserRole { get; set; }
}
}

Related

How to perform a simple query on a join table when the join table is created by EF?

Say I have two entities with a many-to-many relationship and create the join table manually:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserPermission> UserPermissions { get; set; }
}
public class Permission
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserPermission> UserPermissions { get; set; }
}
public class UserPermission
{
public int UserId { get; set; }
public User User { get; set; }
public int PermissionId { get; set; }
public Permission Permission { get; set; }
}
If I want to find out if a user has a specific permission I can run a simple query like this
var query = _context.UserPermissions
.Where(up => up.UserId == userId && up.PermissionId == permissionId);
Now say I remove the join table and let entity framework create it for me:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Permission> Permissions { get; set; }
}
public class Permission
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
How can I perform this same simple query on the db without pulling in a load of unnecessary data or overcomplicating the query?
The EF will create UserPermissions for you. Please find at the following LINK

Is there a way to Ignore specific property in asp.net core Include function from many to many relationship

I am working on Asp.net core 5 Web-api, I am using many to many relationship between User entity and Permission entity, and I need to load users permission from the database using eager loading but the data is returning a looped information including users full information but I only load the permissions. I am using a Dto to return the Json data. Here is my models and my code
Permission Model
public class Permission
{
public int Id { get; set; }
public string ClaimName { get; set; }
public IList<UserPermission> UserPermissions { get; set; }
}
UserPermission model
public class UserPermission
{
public int UserId { get; set; }
public AppUser AppUser { get; set; }
public int PermissionId { get; set; }
public Permission Permission { get; set; }
}
User model
public class AppUser
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Photo> Photos { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public DateTime LastActive { get; set; }
public IList<UserPermission> UserPermissions { get; set; }
public bool Status { get; set; }
}
My user Dto
public class UserDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Photo> Photos { get; set; }
public ICollection<UserPermission> UserPermissions { get; set; }
public string Token { get; set; }
}
My function
var sss = _context.UserPermissions
.Include(l => l.Permission)
.Where(v => v.UserId == user.Id)
.ToList();
Use [JsonIgnore] on the properties that we want to exclude.
note that we should use "using Newtonsoft.Json" name space not using "System.Text.Json.Serialization"

I want to join 2 tables

I am trying to join two tables and getting an error I can't resolve.
var result = _context.FileModels
.Join(_context.FilesMetaData,
fm => fm.Id,
fmd => fmd,
(a, b) => new
{
fmId = a.Id,
fmds = b
});
return result;
This is the error message:
These are the models:
public class FilesMetaData
{
public Guid Id { get; set; }
public Guid FileId { get; set; }
public Guid ApplicationId { get; set; }
public string Comment { get; set; }
public string OtherData { get; set; }
public string AppUser { get; set; }
public string Origin { get; set; }
public string CreatedBy { get; set; }
public string UpdatedBy { get; set; }
public DateTime UpdatedAt { get; set; }
}
enter code here
and FileModels are some random columns that have one index.
Your join is missing a column:
.Join(_context.FilesMetaData,
fm => fm.Id,
fmd => fmd.FileModelId, // <--- Define the right column
You appear to have foreign keys but where are the navigation properties?
I would expect to see something like:
public class FilesMetaData
{
public Guid Id { get; set; }
public File File {get;set;} <-- Navigation Property
public Guid FileId { get; set; }
public Application Application {get;set;} <-- Navigation Property
public Guid ApplicationId { get; set; }
...
}
public class File
{
public Guid Id {get;set;}
...
}
public class Application
{
public Guid Id {get;set;}
...
}
This will automatically do the joins when required.

How to create a foreign relation to one Model from multiple other models using Entity framework?

I am trying to create my first app using ASP.NET MVC framework and Entity Framework 6.
I chose to use code first approach and I started by defining my Models.
I have a model called Client with an identity attribute called Id. I have multiple Models that has an attribute called ClientId. The ClientId attribute should have virtual link to the Clients Model.
Here is how my Client model looks like
[Table("clients")]
public class Client
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string status { get; set; }
public DateTime created_at { get; set; }
public DateTime? modified_at { get; set; }
public Client()
{
status = "Active";
created_at = DateTime.UtcNow;
}
}
Then here is how I am creating a belong to relation using other models.
[Table("BaseClientsToUsers")]
public class ClientToUser : ModelDefault
{
[ForeignKey("User")]
public int UserID { get; set; }
[ForeignKey("Client")]
public int ClientId { get; set; }
[ForeignKey("Team")]
public int DefaultTeamId { get; set; }
public DateTime? JoinedAt { get; set; }
public bool IsActive { get; set; }
public virtual User User { get; set; }
public virtual Client Client { get; set; }
public virtual Team Team { get; set; }
public ClientToUser()
{
DateTime UtcNow = DateTime.UtcNow;
IsActive = true;
CreatedAt = UtcNow;
LastUpdatedAt = UtcNow;
}
[Table("BaseTeams")]
public class Team : ModelDefault
{
[MaxLength(250)]
public string Name { get; set; }
[ForeignKey("Client")]
public int ClientId { get; set; }
public bool IsActive { get; set; }
public virtual Client Client { get; set; }
public Team()
{
DateTime UtcNow = DateTime.UtcNow;
IsActive = true;
CreatedAt = UtcNow;
LastUpdatedAt = UtcNow;
}
}
But, when I try to update my databases I get the following error
Introducing FOREIGN KEY constraint
'FK_dbo.BaseTeams_dbo.BaseClients_ClientId' on table 'BaseTeams' may
cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or
ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could
not create constraint or index. See previous errors.
I am not really sure what could be causing the error but it seems it is because I am creating multiple Foreign keys to the same `Clients model.
How can I fix this error?
Hello #Mike A When I started MVC I got this error too, so you need aditional tables that connects your DB items.
So try connect your database items with tables like that:
Here is my working example:
[Table("Products")]
public class Product
{
[Key]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal InternalPrice { get; set; }
public string Url { get; set; }
}
[Table("Categories")]
public class Category
{
[Key]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string Url { get; set; }
}
[Table("ProductCategories")]
public class ProductCategory
{
[Key]
[Column(Order = 0)]
public string ProductId { get; set; }
[Key]
[Column(Order = 1)]
public string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
So you can connect your items without problems hope this will help you.

Foreign key conflict

//EDMX File
http://pastebin.com/btTCRMf7
I have 2 tables Customers and Sites
//Site
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int CustomerID { get; set; }
public int CityID { get; set; }
public int CountryID { get; set; }
public int EncodedBy { get; set; }
public System.DateTime DateEncoded { get; set; }
public virtual City City { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
public virtual User User { get; set; }
public virtual Customer Customer { get; set; }
//Customer
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int CityID { get; set; }
public int CountryID { get; set; }
public int CreditTermID { get; set; }
public int EncodedBy { get; set; }
public System.DateTime DateEncoded { get; set; }
public virtual City City { get; set; }
public virtual Country Country { get; set; }
public virtual CreditTerm CreditTerm { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
public virtual ICollection<Site> Sites { get; set; }
//Country
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<Site> Sites { get; set; }
//City
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<Site> Sites { get; set; }
//SiteModel
private static IQueryable<Site> Build(this DbSet<Site> query)
{
return query.Include("User").Include("City").Include("Country").Include("Customer");
}
public static Site Find(int siteID)
{
using (DragonRentalsEntities context = new DragonRentalsEntities(new ConfigurationManager().ConnectionString))
{
Site result = context.Sites.Build().SingleOrDefault(s => s.ID == siteID);
return result;
}
}
public static Site Update(Site _updatedSite)
{
using (DragonRentalsEntities context = new DragonRentalsEntities(new ConfigurationManager().ConnectionString))
{
context.Sites.Attach(_updatedSite);
context.Entry(_updatedSite).State = EntityState.Modified;
context.SaveChanges();
return Find(_updatedSite.ID);
}
}
Site test = SiteModel.Find(1);
test.City = null;
test.CityID = 1;
test.Country = null;
test.CountryID = 1;
test.Customer = null;
test.CustomerID = 1;
SiteModel.Update(test);
i am getting A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
however, adding test.Customer.City = null; before updating the object would work. it seems like Customer.City and Site.City is conflicting. can somebody explain why? or any workaround?
I can explain why. Include persists entity to load all the include objects.So our site object has all references to your clusses (City, Country,User Customer). I think that is the problem. The solve can be to load only site object:
Site result = context.Sites.SingleOrDefault(s => s.ID == siteID);
so it would load only ids of the site object. Than you can load refered objects by it ids in runtime where you need.
I think it is because when you use include, you operate objects and collections of objects dbContext tracks this changes and saves them when you call
context.SaveChanges();
a bit refactored code btw:
public static Site Update(Site _updatedSite)
{
using (DragonRentalsEntities context = new DragonRentalsEntities(new ConfigurationManager().ConnectionString))
{
if (context.Entry(_updatedSite).State == EntityState.Detached)
context.Entry(_updatedSite).State = EntityState.Modified;// attaches entity and marks it as modified if it is detached
context.SaveChanges();
return _updatedSite; //after save changes u have the same object as u send in your Update function
}
}
Comment answer
Slauma if i don't set it to null, i won't be able to attach them in the update method, the same error would be triggered
Answer:
Because when you include all entities you have got already attached to your context objects.
Btw Include transforms on sql inner join statement so may be your snapshot of db objects doesnt contain City with that ID.

Categories

Resources