EF and Linked Tables - c#

I think I am missing something simple here. I am getting the error:
"Violation of PRIMARY KEY constraint 'PK_FeatureTypes'. Cannot insert duplicate key in object 'dbo.FeatureTypeCodes'. The duplicate key value is (28).\r\nThe statement has been terminated"
I have a look-up / linked table of FeatureType - (Mountain, Lake, River, etc.) which is already populated with data and is defined as:
[Table("FeatureTypeCodes")]
public class FeatureTypeCode {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int FeatureTypeCodeID { get; set; }
public string Name { get; set; }
}
This is linked to my place table / object like this:
[Table("Places")]
public class Place {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int PlaceID { get; set; }
public string Name { get; set; }
public FeatureTypeCode FeatureTypeCode { get; set; }
public ICollection<PlaceCoordinate> PlaceCoordinates { get; set; }
}
Then I am loading them from the old database like this (it is part of my conversion code):
foreach (DataRow r in table.Rows) {
int ftID = Convert.ToInt32(r["FeatureTypeId"]);
Place temp = new Place {
PlaceID = Convert.ToInt32(r["PlaceID"]),
Name = r["PlaceName"].ToString(),
FeatureTypeCode = featureTypeCodeRepository.FeatureTypeCodes.FirstOrDefault(o=>o.FeatureTypeCodeID == ftID)
};
places.Add(temp);
}
The error is being generated when it tries to insert a new FeatureType object with the same ID as an existing object while saving a Place. My thought was that by loading FeatureType from the context it would not attempt to insert a new FeatureType on saving the Place object. I am obviously wrong on that, but is it something simple I am missing?

I don't think that you use the same DBContext Object in your featureTypeCodeRepository and the places.Add(temp);. So I think that basically EF don't keep track of the FeatureTypeCodes becuse it's loaded by one context, and saved by another.

While I think that Simon Edström is right (+1), you may also consider to expose the primitive foreign key field (something like FeatureTypeId?) in your Place class. Then you can simply set
FeatureTypeId = ftID;
If you're not sure whether the FK field value really exists in the FeatureTypeCodes table, you can query for its existence using the featureTypeCodeRepository even when it has a different context. Using Any() is the cheapest way to do that:
var exists = featureTypeCodeRepository.FeatureTypeCodes
.Any(o => o.FeatureTypeCodeID == ftID)
It is not uncommon to do this in entity framework. Relationships consisting of only a reference (like Place.FeatureTypeCode) are called independent associations, those with a reference and a primitive FK property foreign key associations. Julia Lerman in her book DbContext says
unless you have a very
good reason not to expose the foreign key properties you will save yourself a lot of pain
by including them

Related

Entity Framework is incorrectly enforcing a unique constraint across multiple columns

