Can not set up one to many relationship with EF fluent API - entity-framework

I am trying to configure a one to many relationship using EF Core via fluent api and i keep getting the following error :
The expression 'x => x.parent' is not a valid property expression. The
expression should represent a simple property access: 't =>
t.MyProperty'. (Parameter 'propertyAccessExpression')'
Model(s)
public class Parent {
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child {
public int ID { get; set; }
public string Name { get; set; }
public Parent parent;
public int ParentId { get; set; }
}
Context
public class MyContext : DbContext {
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Child>().HasKey(x => x.ID);
modelBuilder.Entity<Parent>().HasKey(x => x.ID);
modelBuilder.Entity<Child>()
.HasOne(x => x.parent)
.WithMany(y => y.Children)
.HasForeignKey(t => t.ParentId);
}
public MyContext(DbContextOptions options):base(options) { }
}
Usage
static async Task Main(string[] args)
{
string connectionString = "[someconnectionstring]"
var optionsBuilder = new DbContextOptionsBuilder<MyContext>();
optionsBuilder.UseSqlServer(connectionString);
MyContext context = new MyContext(optionsBuilder.Options);
await context.Parents.AddAsync(new Parent {
Name = "myparent",
Children = new List<Child>() {
new Child { Name = "Child1" },
new Child { Name = "Child2" } }
}); //i am getting the error here
await context.SaveChangesAsync();
}

parent in Child class is a field. It should be public property. Please see for more information https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/fluent/types-and-properties#property-mapping

Related

Entity Framework Core null relationship

