I have a problem trying to correctly configure a relationship in EF. I have used EF code first to generate classes from an existing database. The first table holds a list of instructions, the second holds a record of the state that each instruction is in.
Tables (simplified):
Instruction
-----------
InstructionID
CurrentInstructionStateHistoryID
InstructionStateHistory
-----------------------
InstructionStateHistoryID
InstructionID
State
So you can see that there are two relationships between the tables - a 1-many relationship based on InstructionID, which I'm not interested in, and have therefore deleted the properties for. The second relationship is based on the CurrentInstructionStateHistoryID property, which points to the "current" state of the instruction.
The classes are as follows:
public partial class Instruction
{
[Key]
public int InstructionID { get; set; }
public int? CurrentInstructionStateHistoryID { get; set; }
public virtual CurrentInstructionStateHistory InstructionStateHistory { get; set; }
}
public partial class InstructionStateHistory
{
[Key]
public int InstructionStateHistoryID { get; set; }
public int InstructionID { get; set; }
public string State { get; set; }
public virtual Instruction tblInstruction { get; set; }
}
Here's the fluent API setup to define the relationship:
modelBuilder.Entity<InstructionStateHistory>()
.HasRequired(e => e.tblInstruction)
.WithOptional(e => e.CurrentInstructionStateHistory);
So, it all compiles and runs. But when I get to a bit of code like this:
Instruction instruction = await _dal.InstructionRepository.Find(claimID);
InstructionStateHistory history = i.CurrentInstructionStateHistory;
I can see that the instruction is populated correctly, let's say the Id is 1234. When I examine the InstructionStateHistory object, what I want to see is that it's InstructionID is 1234, but instead what I see is that it's InstructionStateHistoryID, i.e. is's primary key, is 1234 and that it's related to a completely different instruction.
Somehow I need to tell EF that Instruction.CurrentInstructionStateHistoryID links to InstructionStateHistory.InstructionStateHistoryID.
I've tried many combinations of data annotations and fluent setup but have been unable to find a combination that actually works, either I get the above result or a runtime error. Any help gratefully accepted!
It seems like EF just can't handle this case, so the solution was to forget the concept of a "current" InstructionStateHistory. Instead I added a date field to the InstructionStateHistory table, and then changed the Instruction class to have a regular collection property as follows:
public virtual ICollection<InstructionStateHistory> InstructionStateHistories{ get; set; }
Then when I need the "current" state I just query the colection, sort by date and take the latest one.
Related
I am making an app using the ASP.Net Boilerplate framework and in my Domain layer I have a simple "Boss" entity. Creating and retrieving these entities from the database works fine but I can't get the "Update" to work. When map my "UpdateBossDto" to a Boss object and try to update it I get this error:
$exception {System.InvalidOperationException: The instance of entity
type 'Boss' cannot be tracked because another instance with the same
key value for {'Id'} is already being tracked. When attaching existing
entities, ensure that only one entity instance with a given key value
is attached. Consider using
'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the
conflicting key values.
This error gets thrown in the BossManager class (I have removed the other methods for readability.
public class BossManager : DomainService, IBossManager
{
private readonly IRepository<Boss> _repositoryBoss;
public BossManager(IRepository<Boss> repositoryBoss)
{
_repositoryBoss = repositoryBoss;
}
public void Update(Boss entity)
{
_repositoryBoss.UpdateAsync(entity);
}
}
Here is my Update method in the BossAppService (i know getting the Id this way probably isn't great but right now I'm just desperate):
public void Update(UpdateBossDto updatedBoss)
{
var boss = new Boss();
updatedBoss.Id = _bossManager.GetBossIdByName(updatedBoss.Name);
boss = ObjectMapper.Map<Boss>(updatedBoss);
_bossManager.Update(boss);
}
And my UpdateDto class which holds the same attributes as the Boss class itself:
public class UpdateBossDto
{
public int Id { get; set; }
public string Name { get; set; }
public int Hp { get; set; }
public int CombatLvl { get; set; }
public int MaxHit { get; set; }
public string AttackStyle { get; set; }
public string Weakness { get; set; }
public string ImageUrl { get; set; }
}
How can I update the Boss object either with or without the Id? Any help would be greatly appreciated!
There's a number of issues here. First, the id should be coming from the request URL, since it uniquely identifies the resource that's being modified. This also saves you from having to do silly things like GetBossIdByName. Not only does that require an unnecessary query, but it's prone to error. The id is your key for a reason: it's unique. Names are not. You could have multiple bosses with the same name. Additionally, your name columns are likely not indexed, which means such a query is vastly more inefficient. Then, with your id, you should be querying the corresponding Boss out of your database, and mapping onto this instance, not creating a new instance. Finally, save that same instance back to the database. Then, you will have no issues.
I'm having a problem very similar to the ones mentioned in these questions:
Why is Entity Framework navigation property null?
Why EF navigation property return null?
The plot twist in my case is that the navigation collection properties are populated by EF, but only after I've queried DbSet<T> properties of the dependent types in the DbContext. To make my situation clearer, here's how my model is set up:
[Table(nameof(Composer))]
internal class ComposerRelationalDto : RelationdalDtoBase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual ICollection<NameRelationalDto> LocalizedNames { get; set; } = new HashSet<NameRelationalDto>();
public virtual ICollection<ArticleRelationalDto> Articles { get; set; } = new HashSet<ArticleRelationalDto>();
}
[Table(nameof(ComposerName))]
internal class NameRelationalDto : RelationdalDtoBase
{
[Key]
public long Id { get; set; }
[Required]
[ForeignKey(nameof(Composer))]
public Guid Composer_Id { get; set; }
public ComposerRelationalDto Composer { get; set; }
}
[Table(nameof(ComposerArticle))]
internal class ArticleRelationalDto : RelationdalDtoBase
{
[Key]
public long Id { get; set; }
[Index]
public Guid StorageId { get; set; }
[Required]
[ForeignKey(nameof(Composer))]
public Guid Composer_Id { get; set; }
public ComposerRelationalDto Composer { get; set; }
[Required]
[MaxLength(5)]
public string Language { get; set; }
}
In the corresponding repository I filter ComposerRelationalDto objects by their name:
DbContext.Set<NameRelationalDto>().Where(nameWhereClause).GroupBy(n => n.Composer_Id).Select(group => group.FirstOrDefault().Composer)
The set of ComposerRelationalDtos has empty collections for the Articles and LocalizedNames properties, even though the data has been correctly persisted in the database. However, if I load all DTOs of type ArticleRelationalDto and NameRelationalDto in a QuickWatch while debugging, then the same filter no longer returns empty collections and all relevant objects are present in the collection properties.
What I've tried so far was to
enable lazy loading and the creation of proxies explicitly
configure the one-to many-relationships manually:
modelBuilder.Entity<ComposerRelationalDto>().HasMany(c => c.LocalizedNames).WithRequired(n => n.Composer).HasForeignKey(n => n.Composer_Id);
modelBuilder.Entity<ComposerRelationalDto>().HasMany(c => c.Articles).WithRequired(a => a.Composer).HasForeignKey(a => a.Composer_Id);
and finally I just tried fiddling with the DbQuery<T>.Include() method DbContext.Set<ComposerRelationalDto>().Include(c => c.Articles) which unfortunately throws an ArgumentNullException from one of the internal methods it calls.
Basically, whatever fixes or workarounds I've tried haven't helped, so I must ask for more help.
Edit:
I modified the dependent types' Composer property to be virtual. However, the problem persists.
After using .Select(group => group.FirstOrDefault().Composer).Include(c => c.Articles).Include(c => c.LocalizedNames) I now no longer get an ArgumentNullException (maybe I was getting the ArgumentNullException because I was initially using .Include() in a QuickWatch?), but rather a MySqlException: Unknown column 'Join2.Id' in 'field list'; the Data dictionary contains Key: "Server Error Code" Value: 1054. Also the generated SQL is ridiculously large and barely legible.
I figured it out. It was the internal access modifier on class declarations. A shame, because I really wanted to make the rest of the solution entirely database-agnostic (hence the unusual use of DTOs for code first, instead of the actual entities, as was already pointed out in the comments) and I wanted to enforce this in a strict manner.
Anyway, I played around some more with access modifiers and I could only manage restricting the DB object's visibility by making them public with internal protected constructors. Any other combination of class and ctor visibility involving internal caused the problem to reappear. No luck with InternalsVisibleTo, either.
This question - Entity Framework Code First internal class - is it possible? - seems to suggest that using an internal class shouldn't be a problem for EF, but it appears it is, after all, somewhat of a problem. If it wasn't then (Julie Lerman's answer dates back to 2011), it is now. I'm using EF 6.2.0 at the moment.
I've read as many posts as I can on this topic but none of the solutions I have tried seem to work. I have an existing database and created a new Code First From Existing Database project.
I have a base table called Thing. Every object has a record in this table using Id as the Unique Primary Key. Each other object inherits from this but they use the same Id in the child tables without using a new Identity column in the sub tables. Effectively giving each 'Thing' a unique Id:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Car
{
public int Id { get; set; }
//other properties
}
public class Person
{
public int Id { get; set; }
//other properties
}
public class Color
{
public int Id { get; set; }
//other properties
}
Every new record first creates an item in 'Thing' and then using that Id value creates a new record in its respective table, creating multiple 1 to 0..1 relationships where the Id field on the derived tables is also the FK to Thing.
Thing 1 to 0..1 Car
Thing 1 to 0..1 Person
Thing 1 to 0..1 Color
and so on
I have tried many different Data Annotation and Fluent API combinations but it always comes back to the same error:
'Unable to retrieve metadata for Model.Car'. Unable to determine the principal end of association between the types 'Model.Thing' and 'Model.Car'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
I did manage to get past this error by using virtual with the inverse annotation and setting the Id field to be Key and ForeignKey, but then the message jumps to Person. If you then set it up the same as Car the message reverts back to Car.
It seems I could go back and create a normal Foreign Key to each child table, but that is a lot of work and I am sure it is possible to get this working somehow. Preferably using fluent API.
If you are going to use Data Annotations, you need to declare the PK of the dependent entity as FK too:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car{get;set;}
}
public class Car
{
[Key,ForeignKey("Thing")]
public int ThingId { get; set; }
//other properties
public virtual Thing Thing{get;set;}
}
And if you are going to use Fluent Api (remove the attributes from your model), the configuration would be like this:
modelBuilder.Entity<Car>().HasRequired(c=>c.Thing).WithOptional(t=>t.Thing);
Based on the multiplicity that is specified, it only makes sense for Thing to be the principal and Car to be the dependent, since a Thing can exist without a Car but a Car must have a Thing.
As you can see you don't need to specify that ThingId is the FK of this relationship.This is because of Entity Frameworkâs requirement that the primary key of the dependent be used as the foreign key. Since there is no choice, Code First will just infer this for you.
Update
Reading again your question I think you are trying to create a hierarchy. In that case you could use the Table per Type (TPT) approach.
I would like to know what rules Entity Framework follows in regards to the naming/generation of navigation properties. I have observed several scenarios which don't seem to make sense so I was wondering if anyone knows exactly how these work.
Scenario 1:
public class Post
{
public int Id { get; set; }
public User Author { get; set; }
}
Generates
ie. by default navigation properties generate FKs named [PropertyName]_Id
Scenario 2:
It makes sense that if EF generates properties such of the format [PropertyName]_Id when you manually specify a FK Id it will follow the same rules however:
public class Post
{
public int Id { get; set; }
public int? Author_Id { get; set; }
public User Author { get; set; }
}
Generates
As you can see this doesn't automatically register as a nav property.
Scenario 3:
If it doesn't work for Scenario 2 why does it work for an alternate naming convention?
public class Post
{
public int Id { get; set; }
public int? AuthorId { get; set; }
public User Author { get; set; }
}
Generates
What are the rules around navigation property detection and generation?
That is expected behavior and it is based on two different conventions based by EF
In the first example you are using Independent association where your entity doesn't have FK property. EF will create FK in the database using simple pattern: NameOfNavigationProperty_NameOfRelatedPK This convention follows traditional database naming.
In the second example you defined property with the same name as FK used by EF. EF detected this and added 1 to its generated FK. The reason why your property is not used as FK is the second convention which searches for FK properties. This convention expects that FK property will have this name (conventions follows traditional .NET naming):
NameOfNavigationPropertyNameOfRelatedPK provided by NavigationPropertyNameForeignKeyDiscoveryConvention
NameOfRelatedTypeNameOfItsPK provided by TypeNameForeignKeyDiscoveryConvention
NameOfRelatedPK provided by PrimaryKeyNameForeignKeyDiscoveryConvention
In the last example you correctly defined FK property and EF detected it so it uses Foreign key association.
In addition to #Ladislav Mrnka's answer above, you can find a detailed reference of the entity framework default conventions here:
http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.conventions(v=vs.103).aspx
Error:
Unhandled Exception: System.Data.SqlClient.SqlException: The operation failed because an index or statistics with name 'IX_ID' already exists on table 'PrivateMakeUpLessons'.
Model (Simplified, building in a separate test project for debugging):
public abstract class Lesson
{
public Guid ID { get; set; }
public string Room { get; set; }
public TimeSpan Time { get; set; }
public int Duration { get; set; }
}
public abstract class RecurringLesson : Lesson
{
public int DayOfWeek { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Frequency { get; set; }
}
public class PrivateLesson : RecurringLesson
{
public string Student { get; set; }
public string Teacher { get; set; }
public virtual ICollection<Cancellation> Cancellations { get; set; }
}
public class Cancellation
{
public Guid ID { get; set; }
public DateTime Date { get; set; }
public virtual PrivateLesson Lesson { get; set; }
public virtual MakeUpLesson MakeUpLesson { get; set; }
}
public class MakeUpLesson : Lesson
{
public DateTime Date { get; set; }
public string Teacher { get; set; }
public virtual Cancellation Cancellation { get; set; }
}
Configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Lesson>().ToTable("Lessons");
modelBuilder.Entity<RecurringLesson>().ToTable("RecurringLessons");
modelBuilder.Entity<PrivateLesson>().ToTable("PrivateLessons");
modelBuilder.Entity<MakeUpLesson>().ToTable("PrivateMakeUpLessons");
modelBuilder.Entity<Cancellation>()
.HasOptional(x => x.MakeUpLesson)
.WithRequired(x => x.Cancellation);
base.OnModelCreating(modelBuilder);
}
Notes:
This worked fine in EF 4.2. Is there something wrong with my model? The actual model is much more complicated which is why I have all the classes abstracted out. Also, I am working against an existing database so I need to use Table-Per-Type inheritance.
If I change the relationship of Cancellation to PrivateMakeUpLesson from 1 to 0..1 to 0..1 to 0..1 it works. This is undesirable because you can't have a PrivateMakeUpLesson without a Cancellation.
Also, if I make PrivateMakeUpLesson NOT inherit from Lesson then it also works, but it IS a lesson and needs to remain so for existing business logic.
I'd appreciate any guidance. Thank you!
Edit:
Starting a bounty. I can't find any documentation on what changed between EF 4.2 and EF 4.3 with regard to the index generation for code first. It's clear that EF 4.3 is creating more indexes and that the naming scheme has changed but I want to know if there's a bug in EF or if there is something fundamentally wrong with my model or fluent API configuration.
As of EF 4.3, indexes are added for freign key columns during database creation. There is a bug that can cause an index to be created more than once. This will be fixed in a future EF release.
Until then, you can work around the issue by creating your database using Migrations instead of database initializers (or the Database.Create() method).
After generating the initial migration, you will need to delete the redundant call to Index().
CreateTable(
"dbo.PrivateMakeUpLessons",
c => new
{
ID = c.Guid(nullable: false),
...
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Lessons", t => t.ID)
.ForeignKey("dbo.Cancellations", t => t.ID)
.Index(t => t.ID)
.Index(t => t.ID); // <-- Remove this
To continue creating your database at run-time, you can use the MigrateDatabaseToLatestVersion initializer.
In my opinion this is clearly a bug.
The problem starts with the observation that EF creates an index IX_ID at all. If you strip down the model to the following...
public abstract class Lesson
{
public Guid ID { get; set; }
}
public class RecurringLesson : Lesson
{
}
public class MyContext : DbContext
{
public DbSet<Lesson> Lessons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RecurringLesson>().ToTable("RecurringLessons");
}
}
... and let EF create the database schema you get two tables Lessons and RecurringLessons as expected for a TPT inheritance mapping. But I am wondering why it creates two indices for the table RecurringLessons:
Index PK_RecurringLessons (clustered, unique) with Index column ID
Index IX_ID (not clustered, not unique) with Index column ID again
I don't know if there is any benefit for the database to have a second index on the same column. But for my understanding it doesn't make sense 1) to create an index on the same column that is already covered in the PK clustered index, and 2) to create a not unique index on a column which is the primary key and therefore necessarily unique.
Moreover due to the one-to-one relationship EF tries to create an index on the table of the dependent of this association which is PrivateMakeUpLessons. (It's the dependent (and not the principal) because Cancellation is required in entity MakeUpLesson.)
ID is the foreign key in this association (and primary key at the same time because one-to-one relationships are always shared primary key associations in Entity Framework). EF apparently always creates a index on the foreign key of a relationship. But for one-to-many relationships this is not a problem because the FK column is different from the PK column. Not so for one-to-one relatonships: The FK and PK are the same (that is ID), hence EF tries to create an index IX_ID for this one-to-one relationship which already exists due to the TPT inheritance mapping (which leads to a one-to-one relationship as well from database perspective).
The same consideration as above applies here: The table PrivateMakeUpLessons has a clustered PK index on column ID. Why is a second index IX_ID on the same column required at all?
In addition EF doesn't seem to check that it already wants to create an Index with name IX_ID for the TPT inheritance, leading finally to the exception in the database when the DDL is sent to create the database schema.
EF 4.2 (and before) didn't create any indices (except PK indices) at all, this was introduced in EF 4.3, especially indices for FK columns.
I didn't find a workaround. In the worst case you have to create the database schema manually and avoid that EF tries to create it (= disable database initialization). In the best case there is a way to disable automatic FK index creation, but I don't know if it's possible.
You can submit a bug report here: http://connect.microsoft.com/VisualStudio
Or maybe someone from EF development team will see your question here and provide a solution.
I got a very similar error to this one in my code a while back. Try putting the cancellation list inside the Lesson class. That's what solved my problem.
Below I describe 2 scenarios what is probably going wrong. Please read in depth by clicking the links I provided to know more about my explanation.
First
Lesson and RecurringLesson are abstract classes (so you want to have it as the base classes).
You are creating a table of the Lesson and the RecurringLesson entities which will result in a Table per hierarchy structure.
brief description
Creating a class of the base table will result in one big table which contains the columns of all inherited tables. So all properties of PrivateLesson, MakeUpLesson and all others inherited entities will be stored in the Lessons table. EF will add also a Discriminator column. The value of this column defaults to the persistent class name (like "PrivateLesson" or "MakeUpLesson") only the column matching to that particular entity (matching the Discriminator value) will be used in that particular row.
BUT
You are also mapping the inherited classes like PrivateLesson and MakeUpLesson. This will force EF to use the Table per Type structure which results in one table per class. This can cause conflicts you are facing right now.
Second
Your example shows you have an one-to-one relationship (Cancellation -> MakeUpLesson) and a one-to-many relationship (Cancellation -> PrivateLesson) because PrivateLesson and MakeUpLessonare both (indirect) inherited from Lesson in combination with the first described scenario can cause problems because it will result in 2 foreign key relationships in the database per entity. (one using Table per hierarchy structure and one using the Table per Type structure).
Also this post can help you defining a correct one-to-one definition.
Please verify by performing the following steps:
I assume you have your own test environment so you can create new test databases
1.
Delete the relationships to the Cancellation by commenting out all properties to this class:
public class PrivateLesson : RecurringLesson
{
public string Student { get; set; }
public string Teacher { get; set; }
//public virtual ICollection<Cancellation> Cancellations { get; set; }
}
public class Cancellation
{
public Guid ID { get; set; }
public DateTime Date { get; set; }
//public virtual PrivateLesson Lesson { get; set; }
//public virtual MakeUpLesson MakeUpLesson { get; set; }
}
public class MakeUpLesson : Lesson
{
public DateTime Date { get; set; }
public string Teacher { get; set; }
//public virtual Cancellation Cancellation { get; set; }
}
And remove the configuration to it:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Lesson>().ToTable("Lessons");
modelBuilder.Entity<RecurringLesson>().ToTable("RecurringLessons");
modelBuilder.Entity<PrivateLesson>().ToTable("PrivateLessons");
modelBuilder.Entity<MakeUpLesson>().ToTable("PrivateMakeUpLessons");
//modelBuilder.Entity<Cancellation>()
// .HasOptional(x => x.MakeUpLesson)
// .WithRequired(x => x.Cancellation);
base.OnModelCreating(modelBuilder);
}
2.
Create a new empty database
3.
Let EF generate the table structure for you in this empty database.
4.
Verify the first scenario. If that's true this need to be fixed first by using the Table per hierarchy structure OR the Table per Type structure. Probably you want to use the Table per hierarchy structure because (if I understand your question well) there is already an production environment.
When my project was updated from EF 6.0.2 to EF 6.1.1, I had such a problem, then back to 6.0.2, after the return of an older version, the error disappeared