Edit: I'm so sorry I wasted your time. I missed a line of code when debugging that was causing this problem. Someone had put a conditional that was checking if the name already existed in the database and threw this exception if it was.
I am using Entity Framework code first and I have an entity which contains an index with a unique constraint across two columns as shown in the code below. (I changed the names of properties to generic things so as to not show any of my company's code base)
[Table("TB_Table")]
public class Table : IEntity
{
[Key]
[Column("TBT_RID")]
public int Id { get; set; }
[ForeignKey("Something")]
[Index("idxSomethingTableName", 1, IsUnique = true)]
[Column("TBT_SOS_RID")]
public int SomethingId { get; set; }
[Index("idxSomethingTableName", 2, IsUnique = true)]
[MaxLength(255)]
[Column("TBT_Name")]
public string Name { get; set; }
// Navigation properties
[JsonIgnore]
public virtual Something Something { get; set; }
[InverseProperty("Table")]
[JsonIgnore]
public virtual ICollection<AssetTag> ObjectTables { get; set; }
}
When inserting a record in SQL, the unique constraint is being enforced properly. Then, when trying to enter a record through Entity Framework, it tells me that it cannot add a record that has a different "SomethingId" value, but the same "Name" as another record.
For example, I can insert these records with SQL:
insert into TB_Table (TBT_SOS_RID, TGT_Name) values
(1, 'A'),
(1, 'B'),
(30, 'A')
But then I cannot add another (1, 'A') with SQL. Okay, great. The constraint is working correctly. Now, if I try to insert a record using entity framework with the values (30, 'B'), I should be able to do this, because the TBT_SOS_RID (SomethingId in C#) is different. Instead, I get an InvalidOperationException with the message "Invalid table, table already Exists". This happens on the DbSet.Add() method, before calling the SaveChanges() method.
Can you think of any reason why Entity Framework would think that this is a violation of the unique constraint when SQL does not?
It appears that Kryptos answered a similar question on how to handle composite indices with foreign keys here: Composite Indices with Foreign Keys
I've not tested the solution, but it might help lead you down the correct path, expanding on the answer by niaher in the same question.
You decorated Name as follows:
[Index("idxSomethingTableName", 2, IsUnique = true)]
[MaxLength(255)]
[Column("TBT_Name")]
public string Name { get; set; }
As far as Entity is concerned, you have stated that Name must be unique, therefore, you cannot enter two names that are the same.

Entity Framework - Multiple 1 to 0..1 relationships using the same Key

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.

Save a complex entity using EF from JSON

I've been trying to save a complex entity in EF code-first using Json.NET for a couple of days without success.
[Major edit and tl;dr;:] Is there a way to deserialise a JSON object into an entity and keep their relationships?
I can store it the regular way. My problem is after deserialising the object.
By design, the Preferences should added to the database, but their Values are foreign keys (giving a PreferenceValue table).
This is my model (oversimplified for brevity):
public class Preference {
public virtual ICollection<PreferenceAttribute> Attributes { get; set; }
}
public class PreferenceAttribute {
public virtual ICollection<Value> Values { get; set; }
}
public class Value {
public int ValueId { get; set; }
public string Description { get; set; }
}
The values seem not to be attached to the context before saving, causing the engine to store new Values instead of using the foreign keys provided by the JSON object, which looks like:
{
"PreferenceAttributes":[{
"PreferenceTypeId" : 1,
"Values":[
{
"ValueId" : 1
},
{
"ValueId" : 2
},
{
"ValueId" : 3
},
]
}]
}
I can save it whithout any problems directly in C#; the "code" I use to seed the preferences:
var attribute = new PreferenceAttribute {
AttributeId = 1,
Values = context.Values.OrderBy(a => a.ValueId).Skip(1).Take(5).ToList();
};
var preferece = new Preference {
Attributes = new List<PreferenceAttribute> {
attribute
}
};
//user is fetched from "context" as well
user.Preferences.Add(preferece);
context.SaveChanges();
Please, keep in mind that it's just about the Values. The issue is, as noted before, that new Values are being added to the database instead of using their Ids as foreign keys to relate to PrefferenceAttributes, i.e., EF is thinking that I want to add new Values, like so:
attribute.Values = new List<Value> {
new Value {
ValueId = 1, //This id will be ignored by EF since it's not fetched using context; new record will be inserted;
WhateverAttributes = "WTF"
}
}
Best regards.
I think your problem is foreign key records are not referenced by existing record, instead they are newly created and referenced.
I think that issue occurs because all your entities are in Added state. You may want your foreign key records in Unchanged state.
Check this link for entity states http://msdn.microsoft.com/en-us/data/jj592676.aspx
First of all, I would advice you to separate domain model from Dto. Once you do this, you are going to resolve foreign key records manually before you flush them out.

Define Many Tables to One Table relationship in Code First approach

I am in the process of building up a data model in Entity Framework using the Code First approach, but one part has me a bit stumped. The title on this question may be a bit confusing, so I will explain my problem in detail. The length of this post may be daunting, but I think it's a fairly straightforward problem.
I have one model defined like this:
public class KeyValuePair
{
[Key]
[MaxLength(128)]
[Column(Order = 0)]
public virtual string OwnerId { get; set; }
[Key]
[MaxLength(128)]
[Column(Order = 1)]
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
My intent is for this to just define a generic table for storing key-value properties on other entities in the system. I am using GUIDs for all of my Ids, so OwnerId should uniquely refer to one entity in the system, and the pair (OwnerId, Key) should uniquely identify one property on one entity.
In other words, I want to allow multiple tables in my system to have a One->Many relationship to this KeyValuePair table.
So for example, if I wanted to store the height of a Person who has the ID b4fc3e9a-2081-4989-b016-08ddd9f73db0, I would store a row in this table as:
OwnerId = "b4fc3e9a-2081-4989-b016-08ddd9f73db0"
Key = "Height"
Value = "70 in."
So now I want to define navigation properties from the parent entities to this table, like (to take the Person example):
public class Person
{
[Key]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
// I want this to be a navigation property
public ICollection<KeyValuePair> Properties { get; set; }
}
But I'm not sure how do define the relationship between Person and KeyValuePair so that Entity Framework knows that it should look up the Person's properties by matching the Person's Id against the KeyValuePairs' OwnerId. I can't define a foreign key in the KeyValuePair model, because the OwnerId is going to refer to Ids in several different tables.
It looks like I can do the following to define a relationship from Person to KeyValuePair in OnModelCreating:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("OwnerId", "Key");
mp.ToTable("PersonDetail");
});
Or I could even give the KeyValuePairs their own unique IDs, get rid of OwnerId, and do this:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("Id");
mp.ToTable("PersonDetail");
});
But both of these approaches involve the creation of an intermediary table to link the Person and KeyValuePair tables, and that seems like excessive overhead in terms of bloating my database schema and requiring more expensive JOINs to query the data.
So is there a way to define the relationship such that I don't need to involve intermediary tables? Am I going about this database design the wrong way?
Side note: For anyone wondering why I am using this approach to define properties on my entities rather than simply adding fixed properties to the data model, I am using fixed properties in the data model where applicable, but the application I am building requires the ability to define custom properties at runtime. I also think this question is applicable to other potential scenarios where multiple tables have a One->Many relationship to a shared table.
The only way I can think of doing it (and I'll admit, this is not the best of ideas, but it will do what you're asking) would be to have any classes that need to have this relationship with KeyValuePair implement an abstract class that contains the fully implemented navigational property, as well as the ID field. By "fully implemented" I don't mean an actual, mapped relationship; I mean that it should use a DbContext to go out to the KeyValuePair table and actually grab the relevant properties given the ID.
Something like this:
public abstract class HasKeyValuePairs
{
[Key]
public virtual string Id { get; set; }
[NotMapped]
public ICollection<KeyValuePair> Properties
{
get
{
using(var db = new DbContext())
{
return db.KeyValuePairs.Where(kvp => kvp.OwnerID == this.ID);
}
}
}
}
Assuming you're using Lazy Loading (given that you're using the virtual keyword), there shouldn't be much extra overhead to doing it like this, since EF would have to go back to the database anyway to pick up the properties if you ever called for them. You might need to have that return a List just to avoid any potential ContextDisposedException later on in your code, but that at least will get you up and running.

