Is it possible to have a different schema name on correlation tabels than [dbo]?
I'm using code first.
Example:
ApplicationRole.cs
public class ApplicationRole
{
public Guid ApplicationRoleId { get; set; }
public string Name { get; set; }
public virtual ICollection<ADGroup> ADGroups { get; set; }
}
ADGroup.cs
public class ADGroup
{
public Guid ADGroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicationRole> ApplicationRoles { get; set; }
}
ApplicationRoleConfiguration.cs
public class ApplicationRoleConfiguration : EntityTypeConfiguration<ApplicationRole>
{
public ApplicationRoleConfiguration()
{
ToTable("T_ApplicationRoles", "LabConfig");
this.HasKey(a => a.ApplicationRoleId);
this.Property(t => t.ApplicationRoleId)
.HasColumnName("ApplicationRole_GUID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
ADGroupConfiguration.cs
public class ADGroupConfiguration : EntityTypeConfiguration<ADGroup>
{
public ADGroupConfiguration()
{
ToTable("T_ADGroups", "LabConfig");
this.HasKey(a => a.ADGroupId);
this.Property(t => t.ADGroupId)
.HasColumnName("ADGroup_GUID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// correlation table should also get schema [LabConfig]
this.HasMany(o => o.ApplicationRoles)
.WithMany(r => r.ADGroups)
.Map(m =>
{
m.MapLeftKey("ADGroup_GUID");
m.MapRightKey("ApplicationRole_GUID");
ToTable("T_ApplicationRoleADGroups", "LabConfig");
});
}
}
But the result on the database is always:
[LabConfig].[T_ADGroups]
[LabConfig].[T_ApplicationRoles]
[dbo].[ApplicationRoleADGroups]
Any ideas? I spent hours for this to work with my desired schema without any success.
In my case i did something stupid i didn't see...
Compare the original ADGroupConfiguration.cs with this:
public class ADGroupConfiguration : EntityTypeConfiguration<ADGroup>
{
public ADGroupConfiguration()
{
ToTable("T_ADGroups", "LabConfig");
this.HasKey(a => a.ADGroupId);
this.Property(a => a.ADGroupId)
.HasColumnName("ADGroup_GUID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(t => t.Name).HasColumnName("Name").IsRequired();
// the Mapping was causing the error.
// this Mapping is correct now
this.HasMany(o => o.ApplicationRoles)
.WithMany(r => r.ADGroups)
.Map(m => m.MapLeftKey("ADGroup_GUID")
.MapRightKey("ApplicationRole_GUID")
.ToTable("T_ApplicationRoleADGroups", "LabConfig"));
}
}
Hence my mapping had an error it was hiding the real problem i was searching for...
Always double check the mappings!
Related
I have been getting error when I try to run add-migration script.
The error is:
Domain.DataAccessLayer.AllRoutines_Product: : Multiplicity conflicts with the referential constraint in Role 'AllRoutines_Product_Target' in relationship 'AllRoutines_Product'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
And I cannot figure what I am doing wrong. I have AllRoutines and Product entities. AllRoutines can have 0 or 1 Products. Here is my AllRoutines class (some code has been omitted for clarity):
public class AllRoutines
{
[Key]
public long Id { get; set; }
public string Name { get; set; }
public string Tags { get; set; }
public int? RoutineLevelId { get; set; }
public RoutineLevel RoutineLevel { get; set; }
public Guid? ProductId { get; set; }
public virtual Product Product { get; set; }
}
Here is FluetnApi mapping (again some code is omitted):
public void MapRoutine(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RoutineLevel>().HasKey(r => r.RoutineLevelId);
modelBuilder.Entity<AllRoutines>()
.HasKey(r => r.Id)
.Property(r => r.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<AllRoutines>()
.HasOptional(r => r.RoutineLevel)
.WithMany()
.HasForeignKey(r => r.RoutineLevelId);
modelBuilder.Entity<AllRoutines>().HasOptional(c => c.Product)
.WithMany()
.HasForeignKey(c => c.ProductId)
.WillCascadeOnDelete(false);
}
Also I am not sure if this is important or not, but there is also class CustomRoutine which inherits AllRoutines and looks like this:
public class CustomRoutine : AllRoutines
{
public DateTime DateCreated { get; set; }
public string UserId { get; set; }
public User UserWhoCreatedRoutine { get; set; }
}
The inheritance approach was Table per Hierarchy.
I've tried to add to mapping configuration this:
modelBuilder.Entity<CustomRoutine>().HasOptional(c => c.Product)
.WithMany()
.HasForeignKey(c => c.ProductId)
.WillCascadeOnDelete(false);
But the error was same. I am not sure why this is happening, because, as you can see in the code same mapping was already done (without any problems) for RoutineLevel, also I have same mapping for Product and the other class, again with no problems.
EDIT
Here is also Product class:
public class Product
{
public Guid Id { get; protected set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsFree { get; set; }
public ICollection<SubscriptionProduct> SubscriptionProducts { get; set; }
}
And FluentAPI mapping:
modelBuilder.Entity<Product>()
.HasKey(p => p.Id);
modelBuilder.Entity<Product>()
.Property(p => p.Code)
.IsRequired()
.HasMaxLength(10);
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired()
.HasMaxLength(100);
modelBuilder.Entity<Product>()
.HasMany(p => p.SubscriptionProducts)
.WithRequired()
.HasForeignKey(p => p.ProductId)
.WillCascadeOnDelete(true);
Please help. I don`t understand why from my entity context
var stagesExist = context.WfwDocumentWorkStages
.Any(it => it.Enabled && it.ExecutionId == execution.Id
&& it.Level == execution.Level && it.ResultId == null);
value stagesExist is false
But
var stages = context.WfwDocumentWorkStages.Where(it => it.Enabled
&& it.ExecutionId == execution.Id
&& it.Level == execution.Level).ToList();
bool stagesExist = stages.Any(it=>it.ResultId == null);
value stagesExist is true??
Model:
public partial class WfwDocumentWorkScheme : EnabledEntity
{
public WfwDocumentWorkScheme()
{
this.WfwExecutionEvents = new List<WfwExecutionEvent>();
}
public int ExecutionId { get; set; }
public int Level { get; set; }
public int? RoleId { get; set; }
public string CoordinatorSid { get; set; }
public DateTimeOffset? Date { get; set; }
public int? ResultId { get; set; }
public string Comment { get; set; }
public virtual Employee Coordinator { get; set; }
public virtual EmployeeRole EmployeeRole { get; set; }
public virtual WfwEventResult WfwEventResult { get; set; }
public virtual WfwDocumentExecution WfwDocumentExecution { get; set; }
public virtual ICollection<WfwExecutionEvent> WfwExecutionEvents { get; set; }
}
Mapping
public class WfwDocumentWorkSchemeMap : EntityTypeConfiguration<WfwDocumentWorkScheme>
{
public WfwDocumentWorkSchemeMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.CoordinatorSid)
.HasMaxLength(46);
// Table & Column Mappings
this.ToTable("WfwDocumentWorkSchemes");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.ExecutionId).HasColumnName("ExecutionId");
this.Property(t => t.Level).HasColumnName("Level");
this.Property(t => t.RoleId).HasColumnName("RoleId");
this.Property(t => t.CoordinatorSid).HasColumnName("CoordinatorSid");
this.Property(t => t.Date).HasColumnName("Date");
this.Property(t => t.ResultId).HasColumnName("ResultId");
this.Property(t => t.Comment).HasColumnName("Comment");
this.Property(t => t.Enabled).HasColumnName("Enabled");
// Relationships
this.HasRequired(t => t.Coordinator)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.CoordinatorSid);
this.HasRequired(t => t.WfwDocumentExecution)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.ExecutionId);
this.HasRequired(t => t.WfwEventResult)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.ResultId);
this.HasOptional(t => t.EmployeeRole)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.RoleId);
}
}
Result model contains virtual List
public class WfwEventResult : EnabledEntity
{
public WfwEventResult()
{
this.WfwExecutionEvents = new List<WfwExecutionEvent>();
this.WfwDocumentWorkSchemes = new List<WfwDocumentWorkScheme>();
}
public string Name { get; set; }
public string Description { get; set; }
public bool Success { get; set; }
public virtual ICollection<WfwExecutionEvent> WfwExecutionEvents { get; set; }
public virtual ICollection<WfwDocumentWorkScheme> WfwDocumentWorkSchemes { get; set; }
}
The problem is this line in your mapping:
this.HasRequired(t => t.WfwEventResult)
You are effectively telling the EF that the associated FK column will never be null (although you've made it int? and have records with null value). Remember that EF uses metadata information when building SQL queries, and in this case I guess the query optimizer decides that this query will never return records (similar to .Where(it => false)) and generates a fake SQL query you see.
Shortly - make sure you always provide the correct information to EF. In this case, change the above to
this.HasOptional(t => t.WfwEventResult)
and you'll see a different (real) query and get a correct results.
I have an existing database that has a PK that is a numberic(18,0) and a FK that is an int. When I try to use the navigation property EF throws an invalid cast exception.
Is there a way to map this relationship that will sort out the invalid cast?
In the code below promo_cfg.pc_id is numeric(18,0) and promo.pc_id is int.
public class PromotionMap : EntityTypeConfiguration<Promotion>
{
public PromotionMap()
{
// Primary Key
this.HasKey(p => p.PromotionId);
// Properties
// table and column mappings
this.ToTable("promo");
this.Property(p => p.PromotionId).HasColumnName("p_id");
this.Property(p => p.PromotionConfigId).HasColumnName("pc_id");
this.Property(p => p.PromotionCode).HasColumnName("p_code");
this.HasRequired(t => t.PromotionConfig)
.WithMany(t => t.Promotions)
.HasForeignKey(d => new { d.PromotionConfigId });
}
}
public class Promotion
{
public decimal PromotionId { get; set; }
public int PromotionConfigId { get; set; }
public string PromotionCode { get; set; }
}
public PromotionConfigMap()
{
// Primary Key
this.HasKey(s => s.PromotionConfigId);
// Properties
// Table and Column mappings
this.ToTable("promo_cfg");
this.Property(p => p.PromotionConfigId).HasColumnName("pc_id");
this.Property(p => p.AllowOrders).HasColumnName("allow_orders");
this.HasOptional(p => p.Promotions).WithRequired().Map(x => x.MapKey("pc_id"));
}
public class PromotionConfig
{
public int PromotionConfigId { get; set; }
public int AllowOrders { get; set; }
public virtual ICollection<Promotion> Promotions { get; set; }
}
May be, with a big MAY, with a non materialized FK:
HasOptional(x => x.SomeProperty).WithMany().Map(x => x.MapKey("intDbColumnName"));
========================================================
I remove all unneeded code, the following should do:
public class Promotion
{
public decimal PromotionId { get; set; }
//Here you do not materialize the FK
public PromotionConfig PromotionConfig { get; set; }
}
public class PromotionConfig
{
public decimal PromotionConfigId { get; set; } // you can't do otherwise as
//you must declare a PK for EF
public virtual ICollection<Promotion> Promotions { get; set; }
}
public class PromotionMap : EntityTypeConfiguration<Promotion>
{
public PromotionMap()
{
// Primary Key
this.HasKey(p => p.PromotionId);
// Properties
// table and column mappings
this.ToTable("promo");
this.Property(p => p.PromotionId).HasColumnName("p_id");
//if I well understand pc_id is an int that relates to a decimal
this.HasRequired(p => p.PromotionConfig)
.WithMany(pc => pc.Promotions)
.Map(p => p.MapKey("pc_id"));
}
}
public class PromotionConfigMap : EntityTypeConfiguration<PromotionConfig> {
public PromotionConfigMap()
{
// Primary Key
this.HasKey(s => s.PromotionConfigId);
// Properties
// Table and Column mappings
this.ToTable("promo_cfg");
}
}
I am getting the following error
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'UserRoles_Roles_Source' in relationship 'UserRoles_Roles'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
My entities and the associated mappings are defined as follows,
public class UserProfile
{
public UserProfile()
{
UserUserRoles = new List<UserRoles>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<UserRoles> UserUserRoles { get; set; }
}
public class Roles
{
public Roles()
{
RoleUserRoles = new List<UserRoles>();
}
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<UserRoles> RoleUserRoles { get; set; }
}
public class UserRoles
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual UserProfile User { get; set; }
public virtual Roles Roles { get; set; }
}
//Mappings
public UserProfileMap()
{
// Primary Key
HasKey(t => t.UserId);
// Properties
Property(t => t.UserName)
.HasMaxLength(56);
// Table & Column Mappings
ToTable("UserProfile");
Property(t => t.UserId).HasColumnName("UserId");
Property(t => t.UserName).HasColumnName("UserName");
}
public class RolesMap : EntityTypeConfiguration<Roles>
{
public RolesMap()
{
// Primary Key
HasKey(t => t.RoleId);
// Properties
Property(t => t.RoleName)
.HasMaxLength(256);
// Table & Column Mappings
ToTable("webpages_Roles");
Property(t => t.RoleId).HasColumnName("RoleId");
Property(t => t.RoleName).HasColumnName("RoleName");
}
}
public class UserRolesMap : EntityTypeConfiguration<UserRoles>
{
public UserRolesMap()
{
// Primary Key
HasKey(t => t.UserId);
HasKey(t => t.RoleId);
// Table & Column Mappings
ToTable("webpages_UsersInRoles");
Property(t => t.UserId).HasColumnName("UserId");
Property(t => t.RoleId).HasColumnName("RoleId");
// Relationships
HasRequired(t => t.User)
.WithMany(t => t.UserUserRoles)
.HasForeignKey(d => d.UserId);
HasRequired(t => t.Roles)
.WithMany(t => t.RoleUserRoles)
.HasForeignKey(d => d.RoleId);
}
}
what am I doing wrong?
Per the comments:
A combined primary key should be constructed as HasKey(t => new { t.UserId, t.RoleId });
It looks like you want to create a many-to-many relation between models. In that case you do not need the class UserRoles, it will be generated by EF when using this scenario:
public class User
{
// other properties
public virtual IList<Role> Roles {get;set;}
}
public class Role
{
// other properties
public virtual IList<User> Users {get;set;}
}
However, if you do want the UserRoles model to for instance store additional information you need to make the mapping manually in for example the OnModelCreating() function of the DbContext class.
When trying to eager load the PriceGridRow, the index and value properties of PriceGridColumn are populated but the Id and the ProduceGridRowId are not. If i try to explicitly include PriceGridColumns it get duplicate columns (ie. i have 10 columns but the object returned by EF has 20) and half of the the columns returned are fully populated and the other half are not.
I've been pulling what's left of my hair trying to figure out why this is occurring. Can anyone see based on my configuration why it would be acting this way? Thanks!
The code i use to get columns are:
public override PriceGrid GetLoadedById(object id)
{
var priceGrid = Entities
Include(x => x.PriceGridRows.Select(o => o.PriceGridColumns))
.FirstOrDefault(x => x.Id == (int) id);
return priceGrid;
}
Here are the classes in question
public class PriceGrid : DomainEntity<int>
{
public string Description { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
public List<PriceGridRow> PriceGridRows
{
get { return _priceGridRow; }
set { _priceGridRow = value; }
}
}
public class PriceGridRow : DomainEntity<int>
{
public PriceGrid PriceGrid { get; set; }
public int PriceGridId { get; set; }
public ProductOption ProductOption { get; set; }
public int ProductOptionId { get; set; }
public List<PriceGridColumn> PriceGridColumns { get; set; }
}
And finally the third level of nesting
public class PriceGridColumn : DomainEntity<int>
{
public PriceGridRow PriceGridRow { get; set; }
public int PriceGridRowId { get; set; }
public int Index { get; set; }
public decimal Value { get; set; }
}
Here are my mapping files
public class PriceGridMap : EntityTypeConfiguration<PriceGrid>
{
public PriceGridMap()
{
HasKey(x => x.Id);
Property(x => x.Description);
HasRequired(x => x.Product);
HasMany(x => x.PriceGridRows)
.WithRequired(x => x.PriceGrid)
.HasForeignKey(x => x.PriceGridId)
.WillCascadeOnDelete();
}
}
public class PriceGridRowMap : EntityTypeConfiguration<PriceGridRow>
{
public PriceGridRowMap()
{
HasKey(x => x.Id);
HasRequired(x => x.ProductOption);
HasMany(x => x.PriceGridColumns)
.WithRequired(x => x.PriceGridRow)
.HasForeignKey(x => x.PriceGridRowId)
.WillCascadeOnDelete();
}
}
public class PriceGridColumnMap : EntityTypeConfiguration<PriceGridColumn>
{
public PriceGridColumnMap()
{
HasKey(x => x.Id);
Property(x => x.Index);
Property(x => x.Value);
HasRequired(x => x.PriceGridRow);
}
}
Try to remove this mapping line from PriceGridColumnMap:
HasRequired(x => x.PriceGridRow);
which basically means that the relationship the PriceGridRow navigation property belongs to does not have an inverse navigation property. It is a shortcut for:
HasRequired(x => x.PriceGridRow)
.WithMany()...
But this is in contradiction with the mapping in PriceGridRowMap:
HasMany(x => x.PriceGridColumns)
.WithRequired(x => x.PriceGridRow)...
which says that the PriceGridRow navigation property does have an inverse navigation property, namely PriceGridColumns.