Missing Information When Eager Loading With Entity Framework - entity-framework

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.

Related

EF 6.1.3 Multiplicity conflicts

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

Eager many to many projection not working

My classes:
public class Post : Entity
{
public bool Active { get; set; }
public IList<Comment> Comments { get; set; }
public IList<Category> Categories { get; set; }
public Post ()
{
Categories = new List<Category>();
Comments = new List<Comment>();
}
}
public class Comment: Entity
{
public string UserName { get; set; }
public CommentStatus Status { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
public class Category: Entity
{
public string Name { get; set; }
public IList<Post> Posts { get; set; }
public Category()
{
Posts= new List<Post>();
}
}
My mapping:
public class PostMap: EntityTypeConfiguration<Post>
{
public PostMap()
{
ToTable("Posts");
HasKey(x => x.Id)
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.Categories)
.WithMany(x => x.Posts)
.Map(x =>
{
x.MapLeftKey("PostId");
x.MapRightKey("CategoryId");
x.ToTable("PostXCategory");
});
HasMany(x => x.Comments)
.WithRequired(x => x.Post)
.HasForeignKey(x => x.PostId);
}
}
public class CategoryMap: EntityTypeConfiguration<Category>
{
public CategoryMap()
{
ToTable("Categories");
HasKey(x => x.Id)
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
and finally my projection:
var query = _sqlRepository.Query<Post>()
.Where(x => x.Active)
.Select(x => new
{
Post = x,
Categories = x.Categories.ToList(),
Comments = x.Comments.Where(c => c.Status == CommentStatus .Approved).ToList()
});
var data = query.ToList();
The problem is that eager loading is not working for "Categories", just for "Comments".
When I check my projection (var data), I see this:
But when I select "Post" (data.Select(x => x.Post)) the Categories is empty:
Why Categories is empty and comments not?

Entity Framework mysterious error

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.

Different schema name on correlation table - Entity Framework

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!

EF CodeFirst - One-to-One relationship with non nullable columns - For Experts

My database has a convention from a third party company, that "all columns in the database must be 'not null'".
Now I'm mapping all tables using EFCodefirst and I ran into a problem.
For example, I have an entity SA1 that has a one-to-one relation to a SA3 entity, and I would like to add a new SA1 with its a1_vend property having an empty string.
What I did to solve this problem was add a SA3 entity with an empty string in the PK, but I didn't like this approach. I'd like a better solution to my problem.
My EFCodefirst classes:
[ComplexType]
public class Endereco
{
public string Logradouro { get; set; }
public string Numero { get; set; }
public string CEP { get; set; }
}
public class SA3
{
public string Codigo { get; set; }
public string Nome { get; set; }
}
public class SA1
{
public string Codigo { get; set; }
public string Nome { get; set; }
public Endereco Endereco { get; set; }
public Endereco EnderecoCobranca { get; set; }
public bool IsDeleted { get { return false; } }
public string a1_vend { get; set; }
public SA3 Vendedor { get; set; }
public SA1()
{
Endereco = new Endereco();
EnderecoCobranca = new Endereco();
}
}
public class SA3Map : EntityTypeConfiguration<SA3>
{
public SA3Map()
{
ToTable("sa3010");
HasKey(x => x.Codigo);
Property(x => x.Codigo)
.HasColumnName("a3_cod");
Property(x => x.Nome)
.HasColumnName("a3_nome");
}
}
public class SA1Map : EntityTypeConfiguration<SA1>
{
public SA1Map()
{
ToTable("sa1010");
HasKey(x => x.Codigo);
Property(x => x.Codigo)
.HasColumnName("a1_cod")
.IsRequired();
Property(x => x.Nome)
.HasColumnName("a1_nome")
.IsRequired();
Property(x => x.Endereco.Logradouro)
.HasColumnName("a1_end")
.IsRequired();
Property(x => x.Endereco.Numero)
.HasColumnName("a1_num")
.IsRequired();
Property(x => x.Endereco.CEP)
.HasColumnName("a1_cep")
.IsRequired();
Property(x => x.EnderecoCobranca.Logradouro)
.HasColumnName("a1_endcob")
.IsRequired();
Property(x => x.EnderecoCobranca.CEP)
.HasColumnName("a1_cepcob")
.IsRequired();
Property(x => x.EnderecoCobranca.Numero)
.HasColumnName("a1_numcob")
.IsRequired();
Property(x => x.a1_vend)
.IsRequired();
HasRequired(x => x.Vendedor)
.WithMany()
.HasForeignKey(x => new { x.a1_vend })
.WillCascadeOnDelete(false);
}
}
My sample program:
class Program
{
static void Main(string[] args)
{
MyContext ctx = new MyContext();
var novoVendedor = new SA3()
{
Codigo = "",
Nome = "Empty, don´t remove this row"
};
ctx.Vendedores.Add(novoVendedor);
var novoCliente = new SA1()
{
Codigo = "000001",
a1_vend = "", //I can´t use null here because my database convention
Endereco = new Endereco() { Numero = "99", CEP = "13280000", Logradouro = "Rua Teste" },
Nome = "Cliente S/A",
EnderecoCobranca = new Endereco { CEP = "13444999", Numero = "S/N", Logradouro = "Rua Cobranca" }
};
ctx.Clientes.Add(novoCliente);
ctx.SaveChanges();
}
}
IF the following are true:
SA1 -> SA3 is a one-to-one relationship.
The a1_vend column is in your sa1010 table.
You cannot make the a1_vend column nullable
you will not be able to create an SA1 object without having an SA3 object to reference first.
If you cannot make the a1_vend column nullable, your other option is to remove the a1_vend column from the sa1010 table and create a mapping table that maps SA1 objects to SA3 objects (would just need two columns: SA1.Codigo and a1_vend which I'm guessing is the same as SA3.Codigo)
Then you would change your SA1Map as follows:
...
Property(x => x.EnderecoCobranca.Numero)
.HasColumnName("a1_numcob")
.IsRequired();
//Property(x => x.a1_vend) // removing this
//.IsRequired();
HasRequired(x => x.Vendedor)
.WithMany()
.Map(m =>
{
m.ToTable("sa1010sa3010Map");
m.MapLeftKey("sa1_Codigo");
m.MapRightKey("sa3_Codigo");
});
}