How to work with true self referencing entities in code first EF5?

There are a few questions out there on this topic, but my question is very specific to true self referencing. All the examples for other questions are circular references and that doesn't help me in this case.
Lets say I have this model:
public class User
{
[Key]
public int Id { get; set; }
...
public int CreatedByUserId { get; set; }
}
and this map:
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
this.HasRequired(a => a.CreatedByUser)
.WithMany()
.HasForeignKey(u => u.CreatedByUserId);
}
}
After migrations generates a database with this code I can manually add a User in SQL Management Studio with Id = 1, and CreatedByUserId = 1 so that tells me that self references like this can work.
However when using EF to create a user, I run into a "unable to determine a valid ordering for dependent operations" issue. There are a lot of questions on the matter that involve a new entity that refers another new entity that has a foreign key on the first entity, which is a circular reference. The solution in those cases is either save one of entities first or to have a nullable id on the circular entity foreign key. I can not do either of those because the first would be impossible and the second is a external constraint that I cannot have nullable ids.
So, seeing how I can achieve this by adding a entry manually I can assume it's a limitation of EF5. What are the work arounds?
You can still satisfy your interface and do the save first then set method by adding another property to act as a nullable backer for CreatedByUserId:
public class User : ICreatable
{
[Key]
public int Id { get; set; }
...
public int CreatedByUserId
{
get
{
if (!_CreatedByUserId.HasValue)
//throw new exception, something went wrong.
return _CreatedByUserId;
}
set
{
_CreatedByUserId = value;
}
}
int? _CreatedByUserId { get; set; }
}
You may want to rethink the possibility that a user can create him or herself...
However if you really want to do this then there is a solution. Your main problem is the fact that your column is an IDENTITY column which means that EF doesn't specify the Id, SQL server is giving each row an auto-incrementing Id. Any value you set as the Id is ignored. You don't necessarily know when executing the INSERT what the next Id is going to be so you can't create a reference to a row that doesn't exist yet.
Change your mapping code to something like the following:
this.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.HasRequired(x => x.CreatedByUser)
.WithMany();
You don't need to specify the foreign key if the name pattern matches (eg. CreatedByUser and CreatedByUserId).
Now when you insert a User you can specify the Id and the CreatedById. Although note that you must now always specify the Id to insert a new User. This is common practice if you are using GUIDs as Ids because you can just generate a new GUID without having to first query for the next "available" Id before creating a new object.

Categories