This question already has answers here:
Entity Framework - Include Multiple Levels of Properties
(10 answers)
Closed 6 years ago.
I have three Entity classes.
public partial class Person
{
public Person()
{
this.Locations = new HashSet<Location>();
}
public int PersonID { get; set; }
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
public partial class Location
{
public Location()
{
this.People = new HashSet<Person>();
}
public int LocationID { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public int CityID { get; set; }
public virtual City City { get; set; }
public virtual ICollection<Person> People { get; set; }
}
public partial class City
{
public City()
{
this.Locations = new HashSet<Location>();
}
public int CityID { get; set; }
public string Name { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
I am trying to query my entities and get all the locations of a given person.
So far i have this method.
public IQueryable<Person> GetLocationsForPerson(int id)
{
return context.People
.Include(p => p.Locations)
.Where(p => p.PersonID == id);
}
which is working fine.The problem is that i want to get the name of the city for each location too. while i am getting the cityID from Location table, the City property of Location entity is returning null. Why is that null? and how can i modify my query to get the City Name? Any hint would be greatly appreciated.
Replace:
.Include(p => p.Locations)
With:
.Include(p => p.Locations.Select(l => l.City))
In EF Core you also do:
.Include(p => p.Locations)
.ThenInclude(l => l.City)
Related
I have 2 entities configured using Entity Framework 6.
Both entities have Identity on for generating primary keys on insert.
When i try adding new customer I get following error.
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'.
My assumption is that I did not configured one of the entities properly for one to one relationships.
public class Customer : IdEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Guid? BalanceId { get; set; }
public DateTime? Dob { get; set; }
public DateTime CreateDate { get; set; }
public CustomerBalance Balance { get; set; }
public Address Address { get; set; }
public virtual ICollection<Email> Emails { get; set; } = new List<Email>();
public virtual ICollection<Phone> Phones { get; set; } = new List<Phone>();
public virtual ICollection<Reward> Rewards { get; set; }
public ICollection<Call> Calls { get; set; }
}
Here is mapping for Customer
public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration()
{
ToTable(nameof(Customer));
HasKey(x => x.Id).Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.Phones);
HasMany(x => x.Emails);
HasOptional(x => x.Balance);
HasRequired(x => x.Address).WithRequiredDependent(x=>x.Customer);
}
}
Here is Address Entity
public class Address : IdEntity
{
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
And mapping
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
ToTable(nameof(Address));
HasKey(x => x.Id, e => e.IsClustered())
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
Here is a code how i insert new customer
var customer = new Customer
{
FirstName = request.FirstName,
LastName = request.LastName,
CreateDate = DateTime.Now,
Address = new Address()
};
if (!string.IsNullOrEmpty(request.Email))
customer.Emails.Add(new Email { EmailName = request.Email, IsPrimary = true });
if (!string.IsNullOrEmpty(request.Phone))
customer.Phones.Add(new Phone { Number = request.Phone, IsPrimary = true });
_repository.AddOneAsync(customer);
await _repository.Context.SaveChangesAsync();
Error is happening on save changes.
Address and Customer are one to one relationship.
Here is how my tables are configured at SQL
https://ibb.co/cYYphbJ
https://ibb.co/LnXcsyB
One-to-One relationships in EF6 must use the same keys. EG Address.CustomerId would have to be its key.
Either allow customers to have multiple addresses in the model, or change the key of Address to be CustomerID.
I have a database with thee tables - Organisations, Locations and OrganisationLocations for example. Organisations holds information on companies, Locations holds information on offices address, and OrganisationLocations holds the many-to-many relationship info about the companies and their addresses. For example Company A might be locations X and Y, Company B might be in locations Y and Z.
These are the generated classes
public partial class Locations
{
public Locations()
{
OrganisationLocations = new HashSet<OrganisationLocations>();
}
public long Id { get; set; }
public string Description { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string PostCode { get; set; }
public string Name { get; set; }
public virtual ICollection<OrganisationLocations> OrganisationLocations { get; set; }
}
public partial class Organisations
{
public Organisations()
{
OrganisationLocations = new HashSet<OrganisationLocations>();
}
public long Id { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
public string ContactName { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
public virtual ICollection<OrganisationLocations> OrganisationLocations { get; set; }
}
public partial class OrganisationLocations
{
public long Id { get; set; }
public long OrganisationId { get; set; }
public long LocationId { get; set; }
public virtual Locations Location { get; set; }
public virtual Organisations Organisation { get; set; }
}
The DB Context looks like this:
public class PfApiContext : DbContext
{
public PfApiContext(DbContextOptions<PaymentAPIContext> options)
: base(options)
{
}
public DbSet<Locations> Locations { get; set; }
public DbSet<Organisations> Organisations { get; set; }
public virtual DbSet<OrganisationLocations> OrganisationLocations { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrganisationLocations>(entity =>
{
entity.HasOne(d => d.Location)
.WithMany(p => p.OrganisationLocations)
.HasForeignKey(d => d.LocationId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_84m7dbl1gv1p6tg1wquwm8j5u");
entity.HasOne(d => d.Organisation)
.WithMany(p => p.OrganisationLocations)
.HasForeignKey(d => d.OrganisationId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_r0mkkndb6c2tr9nl0rgjm068t");
});
}
}
How would I get, for example, all the company data for all the companies in Location X? At the point I'm querying this I actually know the LocationID, so the SQL would be
SELECT * FROM [dbo].[Organisations] WHERE Id IN (SELECT [OrganisationId] FROM [dbo].[OrganisationLocations] WHERE [LocationId] = 1)
This is probably really simple and I'm just being stupid, but I'm new to EFCore and LINQ, and the syntax of this has me scratching my head.
Selecting locations based on a location name I can understand
var locationUsers = await _pfApiContext.UserLocations.Where
(o => o.LocationName == locationName).ToListAsync();
but this is confusing me something terrible.
Thanks for any help
I would remove the virtual collections and objects from all three classes and then remove the Id field (assuming it's the primary key) in the OrganisationLocations table. The OrganisationId and the LocationId fields in the OrganisationLocations should be both the primary keys and the foreign keys in order to make it a many-to-many relationship between OrganisationLocations, Locations, and Organisation. Then you can use Linq to do a simple join query to get the info you're looking for. For example:
var orgs = (from ol in PfApiContext.OrganisationLocations
inner join o in PfApiContext.Organisation on o.Id equals ol.OrganisationId
inner join l in PfApiContext.Locations on ol.LocationId equals l.Id
where ol.LocationId = 1
select new Organisation()
{
Id = o.Id,
Name = o.Name,
Owner = o.Ower,
etc...
}
This the table structure I have:
#region Tables
public class WorkoutProfile
{
public WorkoutProfile()
{
WorkoutExercises = new List<WorkoutExercise>();
}
[Key]
public int ProfileId { get; set; }
public string Name { get; set; }
public int Sets { get; set; }
public int RestAfterSetInSeconds { get; set; }
public virtual User User { get; set; }
public virtual ICollection<WorkoutExercise> WorkoutExercises { get; set; }
}
public class WorkoutExercise
{
[Key]
public int WorkoutId { get; set; }
public virtual Exercise Exercise { get; set; }
public int Order { get; set; }
public int WorkoutTimeInSeconds { get; set; }
public int RestAfterInSeconds { get; set; }
}
public class Exercise
{
[Key]
public long ExerciseId { get; set; }
public string Title { get; set; }
public string Visualisation { get; set; }
public bool IsDefault { get; set; } // Is exersice should be included when user first registers
}
public class User
{
[Key]
public long UserId { get; set; }
public string Email { get; set; }
public DateTime Registered { get; set; }
}
#endregion Tables
In the repository class I run the following linq query:
return context
.WorkoutProfiles.Include(w => w.WorkoutExercises)
.Where(q => q.User.UserId == userId && q.ProfileId == profileId)
.FirstOrDefault();
and I receive the good and old "Object reference not set to an instance of an object". When examining the result, see that Exercises property in WorkoutExercises is null.
This is how the database is created using code first approach:
So, the question is: why Exercises not included in WorkoutExercises object? Do I need to include it somehow? I am using .NET Core 2
The simple answer would be no lazy loading in EFCore. Not Released yet but if you want to dabble with alpha code, its in the repository. Based on your classes there are no collections for exercises in WorkoutExcercise.
Then you need to ThenInclude(w => w.Exercises) following your Include clause since EFCore doesn't do lazy loading.
I found a solution following this post
Altered my code as following:
var top = context
.Set<WorkoutProfile>()
.Where(q => q.ProfileId == profileId && q.User.UserId == userId)
.Include(q => q.WorkoutExercises)
.SingleOrDefault();
context
.Entry(top)
.Collection(e => e.WorkoutExercises)
.Query()
.OfType<WorkoutExercise>()
.Include(e => e.Exercise)
.Load();
And it worked
Is it possible to create 2 M:M relationships using the same join table?
I have the following situation and am receiving the exception:
Unhandled Exception: System.InvalidOperationException: Cannot create a relationship between 'ApplicationUser.ExpertTags' and 'UserTag.User', because there already is a relationship between 'ApplicationUser.StudyTags' and 'UserTag.User'. Navigation properties can only participate in a single relationship
In Tag:
public class Tag {
public Tag() {
Users = new List<UserTag>();
}
public int TagId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<UserTag> Users { get; set; }
In ApplicationUser:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
StudyTags = new HashSet<UserTag>();
ExpertTags = new HashSet<UserTag>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public ICollection<UserTag> StudyTags { get; set; }
public ICollection<UserTag> ExpertTags { get; set; }
}
In UserTag (CLR join):
public class UserTag
{
public string UserId { get; set; }
public ApplicationUser User { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
In ApplicationDbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserTag>()
.HasKey(x => new { x.UserId, x.TagId });
modelBuilder.Entity<UserTag>()
.HasOne(ut => ut.User)
.WithMany(u => u.StudyTags)
.HasForeignKey(ut => ut.UserId);
modelBuilder.Entity<UserTag>()
.HasOne(ut => ut.User)
.WithMany(u => u.ExpertTags)
.HasForeignKey(ut => ut.UserId);
modelBuilder.Entity<UserTag>()
.HasOne(ut => ut.Tag)
.WithMany(t => t.Users)
.HasForeignKey(ut => ut.TagId);
}
Do I need to create separate CLR classes? Something like UserStudyTag and UserExpertTag?
Thanks!
Step down to SQL DB. You want to have table UserTag with one UserId field. How EF should guess, which records in this table are related to StudyTags and which to ExpertTags collections?
You should duplicate something.
Either split UserTag to two tables (UserStudyTag and UserExpertTag), or make two UserId fields in UserTag, say ExpertUserId and StudyUserId. Both nullable, with only one having some value in each record.
I have the following two classes:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
}
public class Trip
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Person> Persons { get; set; }
}
As you can see, a Trip can have 1 or more Persons...
I tried to use the EntityConfiguration to build the database properly but I cannot manage to make it work... I am quite confused on its usage:
public class TripConfiguration : EntityTypeConfiguration<Trip>
{
internal TripConfiguration()
{
// ???
}
}
What do I need to write to have the application to behave properly:
I need at least one person.
I might have more that one person
A person cannot be in the SAME trip twice
A person can be in more than one trip
Try this:
this.HasRequired(x => x.Person)
.WithMany(x => x.Trips)
.HasForeignKey(x => x.PersonId);
Your classes:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Trip> Trips { get; set;}
}
public class Trip
{
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
And as far that I know, EF doesn't support unique FK (or correct me if I'm wrong..). So you have to check it yourself.
This is not a One-To-Many relationship, this is a Many-To-Many relationship, you need to have collections on both sides of the relationship. EF will create the joiner table on your behalf. Since today you cannot configure a person being in a trip only once you will need to create a unique constraint in your joiner table once is created to assure this happens since EF does not yet support Unique Key constraints through configuration.
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Trip> Trips { get; set; }
}
public class Trip
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
then
class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
this.HasMany(t => t.Trips).WithMany(t => t.Persons);
}
}