I have created a simple EF Core to join two tables by using relationship (HasOne). But when I run it, the query only queries the master table (Employees) without joining to the second table (Contact) and it causes the model to not bind the data.
Could someone point out what I am missing in this code shown below? Thanks
public class Employees
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string ContactNumber { get; set; }
public Employees Employee { get; set; }
public int EmployeeId { get; set; }
}
internal class EmployeeMap : IEntityTypeConfiguration<Employees>
{
public void Configure(EntityTypeBuilder<Employees> builder)
{
builder.HasKey(x => x.EmployeeId);
builder.Property(p => p.EmployeeId).ValueGeneratedOnAdd();
builder.HasOne(x => x.Contact).WithOne(y => y.Employee).HasForeignKey<Contact>(k => k.EmployeeId);
}
}
public class ContactMap : IEntityTypeConfiguration<Contact>
{
public void Configure(EntityTypeBuilder<Contact> builder)
{
builder.HasKey(x => x.Id);
builder.Property(p => p.Id).ValueGeneratedOnAdd();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
private EmployeeResponse GetEmployeeResponse()
{
var emp = _context.Employees.FirstOrDefault();
return new EmployeeResponse
{
ContactNumber = emp!.Contact.ContactNumber,
EmployeeId = emp.EmployeeId,
};
}
Solutions:
1. Enable lazy loading:
DbContext.Configuration.LazyLoadingEnabled = true;
2. Or load it manually with .Include:
_context.Employees.Include(x => x.Contact).FirstOrDefault();
More information about navigation propertys in ef.

How to declare a parent child relationship when both tables are TPH and the relationship is in the base classes?

My problem relates to sales orders and sales invoices but I find it easier to think of pets and their offspring... without creating a full pedigree model.
My DbContext
using System;
using DevExpress.ExpressApp.EFCore.Updating;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using DevExpress.Persistent.BaseImpl.EF.PermissionPolicy;
using DevExpress.Persistent.BaseImpl.EF;
using DevExpress.ExpressApp.Design;
using DevExpress.ExpressApp.EFCore.DesignTime;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using DevExpress.ExpressApp.DC;
using System.Collections.Generic;
namespace Pets.Module.BusinessObjects
{
[TypesInfoInitializer(typeof(PetsContextInitializer))]
public class PetsEFCoreDbContext : DbContext
{
public PetsEFCoreDbContext(DbContextOptions<PetsEFCoreDbContext> options) : base(options)
{
}
public DbSet<Cat> Cats { get; set; }
public DbSet<Dog> Dogs { get; set; }
public DbSet<Kitten> Kittens { get; set; }
public DbSet<Puppy> Puppys { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Pet>()
.HasDiscriminator(x=> x.IsCat)
.HasValue<Cat>(true)
.HasValue<Dog>(false);
modelBuilder.Entity<BabyPet>()
.HasDiscriminator(x => x.IsCat)
.HasValue<Kitten>(true)
.HasValue<Puppy>(false);
modelBuilder.Entity<Puppy>().HasOne(x => x.Parent).WithMany(x => x.Puppies);
modelBuilder.Entity<Kitten>().HasOne(x => x.Parent).WithMany(x => x.Kittens);
}
}
}
My classes
public abstract class Pet
{
[Key] public int Id { get; set; }
public string Name { get; set; }
public bool? IsCat { get; set; }
}
public abstract class BabyPet
{
[Key] public int Id { get; set; }
public int ParentPetId { get; set; }
[ForeignKey("ParentPetId")]
public virtual Pet Parent { get; set; }
public string Name { get; set; }
public bool? IsCat { get; set; }
}
public class Kitten : BabyPet
{
new public virtual Cat Parent { get; set; }
}
public class Dog : Pet
{
public Dog()
{
Puppies = new List<Puppy>();
}
[Aggregated]
public virtual List<Puppy> Puppies { get; set; }
}
public class Cat : Pet
{
public Cat()
{
Kittens = new List<Kitten>();
}
[Aggregated]
public virtual List<Kitten> Kittens { get; set; }
}
public class Puppy : BabyPet
{
new public virtual Dog Parent { get; set; }
}
Also there is
public class PetsContextInitializer : DbContextTypesInfoInitializerBase
{
protected override DbContext CreateDbContext()
{
var optionsBuilder = new DbContextOptionsBuilder<PetsEFCoreDbContext>()
.UseSqlServer(#";");
return new PetsEFCoreDbContext(optionsBuilder.Options);
}
}
However this creates the following structure in BabyPet
Where as I just want
[Update]
I was able to get the structure I want by specifying the foreignkey in OnModelCreating
modelBuilder.Entity<Puppy>().HasOne(x => x.Parent).WithMany(x => x.Puppies).HasForeignKey(x=>x.ParentPetId);
modelBuilder.Entity<Kitten>().HasOne(x => x.Parent).WithMany(x => x.Kittens).HasForeignKey(x => x.ParentPetId);
However when I try to add a Kitten to a cat via the XAF Winforms UI I get:
Unable to cast object of type 'SimplePets.Module.BusinessObjects.Kitten' to type 'SimplePets.Module.BusinessObjects.Puppy'.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.get_Item(IPropertyBase propertyBase)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.GetCurrentValue(IPropertyBase propertyBase)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.TryAddPropertyNameToCollection(InternalEntityEntry entity, ICollection`1 propertiesToCheck, IPropertyBase property)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.TryAddPropertyNameToCollection(InternalEntityEntry entity, IProperty property, ICollection`1 propertiesToCheck)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.GetPropertiesToCheck(InternalEntityEntry entity)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.CheckReadWritePermissionsForNonIntermediateObject(InternalEntityEntry entity)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.CheckReadWritePermissions(InternalEntityEntry entity)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.CheckIsGrantedToSave(InternalEntityEntry entity)
at DevExpress.EntityFrameworkCore.Security.NetStandard.ChangeTracking.SecurityStateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
at DevExpress.ExpressApp.EFCore.EFCoreObjectSpace.DoCommit()
at DevExpress.ExpressApp.BaseObjectSpace.CommitChanges()
at DevExpress.ExpressApp.Win.SystemModule.WinModificationsController.Save(SimpleActionExecuteEventArgs args)
at DevExpress.ExpressApp.SystemModule.ModificationsController.saveAction_OnExecute(Object sender, SimpleActionExecuteEventArgs e)
at DevExpress.ExpressApp.Actions.SimpleAction.RaiseExecute(ActionBaseEventArgs eventArgs)
at DevExpress.ExpressApp.Actions.ActionBase.ExecuteCore(Delegate handler, ActionBaseEventArgs eventArgs)
I put my example on GitHub here
Docs link about relationships here and tph inheritance is here
I think I must have the data structures correct after my update to onModelCreating. That is :
modelBuilder.Entity<Puppy>().HasOne(x => x.Parent).WithMany(x => x.Puppies).HasForeignKey(x=>x.ParentPetId);
modelBuilder.Entity<Kitten>().HasOne(x => x.Parent).WithMany(x => x.Kittens).HasForeignKey(x => x.ParentPetId);
I was able to work around the Cast Object error by using DBContext instead of ObjectSpace
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using SimplePets.Module.BusinessObjects;
using System.Linq;
namespace SimplePets.Module.Win.Controllers
{
public class KittenViewController : ViewController
{
SimpleAction actionAddKittenEF;
SimpleAction actAddKittenXAF;
public KittenViewController() : base()
{
TargetObjectType = typeof(Kitten);
TargetViewNesting = Nesting.Nested;
actAddKittenXAF = new SimpleAction(this, "Add via OS", "View");
actAddKittenXAF.Execute += actAddKittenXAF_Execute;
actionAddKittenEF = new SimpleAction(this, "Add via Db", "View");
actionAddKittenEF.Execute += actionAddKittenEF_Execute;
}
private void actionAddKittenEF_Execute(object sender, SimpleActionExecuteEventArgs e)
{
var cat = View.ObjectSpace.GetObject(((NestedFrame)Frame).ViewItem.CurrentObject) as Cat;
var db = Helpers.MakeDb();
var kitten = new Kitten
{
Parent = db.Cats.FirstOrDefault(c => c.Id == cat.Id),
Name = $"baby {cat.Kittens.Count + 1} of {cat.Name}"
};
db.Kittens.Add(kitten);
db.SaveChanges();
View.ObjectSpace.Refresh();
}
//Errors
private void actAddKittenXAF_Execute(object sender, SimpleActionExecuteEventArgs e)
{
var cat = View.ObjectSpace.GetObject(((NestedFrame)Frame).ViewItem.CurrentObject) as Cat;
var os = View.ObjectSpace;
var kitten = os.CreateObject<Kitten>();
kitten.Parent = cat;
kitten.Name = $"baby {cat.Kittens.Count + 1} of {cat.Name}";
View.ObjectSpace.CommitChanges();
View.ObjectSpace.Refresh();
}
}
}

EF6: Single relationship table for multiple related entities

I have a EF Model with many entities, like Nodes, Attributes, Tags, etc.
There is also an "Alias" entity, and pretty much every other entity else can have a many-to-many relationship with Aliases. One of the undesired things about this is the number of tables that are created to track these relationships (eg. NodeAlias, AttributeAlias, etc.).
Are there any design alternatives that could map an Alias to all of the other entities in a single table? I was thinking maybe something along these lines if it's possible:
+---------+--------+-------------+-----------+
| AliasId | NodeId | AttributeId | TagId |
+---------+--------+-------------+-----------+
| 1 | 1 | 2 | 3 |
+---------+--------+-------------+-----------+
I updated my solution to provide many-to-many relationships between aliases and every other entity.
I intentionally posted this as a separate answer so that my previous answer can also remain here if anyone would need it.
Step #1: I created extension methods for getting and setting property values using reflection in a convenient way:
public static class ObjectExtensions
{
public static TResult GetPropertyValue<TResult>(this object entity, string propertyName)
{
object propertyValue = entity?.GetType().GetProperty(propertyName)?.GetValue(entity);
try
{
return (TResult)propertyValue;
}
catch
{
return default(TResult);
}
}
public static void SetPropertyValue(this object entity, string propertyName, object value)
{
entity?.GetType().GetProperty(propertyName)?.SetValue(entity, value);
}
}
Step #2: I updated the models to provide many-to-many relationship.
public class Node
{
[Key]
public int NodeId { get; set; }
public string Name { get; set; }
public virtual ICollection<AliasMapping> AliasMappings { get; set; }
}
public class Attribute
{
[Key]
public int AttributeId { get; set; }
public string Name { get; set; }
public virtual ICollection<AliasMapping> AliasMappings { get; set; }
}
public class Tag
{
[Key]
public int TagId { get; set; }
public string Name { get; set; }
public virtual ICollection<AliasMapping> AliasMappings { get; set; }
}
public class Alias
{
[Key]
public int AliasId { get; set; }
public string Name { get; set; }
public virtual ICollection<AliasMapping> AliasMappings { get; set; }
}
public class AliasMapping
{
[Key]
public int Id { get; set; }
[ForeignKey("Alias")]
public int AliasId { get; set; }
public Alias Alias { get; set; }
[ForeignKey("Node")]
public int? NodeId { get; set; }
public virtual Node Node { get; set; }
[ForeignKey("Attribute")]
public int? AttributeId { get; set; }
public virtual Attribute Attribute { get; set; }
[ForeignKey("Tag")]
public int? TagId { get; set; }
public virtual Tag Tag { get; set; }
}
Step #3: Due to relationship changes the MyDbContext could have been simplified as the [ForeignKey] data annotations are enough.
public class MyDbContext : DbContext
{
public DbSet<Node> Nodes { get; set; }
public DbSet<Attribute> Attributes { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Alias> Aliases { get; set; }
public DbSet<AliasMapping> AliasMappings { get; set; }
}
Step #4: I also updated the extension methods so that you can create and remove alias mappings.
public static class AliasExtensions
{
public static void CreateMapping(this MyDbContext context, object entity, Alias alias)
{
if (entity == null || alias == null)
{
return;
}
string mappingEntityPropertyName = entity.GetType().Name;
string entityKeyPropertyName = String.Concat(mappingEntityPropertyName, "Id");
int entityId = entity.GetPropertyValue<int>(entityKeyPropertyName);
AliasMapping[] mappings =
context
.AliasMappings
.Where(mapping => mapping.AliasId == alias.AliasId)
.ToArray();
if (mappings.Any(mapping => mapping.GetPropertyValue<int?>(entityKeyPropertyName) == entityId))
{
// We already have the mapping between the specified entity and alias.
return;
}
bool usableMappingExists = true;
var usableMapping = mappings.FirstOrDefault(mapping => mapping.GetPropertyValue<int?>(entityKeyPropertyName) == null);
if (usableMapping == null)
{
usableMappingExists = false;
usableMapping = new AliasMapping()
{
Alias = alias
};
}
usableMapping.SetPropertyValue(mappingEntityPropertyName, entity);
usableMapping.SetPropertyValue(entityKeyPropertyName, entityId);
if (!usableMappingExists)
{
context.AliasMappings.Add(usableMapping);
}
// This step is required here, I think due to using reflection.
context.SaveChanges();
}
public static void RemoveMapping(this MyDbContext context, object entity, Alias alias)
{
if (entity == null || alias == null)
{
return;
}
string mappingEntityPropertyName = entity.GetType().Name;
string entityKeyPropertyName = String.Concat(mappingEntityPropertyName, "Id");
int entityId = entity.GetPropertyValue<int>(entityKeyPropertyName);
AliasMapping[] mappings =
context
.AliasMappings
.Where(mapping => mapping.AliasId == alias.AliasId)
.ToArray();
AliasMapping currentMapping = mappings.FirstOrDefault(mapping => mapping.GetPropertyValue<int?>(entityKeyPropertyName) == entityId);
if (currentMapping == null)
{
// There is no mapping between the specified entity and alias.
return;
}
currentMapping.SetPropertyValue(mappingEntityPropertyName, null);
currentMapping.SetPropertyValue(entityKeyPropertyName, null);
// This step is required here, I think due to using reflection.
context.SaveChanges();
}
}
Step #5: Updated the console app steps to align it with the changes.
class Program
{
static void Main(string[] args)
{
// Consider specify the appropriate database initializer!
// I use DropCreateDatabaseAlways<> strategy only for this example.
Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());
var aliases =
Enumerable
.Range(1, 9)
.Select(index => new Alias() { Name = String.Format("Alias{0:00}", index) })
.ToList();
var attributes =
Enumerable
.Range(1, 5)
.Select(index => new Attribute() { Name = String.Format("Attribute{0:00}", index) })
.ToList();
var nodes =
Enumerable
.Range(1, 5)
.Select(index => new Node() { Name = String.Format("Node{0:00}", index) })
.ToList();
var tags =
Enumerable
.Range(1, 5)
.Select(index => new Tag() { Name = String.Format("Tag{0:00}", index) })
.ToList();
using (var context = new MyDbContext())
{
context.Aliases.AddRange(aliases);
context.Nodes.AddRange(nodes);
context.Attributes.AddRange(attributes);
context.Tags.AddRange(tags);
// Always save changes after adding an entity but before trying to create a mapping.
context.SaveChanges();
// One Alias To Many Entities
context.CreateMapping(nodes[0], aliases[0]);
context.CreateMapping(nodes[1], aliases[0]);
context.CreateMapping(nodes[2], aliases[0]);
context.CreateMapping(nodes[3], aliases[0]);
context.CreateMapping(attributes[0], aliases[0]);
context.CreateMapping(attributes[1], aliases[0]);
context.CreateMapping(attributes[2], aliases[0]);
context.CreateMapping(tags[0], aliases[0]);
context.CreateMapping(tags[1], aliases[0]);
// One Entity To Many Aliases
context.CreateMapping(nodes[4], aliases[0]);
context.CreateMapping(nodes[4], aliases[1]);
context.CreateMapping(nodes[4], aliases[2]);
context.CreateMapping(attributes[3], aliases[1]);
context.CreateMapping(attributes[3], aliases[3]);
context.CreateMapping(tags[2], aliases[2]);
context.CreateMapping(tags[2], aliases[3]);
// Remove mapping
context.RemoveMapping(nodes[4], aliases[0]);
// Not really needed here as both 'CreateMapping' and 'RemoveMapping' save the changes
context.SaveChanges();
}
Console.Write("Press any key to continue . . .");
Console.ReadKey(true);
}
}
Please note: RemoveMapping() will not delete an AliasMapping even if no entity is associated with it! But CreateMapping() will make use of it later if needed. E.g. look at the screenshot below and check AliasMapping where Id = 5.
Screenshot about the execution result:
You were talking about many-to-many relationship but reading your post I think it is more likely a "special one-to-many" relationship, actually "combined multiple one-to-one" relationship as I see that an Alias can be mapped to a single Node AND/OR to a single Attribute AND/OR to a single Tag.
I think I found a solution for this case.
If it's not the case and an Alias can be mapped to multiple Node AND/OR to multiple Attribute AND/OR to multiple Tag then I think this solution below needs only a small change. :)
Step #1 - These are my example models
public class Node
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual AliasMapping AliasMapping { get; set; }
}
public class Attribute
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual AliasMapping AliasMapping { get; set; }
}
public class Tag
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual AliasMapping AliasMapping { get; set; }
}
public class Alias
{
[Key]
public int AliasId { get; set; }
public string Name { get; set; }
public virtual AliasMapping AliasMapping { get; set; }
}
Step #2 - Creating the custom mapping table
public class AliasMapping
{
[Key]
[ForeignKey("Alias")]
public int AliasId { get; set; }
public Alias Alias { get; set; }
[ForeignKey("Node")]
public int NodeId { get; set; }
public virtual Node Node { get; set; }
[ForeignKey("Attribute")]
public int AttributeId { get; set; }
public virtual Attribute Attribute { get; set; }
[ForeignKey("Tag")]
public int TagId { get; set; }
public virtual Tag Tag { get; set; }
}
Step #3 - Creating the DbContext
public class MyDbContext : DbContext
{
public DbSet<Node> Nodes { get; set; }
public DbSet<Attribute> Attributes { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Alias> Aliases { get; set; }
public DbSet<AliasMapping> AliasMappings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<AliasMapping>()
.HasOptional(mapping => mapping.Attribute)
.WithOptionalPrincipal(attribute => attribute.AliasMapping)
.Map(config => config.MapKey("AliasId"));
modelBuilder
.Entity<AliasMapping>()
.HasOptional(mapping => mapping.Node)
.WithOptionalPrincipal(node => node.AliasMapping)
.Map(config => config.MapKey("AliasId"));
modelBuilder
.Entity<AliasMapping>()
.HasOptional(mapping => mapping.Tag)
.WithOptionalPrincipal(tag => tag.AliasMapping)
.Map(config => config.MapKey("AliasId"));
}
}
Step #4 - Creating extension method so that creating a relationship will be easy
public static class AliasExtensions
{
public static void CreateMapping<TEntity>(this MyDbContext context, TEntity entity, Alias alias)
{
string mappingEntityPropertyName = typeof(TEntity).Name;
string entityKeyPropertyName = String.Concat(mappingEntityPropertyName, "Id");
bool entityExists = true;
var mapping = context.AliasMappings.Find(alias.AliasId);
if (mapping == null)
{
entityExists = false;
mapping = new AliasMapping()
{
Alias = alias
};
}
typeof(AliasMapping)
.GetProperty(mappingEntityPropertyName)
.SetValue(mapping, entity);
typeof(AliasMapping)
.GetProperty(entityKeyPropertyName)
.SetValue(mapping, typeof(TEntity).GetProperty("Id").GetValue(entity));
if (!entityExists)
{
context.AliasMappings.Add(mapping);
}
}
}
Step #5 - Created a console app to see this working
class Program
{
static readonly Random rnd = new Random(DateTime.Now.TimeOfDay.Milliseconds);
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());
var aliases =
Enumerable
.Range(1, 9)
.Select(index => new Alias() { Name = String.Format("Alias{0:00}", index) })
.ToList();
var attributes =
Enumerable
.Range(1, 5)
.Select(index => new Attribute() { Name = String.Format("Attribute{0:00}", index) })
.ToList();
var nodes =
Enumerable
.Range(1, 5)
.Select(index => new Node() { Name = String.Format("Node{0:00}", index) })
.ToList();
var tags =
Enumerable
.Range(1, 5)
.Select(index => new Tag() { Name = String.Format("Tag{0:00}", index) })
.ToList();
using (var context = new MyDbContext())
{
context.Aliases.AddRange(aliases);
context.Nodes.AddRange(nodes);
context.Attributes.AddRange(attributes);
context.Tags.AddRange(tags);
context.SaveChanges();
// Associate aliases to attributes
attributes.ForEach(attribute =>
{
var usableAliases = aliases.Where(alias => alias.AliasMapping?.Attribute == null).ToList();
var selectedAlias = usableAliases[rnd.Next(usableAliases.Count)];
context.CreateMapping(attribute, selectedAlias);
});
// Associate aliases to nodes
nodes.ForEach(node =>
{
var usableAliases = aliases.Where(alias => alias.AliasMapping?.Node == null).ToList();
var selectedAlias = usableAliases[rnd.Next(usableAliases.Count)];
context.CreateMapping(node, selectedAlias);
});
// Associate aliases to tags
tags.ForEach(tag =>
{
var usableAliases = aliases.Where(alias => alias.AliasMapping?.Tag == null).ToList();
var selectedAlias = usableAliases[rnd.Next(usableAliases.Count)];
context.CreateMapping(tag, selectedAlias);
});
context.SaveChanges();
}
Console.Write("Press any key to continue . . .");
Console.ReadKey(true);
}
}

