Here there are my domain entities:
public class Province
{
private ICollection<City> _cities;
public virtual ICollection<City> Cities
{
get { return _cities ?? (_cities = new HashSet<City>()); }
set { _cities = value; }
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual double Latitude { get; set; }
public virtual double Longitude { get; set; }
}
public class City
{
private Province _province;
public virtual Province Province
{
get { return _province ?? (_province = new Province()); }
set { _province = value; }
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Latitude { get; set; }
public virtual string Longitude { get; set; }
}
Mappings:
public class ProvinceMap : EntityTypeConfiguration<Province>
{
public ProvinceMap()
{
this.ToTable("Province");
this.HasKey(p => p.Id);
this.Property(x => x.Id).HasColumnName("Id");
this.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(x => x.Name).HasMaxLength(50).IsRequired();
this.Property(x => x.Latitude).IsRequired();
this.Property(x => x.Longitude).IsRequired();
//this.HasMany(x => x.Cities)
// .WithRequired(x => x.Province)
// .HasForeignKey(x => x.Id);
}
}
public class CityMap : EntityTypeConfiguration<City>
{
public CityMap()
{
this.ToTable("City");
this.HasKey(x => x.Id);
this.Property(x => x.Id).HasColumnName("Id");
this.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(x => x.Name).HasMaxLength(50).IsRequired();
this.Property(x => x.Latitude).IsRequired();
this.Property(x => x.Longitude).IsRequired();
this.HasRequired(x => x.Province)
.WithMany(x => x.Cities)
.HasForeignKey(x => x.Id);
}
}
Context:
public class DataContext : DbContext
{
public DataContext(): base("DataContext")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DataContext, Configuration>("DataContext"));
}
public DbSet<Province> Provinces { get; set; }
public DbSet<City> Cities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ProvinceMap());
modelBuilder.Configurations.Add(new CityMap());
//base.OnModelCreating(modelBuilder);
}
}
When I run the 'update-database' command at the Nuget Package Console, I have an error:
Invalid multiplicity in the element Role "City_Province_Source" in connection "City_Province". Because the Dependent Role refers to the key properties, the upper bound of the multiplicity properties Dependent Role must be equal to "1".
Logically, you are trying to define a 1-to-many relationship. Because City cannot be in many Provinces, and one Province can have many Cities.
In this case, you don't necessarily need to specify HasRequired and WithMany in your mapping.
Remove the following code from CityMap
this.HasRequired(x => x.Province)
.WithMany(x => x.Cities)
.HasForeignKey(x => x.Id);
Having ICollection<City> in Province table, and a property type of Province in City table is enough to establish the relationship.
The output will be like this.
Related
I have 2 entities connected by a many to many relationship.
First class:
public class ArticleCategory
{
public int Id {get; set; }
public string MainCategoryName { get; set; }
public List<ArticleCategorySubcategory> ArticleCategorySubcategories { get; set; } = new List<ArticleCategorySubcategory>();
public bool IsActive { get; set; }
}
Second class:
public class ArticleSubcategory
{
public int Id { get; set; }
public string SubcategoryName { get; set; }
public List<ArticleCategorySubcategory> ArticleCategorySubcategories { get; set; } = new List<ArticleCategorySubcategory>();
}
And relationship (many to many):
public class ArticleCategorySubcategory : BaseHistoryEntity
{
public int Id { get; set; }
public int ArticleCategoryId { get; set; }
public ArticleCategory ArticleCategory { get; set; }
public int ArticleSubcategoryId { get; set; }
public ArticleSubcategory ArticleSubcategory {get; set;}
}
And I have also 1 DTO:
public class ArticleCategoryResult
{
public string CategoryName { get; set; }
public List<string> Subcategories { get; set; }
public bool IsActive { get; set; }
}
I want to use AutoMapper to list the names of subcategories. I tried something like this but I get an empty list.
My Automapper code:
CreateMap<ArticleCategory, ArticleCategoryResult>()
.ForMember(dst => dst.CategoryName, opt => opt.MapFrom(src => src.MainCategoryName))
.ForMember(dst => dst.IsActive, opt => opt.MapFrom(src => src.IsActive))
.ForMember(dst => dst.Subcategories, src => src.MapFrom(mbr => mbr.ArticleCategorySubcategories.Select(x => x.ArticleSubcategory.SubcategoryName)));
Result on view as json:
{
"categoryName": "Example category 6",
"subcategories": [],
"isActive": true
}
This is what my configuration looks like for these tables:
public void Configure(EntityTypeBuilder<ArticleCategorySubcategory> builder)
{
builder
.HasKey(x => x.Id);
builder
.HasOne(x => x.ArticleCategory)
.WithMany(x => x.ArticleCategorySubcategories)
.HasForeignKey(x => x.ArticleCategoryId);
builder
.HasOne(x => x.ArticleSubcategory)
.WithMany(x => x.ArticleCategorySubcategories)
.HasForeignKey(x => x.ArticleSubcategoryId);
}
How can I list the names of subcategories using AutoMapper?
You need to add .Include(x=>x.ArticleSubcategory) before your .Select(.. as the related objects are not tracked and are treated as undefined.
I'm trying to achieve the following layout:
User table (has link to user details)
User details table (holds links to various detail tables)
but am getting the following error:
System.InvalidOperationException: Cannot create a relationship between 'Address.ClientDetails' and 'ClientDetails.ResidentialAddress', because there already is a relationship between 'ClientDetails.PostalAddress' and 'Address.ClientDetails'. Navigation properties can only participate in a single relationship.
I understand this problem would occur if entity framework had no way to identify which address to link to each address - but i thought i take care of that by specifying 2 links in the model and then each key map in the mapping class. Any help would be great!
my Client model and mapping looks as follows:
public class Client : BaseEntity
{
public ClientDetails ApplicantDetails
{
get
{
return this.ClientDetails.SingleOrDefault(e => e.ClientType == Enums.ClientType.Applicant.ToString());
}
}
public ClientDetails SpouseDetails
{
get
{
return this.ClientDetails.SingleOrDefault(e => e.ClientType == Enums.ClientType.Spouse.ToString());
}
}
public ICollection<ClientDetails> ClientDetails { get; set; }
public ICollection<BankDetails> BankDetails { get; set; }
public ICollection<Expenses> Expenses { get; set; }
public ICollection<Obligation> Obligations { get; set; }
public ICollection<Budget> Budgets { get; set; }
public ICollection<Document.Document> Documents { get; set; }
public virtual Workflow.Workflow Workflow { get; set; }
Mapping
public class ClientMapping: IEntityTypeConfiguration<Entities.Client.Client>
{
public void Configure(EntityTypeBuilder<Entities.Client.Client> builder)
{
builder.ToTable("Client");
builder.HasKey(e => e.Id);
builder.HasMany(e => e.ClientDetails).WithOne(e => e.Client).HasForeignKey(e => e.ClientId);
builder.HasMany(e => e.Documents).WithOne(e => e.Client).HasForeignKey(e => e.ClientId);
builder.HasOne(e => e.Workflow).WithOne(e => e.Client).HasForeignKey<Entities.Workflow.Workflow>(e => e.ClientId);
builder.HasMany(e => e.Obligations).WithOne(e => e.Client).HasForeignKey(e => e.ClientId);
builder.HasMany(e => e.Expenses).WithOne(e => e.Client).HasForeignKey(e => e.ClientId);
builder.HasMany(e => e.Budgets).WithOne(e => e.Client).HasForeignKey(e => e.ClientId);
builder.HasMany(e => e.BankDetails).WithOne(e => e.Client).HasForeignKey(e => e.ClientId);
builder.Ignore(e => e.ApplicantDetails);
builder.Ignore(e => e.SpouseDetails);
}
}
Client Details and mapping
public class ClientDetails
{
public int ClientId { get; set; }
public int PersonalDetailsId { get; set; }
public int EmployerId { get; set; }
public int ResidentialAddressId { get; set; }
public int PostalAddressId { get; set; }
public int IncomeId { get; set; }
public string ClientType { get; set; }
public virtual Client Client { get; set; }
public virtual PersonalDetails PersonalDetails { get; set; }
public virtual Employer Employer { get; set; }
public virtual Address ResidentialAddress { get; set; }
public virtual Address PostalAddress { get; set; }
public virtual Income Income { get; set; }
}
mapping
public class ClientDetailsMapping : IEntityTypeConfiguration<Entities.Client.ClientDetails>
{
public void Configure(EntityTypeBuilder<ClientDetails> builder)
{
builder.ToTable("ClientDetails");
builder.HasKey(e => new { e.IncomeId, e.PersonalDetailsId, e.ClientId, e.PostalAddressId, e.ResidentialAddressId } );
builder.HasOne(e => e.Income).WithOne(e => e.ClientDetails).HasForeignKey<ClientDetails>(e => e.IncomeId);
builder.HasOne(e => e.PostalAddress).WithOne(e => e.ClientDetails).HasForeignKey<ClientDetails>(e => e.PostalAddressId);
builder.HasOne(e => e.ResidentialAddress).WithOne(e => e.ClientDetails).HasForeignKey<ClientDetails>(e => e.ResidentialAddressId);
builder.HasOne(e => e.Employer).WithOne(e => e.ClientDetails).HasForeignKey<ClientDetails>(e => e.EmployerId);
builder.HasOne(e => e.PersonalDetails).WithOne(e => e.ClientDetails).HasForeignKey<ClientDetails>(e => e.PersonalDetailsId);
}
}
Can you try to delete the content of "WithOne"?
try this:
builder.HasOne(e => e.PostalAddress).WithOne().HasForeignKey<ClientDetails>(e => e.PostalAddressId);
builder.HasOne(e => e.ResidentialAddress).WithOne().HasForeignKey<ClientDetails>(e => e.ResidentialAddressId);
Found this post:
ef core - two one to one on one principal key
Ended up implementing solution 3, client details now has a collection of addresses (which have an address type linked), I then added an address getter on the client details to get the address I want at a later time. Everything seems to work correctly now.
I tried to apply TPH inheritance strategy on my Domain with Code First and FluentApi.
I have the following domain model:
public abstract class Entity
{
public Guid Id { get; set; }
public byte [] RowVersion { get; set; }
}
public class Employee : Entity
{
#region POCO Fields
public string EmployeeId { get; set; }
//Complex Types
public PrimaryPersonalData PrimaryPersonalData { get; set; }
public ContactPersonalData ContactPersonalData { get; set; }
#endregion
#region Navigations
public ICollection<EmployeePosition> EmployeeCurrentPositions { get; set; }
public ICollection<EmployeeDegree> EmployeeCurrentDegrees { get; set; }
public ICollection<EmployeeRole> EmployeeCurrentRoles { get; set; }
public ICollection<Department> AdministrationDepartments { get; set; }
public Guid? FacultyId { get; set; }
public Faculty Faculty { get; set; }
public Guid? SectorId { get; set; }
public Sector Sector { get; set; }
public Guid? AcademicDepartmentId { get; set; }
public Department AcademicDepartment { get; set; }
#endregion
#region Constructors
public Employee()
{
EmployeeCurrentPositions = new List<EmployeePosition>();
EmployeeCurrentDegrees = new List<EmployeeDegree>();
EmployeeCurrentRoles = new List<EmployeeRole>();
AdministrationDepartments = new List<Department>();
PrimaryPersonalData = new PrimaryPersonalData();
ContactPersonalData = new ContactPersonalData();
}
#endregion
}
public class Teacher : Employee
{
public ICollection<Subject> ReadeableSubjects { get; set; }
public Teacher()
{
ReadeableSubjects = new List<Subject>();
}
}
And configurations goes separately :
public abstract class EntityConfiguration<T> : EntityTypeConfiguration<T>
where T : Entity
{
protected EntityConfiguration()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.RowVersion).IsRowVersion();
}
}
public class EmployeeConfiguration : EntityConfiguration<Employee>
{
public EmployeeConfiguration()
{
#region POCO Configuration
Property(p=>p.EmployeeId)
.IsRequired()
.IsConcurrencyToken(true);
#endregion
#region Mapping Configuration
HasMany(p => p.EmployeeCurrentPositions)
.WithMany(p => p.TakingEmployees)
.Map(p =>
{
p.MapLeftKey("EmployeeId");
p.MapRightKey("PositionId");
p.ToTable("EmployeePosition");
});
HasMany(p => p.EmployeeCurrentDegrees)
.WithMany(p => p.TakingEmployees)
.Map(p =>
{
p.MapLeftKey("EmployeeId");
p.MapRightKey("DegreeId");
p.ToTable("EmployeeDegree");
});
HasMany(p => p.EmployeeCurrentRoles)
.WithMany(p => p.TakingEmployees)
.Map(p =>
{
p.MapLeftKey("EmployeeId");
p.MapRightKey("RoleId");
p.ToTable("EmployeeRole");
});
#endregion
}
public class TeacherConfiguration : EntityConfiguration<Teacher>
{
// I suppose problem is hidden here. TeacherConfiguration inherits from
// EntityConfiguration and may miss configuration of Employee. Am I right?
public TeacherConfiguration()
{
HasMany(p => p.ReadeableSubjects).WithMany(p => p.ReadeableTeachers).Map(m =>
{
m.MapLeftKey("TeacherId");
m.MapRightKey("SubjectId");
m.ToTable("TeacherSubject");
});
}
}
And the last piece - Faculty class that consists of both Employees and Teachers.
public class Faculty : Entity
{
#region POCO Fields
public string Acronym { get; set; }
public string FacultyNameEn { get; set; }
public string FacultyNameRu { get; set; }
public string FacultyDescriptionEn { get; set; }
public string FacultyDescriptionRu { get; set; }
#endregion
#region Navigations
public ICollection<Department> Departments { get; set; }
public ICollection<Employee> Employees { get; set; }
public ICollection<Teacher> Teachers { get; set; }
public ICollection<Student> Students { get; set; }
public ICollection<Subject> Subjects { get; set; }
public ICollection<Specialty> Specialties { get; set; }
#endregion
#region Constructors
public Faculty(string facultyAcronym, string facultyNameEn, string facultyNameRu,
string facultyDescriptionEn, string facultyDescriptionRu)
: this()
{
Acronym = facultyAcronym;
FacultyNameEn = facultyNameEn;
FacultyNameRu = facultyNameRu;
FacultyDescriptionEn = facultyDescriptionEn;
FacultyDescriptionRu = facultyDescriptionRu;
}
public Faculty()
{
Departments = new List<Department>();
Employees = new List<Employee>();
Students = new List<Student>();
Subjects = new List<Subject>();
Specialties = new List<Specialty>();
Teachers = new List<Teacher>();
}
#endregion
}
public class FacultyConfiguration : EntityConfiguration<Faculty>
{
public FacultyConfiguration()
{
#region POCO Configurations
Property(p => p.Acronym)
.IsRequired()
.HasMaxLength(10);
Property(p => p.FacultyNameEn)
.IsRequired()
.HasMaxLength(100);
Property(p => p.FacultyNameRu)
.IsRequired()
.HasMaxLength(100);
Property(p => p.FacultyDescriptionEn)
.IsRequired()
.HasMaxLength(1000);
Property(p => p.FacultyDescriptionRu)
.IsRequired()
.HasMaxLength(1000);
#endregion
#region Mapping Configurations
HasMany(p => p.Departments)
.WithRequired(p => p.Faculty)
.HasForeignKey(p => p.FacultyId);
HasMany(p => p.Employees)
.WithOptional(p => p.Faculty)
.HasForeignKey(p => p.FacultyId);
HasMany(p => p.Teachers)
.WithOptional(p => p.Faculty)
.HasForeignKey(p => p.FacultyId);
HasMany(p => p.Students)
.WithRequired(p => p.Faculty)
.HasForeignKey(p => p.FacultyId);
HasMany(p => p.Subjects)
.WithRequired(p => p.Faculty)
.HasForeignKey(p => p.FacultyId);
HasMany(p => p.Specialties)
.WithRequired(p => p.Faculty)
.HasForeignKey(p => p.FacultyId);
#endregion
}
}
And finally I got an exception :
Additional information: The foreign key component 'FacultyId' is not a declared property on type 'Teacher'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
I found similar question here but there is a bit different situation. I think problem is that TeacherConfiguration inherit from EntityConfiguration but not from EmployeeConfiguration. Could you help me to find what is wrong here?
I'm having issues using Entity Framework (6.3) to retrieve a child collection of entities where the relationship uses a composite key. In the example below I'm trying to get the Sprints associated with a Plan, but the Sprints child collection keeps coming back empty.
// Returns no sprints
var queryUsingSelect = await _dbContext
.Plans
.Select(p => new
{
p,
p.Sprints
})
.ToListAsync();
// Returns a plan without sprints
var queryUsingInclude = await _dbContext
.Plans
.Include(p => p.Sprints)
.ToListAsync();
// Returns me all sprints
var allSprints = await _dbContext
.Plans
.SelectMany(p => p.Sprints)
.ToListAsync();
In the last query I've tested it using SelectMany which does return Sprints, but really I need to be able to do it using Include. I'm having the same issue with another collection in the same project so it seems to be an issue with my approach in general. Note that I have lazy loading turned off to prevent accidental n+1 queries.
Here's a stripped down version of my code:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
}
public class Aspiration
{
public int AspirationId { get; set; }
public string Title { get; set; }
}
public class Plan
{
public Plan()
{
Sprints = new List<Sprint>();
}
public int UserId { get; set; }
public int AspirationId { get; set; }
public virtual User User { get; set; }
public virtual Aspiration Aspiration { get; set; }
public virtual ICollection<Sprint> Sprints { get; set; }
}
public class Sprint
{
public int SprintId { get; set; }
public int UserId { get; set; }
public int AspirationId { get; set; }
public virtual Plan Plan { get; set; }
public virtual User User { get; set; }
public virtual Aspiration Aspiration { get; set; }
}
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
Property(t => t.Name)
.HasMaxLength(100)
.IsRequired();
}
}
public class AspirationMap : EntityTypeConfiguration<Aspiration>
{
public AspirationMap()
{
Property(t => t.Title)
.HasMaxLength(100)
.IsRequired();
}
}
public class PlanMap : EntityTypeConfiguration<Plan>
{
public PlanMap()
{
HasKey(s => new { s.UserId, s.AspirationId });
HasRequired(s => s.User)
.WithMany()
.HasForeignKey(s => s.UserId);
HasRequired(s => s.Aspiration)
.WithMany()
.HasForeignKey(s => s.AspirationId);
}
}
public class SprintMap : EntityTypeConfiguration<Sprint>
{
public SprintMap()
{
HasRequired(s => s.User)
.WithMany()
.HasForeignKey(s => s.UserId);
HasRequired(s => s.Aspiration)
.WithMany()
.HasForeignKey(s => s.AspirationId);
HasRequired(s => s.Plan)
.WithMany(d => d.Sprints)
.HasForeignKey(s => new { s.AspirationId, s.UserId });
}
}
public class MyDbContext : DbContext
{
static MyDbContext()
{
Database.SetInitializer<MyDbContext>(null);
}
public MyDbContext()
: base(DbConstants.ConnectionStringName)
{
Configuration.LazyLoadingEnabled = false;
}
public DbSet<User> Users { get; set; }
public DbSet<Aspiration> Aspirations { get; set; }
public DbSet<Plan> Plans { get; set; }
public DbSet<Sprint> Sprints { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder
.Map(new UserMap())
.Map(new AspirationMap())
.Map(new PlanMap())
.Map(new SprintMap())
;
}
}
Well, I see some errors in your mapping.
The FK of Plan in Sprint must have the same order of the PK of Plan. So replace this:
HasRequired(s => s.Plan)
.WithMany(d => d.Sprints)
.HasForeignKey(s => new { s.AspirationId,s.UserId });
for this:
HasRequired(s => s.Plan)
.WithMany(d => d.Sprints)
.HasForeignKey(s => new { s.UserId, s.AspirationId });
After making those changes, I tried to run your code and everything worked fine.
I have a problem when selecting a User.
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap ()
{
ToTable("USERS");
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("ID");
Property(e => e.Name).HasColumnName("NAME");
Property(e => e.Password).HasColumnName("PASSWORD");
Property(e => e.Date).HasColumnName("DATE");
Property(e => e.Token).HasColumnName("TOKEN");
Property(e => e.Active).HasColumnName("ACTIVE");
HasRequired(e => e.Company).WithMany().Map(e => e.MapKey("COMPANY_ID"));
HasMany(e => e.BranchesUsers).WithRequired().Map(e => e.MapKey("USER_ID"));
}
}
public class BranchMap : EntityTypeConfiguration<Branch>
{
public BranchMap ()
{
ToTable("BRANCHES");
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("ID");
Property(e => e.Name).HasColumnName("NAME");
Property(e => e.Date).HasColumnName("DATE");
Property(e => e.Active).HasColumnName("ACTIVE");
HasRequired(e => e.Company).WithMany().Map(e => e.MapKey("COMPANY_ID"));
HasMany(e => e.UsersBranches).WithRequired().Map(e => e.MapKey("BRANCH_ID"));
}
}
public class UserBranchMap : EntityTypeConfiguration<UserBranch>
{
public UserBranchMap()
{
ToTable("USERS_BRANCHES");
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("ID");
HasOptional(e => e.User).WithMany().Map(e => e.MapKey("USER_ID"));
HasOptional(e => e.Profile).WithMany().Map(e => e.MapKey("PROFILE_ID"));
HasOptional(e => e.Branch).WithMany().Map(e => e.MapKey("BRANCH_ID"));
HasOptional(e => e.Company).WithMany().Map(e => e.MapKey("COMPANY_ID"));
}
}
this is my model:
public class User
{
public long Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public DateTime Date { get; set; }
public string Token { get; set; }
public Company Company { get; set; }
public List<UserBranch> BranchesUsers{ get; set; }
public bool Active{ get; set; }
}
public class Branch
{
public long Id { get; set; }
public Company Company{ get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public List<UserBranch> UsersBranches { get; set; }
public bool Active { get; set; }
}
public class UserBranch
{
public long Id { get; set; }
public User User { get; set; }
public Profile Profile { get; set; }
public Branch Branch { get; set; }
public Company Company { get; set; }
}
when I perform a simple select the user model I get this error:
One or more validation errors were detected during model generation:
USER_ID: Name: Each property name in a type must be unique. Property name 'USER_ID' is already defined.
BRANCH_ID: Name: Each property name in a type must be unique. Property name 'BRANCH_ID' is already defined.