I am trying to map a subclass of an existing entity onto the same table as the parent entity, like so:
public class Parent
{
public int ID {get; set;}
public string Name {get; set;}
}
public class Child : Parent
{
public virtual ICollection<Foo> {get; set;}
}
The child entity is identical to the parent except that it contains some navigation properties; the reason the child class exists (instead of adding the navigation properties to the parent class) is that the parent class is closed for modification (different assembly).
Entity framework doesn't like me mapping these to the same database table, expecting a discriminator column. How can I tell EF that these are really the same entity (just that I want some extra navigation properties on the child)?
Put them on separate DbContext's. The EF model builder looks for collisions between entities / tables on the properties of it's own context, not others.
Related
In EntityFramework code first models, there exists a 1:1 relationship:
public class Child1
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public Child2 Child2 { get; set; }
}
public class Child2
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[ForeignKey("Child1")]
public int Id { get; set; }
public string Name { get; set; }
public Child1 Child1 { get; set; }
}
When I tried to insert some data to the database, it thrown an exception:
{"A dependent property in a ReferentialConstraint is mapped to a
store-generated column. Column: 'Id'."}
It seems I cannot use auto generated Id for Child2, how can I keep this feature and make the relationship established successfully meanwhile?
Here there are two problems, the obvious one, shown in the exception. When you define a one-to-one relationship, the FK must be also the PK. In this case the PK and FK of both entities is the Id field. The problem shown in the exception is that the FK is database generated. So, if you insert a Child1 with a related Child2, EF has no way to set the FK value of the related Child2 because it's database generated.
The second problem, that has still not arisen, is that a one-to-one relationship is only a theoric thing in a database like SQL Server. If you want to insert Child1 that depends on Child2, you need to insert first Child1, and then the related Child2. That's right, but, ooops, you also have to insert Child2 before inserting Child1, because Child1 depends also on Child2. So, having a pure one to one relationship is not possible.
To solve this problem you need to do two things:
make the relationship a 1-to-(0 or 1). I.e. you must have a principal entity and a dependent entity which can or cannot exist. This will allow you to insert the principal entity, without the dependent entity, because with this configuration you can isnert the principal without the dependent.
the principal PK can be left as database generated, but you have to change the PK on the dependent entity not to be db generated. So, when you insert the dependent entity the PK, which is also the FK, can be freely specified by EF.
Finally, if you think of it, a 1-to-1 relationship usually makes no sense. You can use a single table that holds all the columns in both tables, because whenver a row exists in table A, it must exists in table B and viceversa. So having a single table has the same effect.
However, if you still want to use the 1-to-1 relationship, EF allows you to model it like this:
modelBuilder.Entity<Child1>()
.HasRequired(c1 => c1.Child2)
.WithRequiredPrincipal(c2 => c2.Child1);
Note that, in this case, the EF abstraction takes care to allow you to have a 1-to-1 relationship, even if it cannot exists in the DB. However, it's necessary to specify this relationship using the ModelBuilder because you need to specify a principal and a dependent side. In this case the principal is Child1 and the dependent is Child2. Note that you still have to be careful with the rule for database generated values.
NOTE that this is modelled in the DB with a single FK from Child2 to Child1, and not FK from Child1 to Child2. So, in the DB is a (1)-to(0 or 1) relationship, as explained above
public class Child1
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] // Leave as is
public int Id { get; set; }
...
public class Child2
{
//[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DatabaseGenerated(DatabaseGeneratedOption.None)] // Not db-generated
//[ForeignKey("Child1")] -- specified in the model builder
public int Id { get; set; }
How can I add a navigation property to my context, when the corresponding column has already been created in the database from a previous migration?
For example: A lady has many cats. I want to add a LadyId property to the Cats class, but a LadyId column already exists in the Cats table. This LadyId column was created in a previous migration to accommodate the Cats.Lady navigation property.
DB:
Lady Table
--------------
Id Name
Cats Table
--------------
Id LadyId FurColor
Classes:
public class Lady {
public Id {get; set;}
public Name {get; set;}
public List<Cat> Cats {get; set;}
}
public class Cat {
public Id {get; set;}
public Lady Lady {get; set;}
//I want to add a LadyId property here
}
So far, I've tried...
Adding the property: Error: "The model backing the 'Context' context has changed..."
Adding a migration to update the DB: This creates a migration, but it just renames the column to LadyId1, which is pointless. The DB should remain the same, I just want to add a Navigation Property.
Commenting out code in migration Up and Down methods to try and "trick" the migration table into having the correct hash to match my context. This throws an error "Invalid column name 'EmployeePerformanceReview_id1"
I am using entity framework code first, I have 2 entities declared like so:
[Table("BaseTable")]
public class BaseEntity
{
public string SomeProperty{get; set;}
}
[Table("DerivedTable")]
public class DerivedEntity
: BaseEntity
{
public string SomeOtherProperty {get; set;}
}
The problem is when I call the generic set method on the dbcontext it always returns the entities as DerivedEntity types. (EF is setting up the correct table mapping in the database just not returing the expected entity type from the set() method)
DbContext.Set<BaseEntity>();
How can I force Set<BaseEntity>(); to return the entities as type BaseEntity so I can only update that table?
You cannot. EF works on entity level not on table level. So if your entity is of type DerivedEntity it will never be loaded as BaseEntity only. When you modify attached entity EF will build update command only for modified columns so it should not modify your second table if you are only changing property from BaseEntity table.
I'm working with EF 4.1 CTP5 and SQL Server 2008. I need to understand how to solve a recurrency problem. I have the following 2 classes:
public class Nation
{
public int ID {get; set;}
public string name {get;set;}
public List<NationAlly> NationAllies {get;set;}
}
public class NationAlly
{
public int ID {get; set;}
public int level {get;set;}
public Nation Owner {get; set;}
public Nation toNation {get;set;}
}
The entities are mapped to the database with two tables (Nations and NationAllies). Besides, there are two relationships. 1) From NationAllies.OwnerID to Nation.ID
2) From NationAllies.ToNationID to Nation.ID
When I try to retrieve an object of Nation type from my database, I access the DbContext class NationDB:
Nation selectedNation = ((nationDB.Nations.Include("NationAllies")).Where(m => m.name == "France")).FirstOrDefault();
The problem is that I get a selectedNation object which has a list of NationAllies but every NationAlly in the list has the field toNation = null.
First of all I would like the field toNation to retrieve the correct information from the database. How do I do this?
Then of course toNation will be connected with other NationAllies which on their turn will have an other Nation. How could possibly a recursive map be built? My idea is to navigate the map until a certain level, by querying the database in a specific way. Doing so, what would be the best approach to have good speed performance?
It looks like NationAllies is junction table with additional properties. The problem is that EF doesn't eager load nested navigation properties if you do not specify them explicitly in Include method. If you want to have toNation filled you must use
nationDB.Nations.Include("NationAllies.toNation")
or
nationDB.Nations.Include(n => n.NationAllies.Select(na => na.toNation))
You can also enable lazy loading. Make all your navigation properties virtual (toNation, NationOwner and NationAllies) and unless you close the context all properties will be loaded once you first access them.
I am new to using the ADO.NET Entity Data Model tool. I have a table in my database that has three properties (FirstName, LastName, Age). I need to add a field to this entity called IsChosen. However, I cannot add this column in the database.
How do I add custom properties to entities generated through this tool?
Thank you!
The Entity Data Model tool creates partial classes.
You can extend those partial classes in another source file. You just need to make sure your section of the partial class lives in the same namespace as the Entity Data Model generated classes. For example:
Tool Generated Code
namespace Your.Generated.Classes
{
public partial class Stuff
{
public string Name {get; set;}
public int Age {get; set;}
}
}
Your Seperate Code File
namespace Your.Generated.Classes
{
public partial class Stuff
{
public string NonDatabaseProperty {get; set;}
}
}