Entity Framework Table Per Hierarchy Inserting Multiple Id Columns

I have seriously spent two work days trying to a TPH setup from Database First to Code first. The Error I get is Something like "Invalid Column Name Entity_EntityId/ Entity_Entity_Id1"
I've drawn up a very basic reproduction of the issue like so:
internal class Program
{
private static void Main(string[] args)
{
using (var context = new Context())
{
var baseClass = new Base {Name = "Test"};
context.BaseClasses.Add(baseClass);
context.SaveChanges();
var baseClasses = context.BaseClasses.ToList();
}
}
}
Context:
public class Context : DbContext
{
public Context() : base("TPH")
{
}
public DbSet<Base> BaseClasses { get; set; }
public DbSet<Derived> DervDerivedClasses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Mapping:
public class BaseMap : EntityTypeConfiguration<Base>
{
public BaseMap()
{
HasKey(b => b.Id);
Property(b => b.Name);
HasOptional(b => b.AnotherClass)
.WithMany(b => b.Bases)
.HasForeignKey(b => b.AnotherClassId);
Map(b => b.Requires("Disc").HasValue(1));
}
}
public class DerivedMap : EntityTypeConfiguration<Derived>
{
public DerivedMap()
{
HasKey(b => b.Id);
Property(b => b.Name);
HasOptional(b => b.AnotherClass)
.WithMany(b => b.Deriveds)
.HasForeignKey(b => b.AnotherClassId);
Map(b => b.Requires("Disc").HasValue(2));
}
}
public class SecondDerivedMap : EntityTypeConfiguration<SecondDerived>
{
public SecondDerivedMap()
{
HasKey(b => b.Id);
Property(b => b.Name);
HasOptional(b => b.AnotherClass)
.WithMany(b => b.SecondDeriveds)
.HasForeignKey(b => b.AnotherClassId);
Map(b => b.Requires("Disc").HasValue(3));
}
}
Entities:
public class Base
{
public int Id { get; set; }
public string Name { get; set; }
public int? AnotherClassId { get; set; }
public AnotherClass AnotherClass { get; set; }
}
public class Derived : Base
{
}
public class SecondDerived : Base
{
}
public class AnotherClass
{
public int Id { get; set; }
public ICollection<Base> Bases { get; set; }
public ICollection<Derived> Deriveds { get; set; }
public ICollection<SecondDerived> SecondDeriveds { get; set; }
}
How can I get the table to just have a single "AnotherClassId?"
You're only supposed to have a single navigation property per entity per relationship -- and you have three (Bases, Deriveds, and SecondDeriveds). EF sees those properties and thinks there are three different one-to-many associations between AnotherClass and the various classes in the Base hierarchy.
If you want to get a collection of the related Derived entities from AnotherClass, you're supposed to use something like anotherClassEntity.Bases.OfType<Derived>().

Why can't I do ToList()?

I build a model as below. The relationship between Recycler and Account is 1:1.
public class MyContext : DbContext
{
public DbSet<Quoter> Quoters { get; set; }
public DbSet<Account> Accounts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>()
.HasRequired(a => a.RecyclerRef)
.WithRequiredDependent(r => r.AccountRef);
}
}
public class Quoter
{
public int QuoterId { get; set; }
public string Text { get; set; }
}
public class Recycler : Quoter
{
public string Description { get; set; }
public virtual Account AccountRef { get; set; }
}
public class Account
{
public int AccountId { get; set; }
public Recycler RecyclerRef { get; set; }
}
But, I get exceptions when I do the either of these queries:
var query1 = context.Quoters
.OfType<Recycler>()
.Include(r => r.AccountRef)
.Where(r => r.QuoterId == 1)
.ToList();
var query2 = context.Set<Recycler>()
.Include(r => r.AccountRef)
.Where(r => r.QuoterId == 1)
.ToList();
Exception shows that ResultType is “Transient.reference[POCOFirst.Quoter]”,but recommanded is “Transient.reference[POCOFirst.Recycler]”
If I remove the ToList(), it works well. But I need a list as the return value of method.
Why can't I do ToList()? Thanks
It looks like you have stumble upon this bug in EF. Another reference to the bug.
Workaround would be to remove the Include method.