I'm using EntityFramework via DbContext and an Exisiting Database.
When I Add an Order entity to my context and call SaveChanges(), I'm encountering an exception of: "A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: OrderId".
I believe this is happening because of the composite key on my OrderAddress table and I'm hoping there is a way around it...I don't want to create an IDENTITY on that table.
Here are my entities, simplified...
// OrderId is an IDENTITY PK
public class Order
{
public int OrderId { get; set; }
public IList<OrderAddress> Addresses { get; set; }
public int Total { get; set; }
}
// PK on this table is composite key of OrderId and OrderAddressTypeId
public class OrderAddress
{
public int OrderId { get; set; }
public int OrderAddressTypeId { get; set; }
public string Address { get; set; }
}
Here is my Context, simplified...
public class StoreContext : DbContext
{
DbSet<Order> Orders { get; set; }
DbSet<OrderAddress> OrderAddresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Set the Identity for Order
modelBuilder.Entity<Order>()
.Property(x => x.OrderId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Set composite key for Order Address
modelBuilder.Entity<OrderAddress>()
.HasKey(x => new { x.OrderId, x.OrderAddressTypeId });
}
}
NOTE: I've looked at the other SO questions that are similar and tried the solutions listed with no success. I've verified that my foreign keys are setup correctly in the database. What's different about my question is the use of the composite key.
Thanks in advance for the help.
UPDATE:
This ended up not being related to the composite key at all. There was an additional line in my Context OnModelCreating method that required a child entity, OrderSummary, which is based on a View in my database. The line looked like this...
modelBuilder.Entity<OrderSummary>().HasRequired(x => x.Order).WithRequiredPrincipal(x => x.OrderSummary);
I had never intended for OrderSummary to be a required principal of Order. Changing it to the following fixed the problem...
modelBuilder.Entity<OrderSummary>().HasRequired(x => x.Order);
Unfortunately, the error message from EF was not very specific and lead me on a wild good chase.
Thanks for looking.
This error says that some OrderId property (the exception should contain information about the entity or relation where this happens) is mapped as store generated = it has DatabaseGeneratedOption set to Identity or Computed. If the issue is related to OrderAddress entity try to add this to your mapping definition:
modelBuilder.Entity<OrderAddress>()
.Property(x => x.OrderId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Related
OK so this should be simple. I have a class
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
}
These two navigation properties are also primary keys for the table.
Declaring PRoductId and CategoryIds are redundat. How can get configure the primary keys using the nav properties?
edit: Stupid me. I forgot something very important in my question above. Those two above are to point out the config. Then we have a third fk thats the selected config for the combination of Product and category. So above entity must be a materialized entity
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
public ProductCategoryType { get; set; }
}
Declaring ProductId and CategoryId are redundant. How can get configure the primary keys using the nav properties?
Shortly - you can't. While EF6 supports shadow property based FKs, it does not provide a way to configure the PK (and many other column related settings) using the shadow property names - [Key], [Column]data annotations cannot be applied on navigation property and HasKey fluent API requires primitive property selector expression. In general EF6 does not support shadow properties in PK.
All these limitations have been removed in EF Core. But in EF6, redundant or not, you must define the actual primitive properties in the entity and map them to the composite PK.
You have only to set up a relationship between Product and Category entities by navigation properties. EF will set up the correct table structure by its own as many-to-many relationship. So no own relationship entity is needed.
Please check this out: many-to-many-relationship in EF
e.g.:
Product class:
public class Product
{
// other properties
public virtual ICollection<Category> Categories { get; set; }
}
Category class:
public class Category
{
// other properties
public virtual ICollection<Product> Products { get; set; }
}
Or did I misunderstood your question?
EDIT:
IF you need an separate entity like your ProductConfig, than you should try to set it as a unique index constraint by following:
modelBuilder
.Entity<ProductConfig>()
.HasIndex(pc => new {pc.Category, pc.Product})
.IsUnique();
For further information you should read this: HasIndex - Fluent API
EDIT 2 (after getting info solution is for EF < 6.2 needed):
Well after your last question edit, another solution approach is needed.
Here we go...
You need a structure like followed:
Product
public class Product
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
Category
public class Category
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
ProductConfig
public class ProductConfig
{
// other properties
public virtual Category { get; set; }
public virtual Product { get; set; }
public virtual ProductCategoryType { get; set; }
}
To set up a unique constraint in EF < 6.2 you have to do it like that way:
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Category)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 1) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Product)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 2) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.ProductCategoryType)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 3) { IsUnique = true }));
in EF 6.2:
modelBuilder.Entity<Person>()
.HasIndex(p => new { p.Category, p.Product, p.ProductCategoryType })
.IsUnique();
EDIT 3
If you have no primary key in your ProductConfig class or you used mine in the example where I added none, because I thought you already have that class.
It is possible to set up multiple properties as key. That will result in unique combinations too.
You would archive that with the following - instead of the index stuff:
modelBuilder.Entity<ProductConfig>()
.HasKey(pc => new { pc.Category, pc.Product, pc.ProductCategoryType });
For further information check out the MS docs.
You could also add an Id as primary key, than the indexes are needed.
At first I created a Person model which only contained the basic properties:
[Table("SGDB_Persons")]
public class Person {
public int Id { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
[Required]
public Department Department { get; set; }
[Required]
public SourceType SourceType { get; set; }
After I noticed I'm missing something I've added a new PersonData Property:
[Required]
public PersonData PersonData { get; set; }
Unfortunately EF won't update the Database at all - PersonData which at first contained an object of type Person got updated so there is no Person property anymore. On the other hand, EF does not create a new Column for PersonData_Id.
Additionally the ID column is not auto-incrementing (all other table's Id Column do). What's confusing me is the following Constraing which gets created inside my Person table:
CONSTRAINT [FK_dbo.SGDB_Persons_dbo.SGDB_PersonData_Id] FOREIGN KEY ([Id]) REFERENCES [dbo].[SGDB_PersonData] ([Id])
I tried everything (at least I think so). I dropped all tables / the whole database manually, reinstalled EF, executed manual migrations but nothing seems to work.
I think it's this problem which causes me to not be able to seed my database with the following code:
protected override void Seed(PersonContext context) {
base.Seed(context);
var dep = new DepartmentContext().Departments.First();
var status = new Status("Test");
var persondata = new PersonData(status);
context.Status.Add(status);
context.PersonData.Add(persondata);
for (int i = 0; i < 10; i++) {
var person = new Person {
Firstname = $"TestPersonFirstname{i}",
Lastname = $"TestPersonLastname{i}",
SourceType = COM.SourceType.Manual,
Department = dep,
PersonData = persondata
};
context.Persons.Add(person);
}
context.SaveChanges();
}
Everytime this code get's executed I'm getting an Exception:
The member with identity 'SGDB.DAL.Contexts.Person_Department' does not exist in the metadata collection. Parameter name: identity.
I don't know if both problems are related to the same problem but both need to be resolved :)
Thanks in advance!
Update 1
My Solution is divided into a few different Projects:
BLL, DAL, COM, UI
DataContexts are located inside the DAL project, Models inside the COM project.
Department Model:
[Table("SGDB_Departments")]
public class Department {
public int Id { get; set; }
[Required]
public string Costcenter { get; set; }
[Required]
public string Abbreviation { get; set; }
public string Description { get; set; }
public string FullDepartmentName {
get {
return $#"{Division.Abbreviation}\{Abbreviation}";
}
}
[Required]
public virtual Division Division { get; set; }
}
PersonData Model:
[Table("SGDB_PersonData")]
public class PersonData {
public PersonData(Status status) {
Status = status;
}
public int Id { get; set; }
public DateTime Limit { get; set; }
public Person Responsible { get; set; }
[Required]
public Status Status { get; set; }
}
The Person table (as you can see) has got a Department_Id column (EF inserted automatically).
Clarification
A Person object contains a PersonData object as additional Information for this Person. A Person may / may not has a Responsible Person (so PersonData.Responsible is not a Navigation Property to the Parent Person).
Additionaly if possible I don't want to have a foreign Key inside the PersonData table.
As I figured out I'd have to modify
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal(e => e.Responsible)
.WillCascadeOnDelete(true);
to
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithMany()
.WillCascadeOnDelete(true);
I'll try this and report in if it solved my problem.
Update 2
The member with identity 'SGDB.DAL.Contexts.Person_Department' does not exist in the metadata collection.
Your model defines one-to-one relationship between Person and PersonData with later being required and the former - optional. EF always uses the required side of the one-to-one relationship as principal and optional part as dependent. Hence it thinks PersonaData is the principal and Person - dependent and reflects that in database table design.
You need the opposite and also both sides being required. When both sides are required or optional, EF cannot automatically derive the principal/dependent side and there is no way to specify that via data annotations (attributes), so you need a fluent API setup.
Override your DbContext OnModelCreating and add something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal(e => e.Responsible)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
What it does is to tell EF that both sides of the Person->PersonData relationship are required and Person is the principal. This should make again your Person.Id column auto-increment and should resolve the person-data part of the problem.
The other thing I've noticed is this line:
var dep = new DepartmentContext().Departments.First();
while all other parts of the same procedure are using a variable called context. This might/might not be a problem, just check it out.
UPDATE: From the clarification in the updated question it turns out you have two relationships between Person and PersonData, so you need separate configuration for each of them like this:
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal()
.WillCascadeOnDelete(true);
modelBuilder.Entity<PersonData>()
.HasOptional(e => e.Responsible)
.WithOptionalDependent() // or WithMany()
.WillCascadeOnDelete(false);
Please note that there is no way to not introduce additional FK column in the PersonData table. It's needed to represent the Responsible relation, so you'll end up with a table column called Responsible_Id.
I have the following data-model in Entity Framework 6.1.3:
using System.Data.Entity;
public class Student
{
public int Id { get; set; }
public virtual Contact Contact { get; set; }
}
public class Contact
{
public int Id { get; set; }
public virtual Student Student { get; set; }
}
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<Contact>()
.HasOptional(x => x.Student)
.WithOptionalDependent(x => x.Contact)
.WillCascadeOnDelete(true);
}
}
public static class Program
{
private static void Main()
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var context = new MyContext())
context.Database.Initialize(force: true);
}
}
When I launch this code, I get exactly the right table structure I am aiming for:
dbo.Contacts
Id (PK)
Student_Id (FK, NULL, CASCADE ON DELETE)
dbo.Students
Id (PK)
However, now I would like to add the Student_Id property to be available in the Contact entity. So I can read the Student_Id without needing to join the other table through .Student.Id navigation.
If I add the property to the Contact entity, I end up either with two columns Student_Id and Student_Id1, or I end up with an error message saying Each property name in a type must be unique..
The column is already in the database, all I need is to have it in the entity as well, why is it so much trouble? Is there a solution?
I managed to get a response from the Entity Framework Program Manager after asking on GitHub.
Unfortunately this is a limitation of EF6. You can not have a foreign key property in a one-to-one relationship, unless it is also the primary key property. This is essentially because EF6 doesn't support alternate keys/unique indexes, so you can't enforce that a non-primary key property is unique. The fact that you can do it when the foreign key property isn't in the entity is a bit of a quirk... but obviously not something we would remove 😄.
BTW alternate keys (and therefore this scenario) is supported in EF Core.
– Rowan Miller #
https://github.com/aspnet/EntityFramework6/issues/159#issuecomment-274889438
If you want to declare the FK property in the dependent entity in an one to one relationship, I'm afraid you must use it as a PK too. EF Code First requires that PK of the dependent entity must be FK of the relationship too:
public class Contact
{
[Key,ForeignKey("Student")]
public int StudentId { get; set; }
public virtual Student Student { get; set; }
}
But I think this is not what you are looking for. So, I think you have three options here:
You preserve your current relationship configuration.
Create an authentic one to one relationship.
Create an one to many relationship
By my experience the last one is the most adjusted to what are you trying to achieve (but that is my opinion). In this case you can work with the Fk property as you want, the only is you need to change the Contact navigation property on Student by a collection (or omit this nav. property and create an unidirectional relationship):
public class Student
{
public int Id { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
The configuration would be this way:
builder.Entity<Contact>()
.HasOptional(x => x.Student)
.WithMany(x => x.Contacts)
.HasForeignKey(x => x.StudentId)
.WillCascadeOnDelete(true);
Update
A fourth option could be create two unidirectional relationships:
builder.Entity<Contact>()
.HasOptional(x => x.Student)
.WithMany()
.HasForeignKey(x => x.StudentId)
.WillCascadeOnDelete(true);
builder.Entity<Student>()
.HasOptional(x => x.Contact)
.WithMany()
.HasForeignKey(x => x.ContactId)
.WillCascadeOnDelete(true);
But this option breaks the real relation between the two tables.
I'm working on a project that required an update to Entity Framework 5. This has required some slight changes to the entity and configuration classes (code first) to bring the data layer current. The upgrade is complete, except for one remaining entity. When executing queries for this entity, I'm getting the following error:
System.InvalidOperationException: A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
The entity models a table that contains two optional, foreign keys that relate to another table in the database.
Here is a portion of what the tables look like in the database (first, the table in question):
LocationMap Location
----------- --------
Id (PK, not null) Id (PK, not null)
SourceId (FK, null) ...
TargetId (FK, null)
In this model, both LocationMap.SourceId and LocationMap.TargetId refer to Location.Id. Here is the portion of the entity and configuration classes used to represent this relationship in my data layer:
public class LocationMap
{
public int Id { get; set; }
public int SourceId { get; set; }
public int TargetId { get; set; }
...
public virtual Location Source { get; set; }
public virtual Location Target { get; set; }
}
public class Location
{
public int Id { get; set; }
...
public virtual ICollection<LocationMap> TargetMaps { get; set; }
public virtual ICollection<LocationMap> SourceMaps { get; set; }
}
public LocationMapConfiguration()
{
HasKey(x => x.Id);
HasRequired(map => map.Source)
.WithMany(location => location.SourceMaps)
.HasForeignKey(map => map.SourceId)
.WillCascadeOnDelete(false);
HasRequired(map => map.Target)
.WithMany(location => location.TargetMaps)
.HasForeignKey(map => map.TargetId)
.WillCascadeOnDelete(false);
}
public LocationConfiguration()
{
HasKey(x => x.Id);
...
}
When running the following code ...
using (var context = new MyDbContext())
{
var map = context.LocationMaps
.FirstOrDefault();
Logger.Info("Source name: {0}", map.Source.Name);
Logger.Info("Target name: {0}", map.Target.Name);
}
... map.Source.Name works, while map.Target.Name produces the exception above. It does not matter how the two mappings are called - Source always works and Target always throws the exception.
The original Location entity class did not have the ICollection navigational properties defined, and was in fact how I set this up when creating the updated data layer. It was in doing research for the exception that multiple sources (including several here) involved solutions implementing the navigational properties in the fashion displayed in the examples. Thus, I added them, but it has not resolved my issue.
As usual, any help on this would be greatly appreciated.
Thanks!
My SQL understanding is fairly basic and I come from a world of NHibernate so I'm fairly puzzled by this issue...
I have two classes:
public class UserProfile
{
public UserProfile() { }
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
public SerialNumber Serial { get; set; }
// Other properties
}
and
public class SerialNumber
{
public SerialNumber()
{
// My code that creates a unique Code
}
[Key]
public string Code { get; set; }
public UserProfile User { get; set; }
// Other Properties
}
I have this
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Other modelBuilder stuff
modelBuilder.Entity<UserProfile>()
.HasOptional(s => s.Serial)
.WithRequired(u => u.User);
}
After I run Add-Migration I get this:
public override void Up()
{
DropForeignKey("dbo.UserProfile", "Serial_Code", "dbo.SerialNumbers");
DropIndex("dbo.UserProfile", new[] { "Serial_Code" });
RenameColumn(table: "dbo.SerialNumbers", name: "Serial_Code", newName: "User_UserId");
AddForeignKey("dbo.SerialNumbers", "User_UserId", "dbo.UserProfile", "UserId");
CreateIndex("dbo.SerialNumbers", "User_UserId");
}
public override void Down()
{
// Inverse of above
}
After I run Update-Database I get an error:
"Either the parameter #objname is ambiguous or the claimed #objtype (COLUMN) is wrong."
I can tell by the code generated by Add-Migration that something is wrong as my db table doesn't even have a column by the name of "Serial_Code"
I need Code to be my Key as EntityFramework doesn't have an option to enforce a column to be Unique except to make it a key.
How do I make Serial to be an optional property of UserProfile and User in SerialNumber to be automatically set when it's added to UserProfile?
By default, in one-to-one relationships, Entity Framework requires the foreign key on the dependent to also be the primary key. This is likely due to the fact that Entity Framework only sets unique constraints on primary keys (or rather the unique constraint is there by virtue of it being a primary key). On the level of the database, the one-to-one is not mapped onto both sides of the relationship, i.e. there's not a foreign key created on both tables. This makes sense when you think about how referential integrity works: if one side of the relationship is removed, it would cause database disintegrity for the other side, resulting in a sort of endless loop.
So, the foreign key is added to the dependent, which in the case of a one-to-zero-to-one like you have here is the end with the required navigation property: your SerialNumber model. That means that to truly create a one-to-one and not a one-to-many, your key on SerialNumber would need to be User_UserId (the auto-generated foreign key column, since you didn't manually specify a foreign key property). To set it up this way, your models would look like:
public class UserProfile
{
[Key]
public int UserId { get; set; }
public SerialNumber Serial { get; set; }
...
}
public class SerialNumber
{
[Key]
[ForeignKey("User")]
public int UserId { get; set; }
public UserProfile User { get; set; }
...
}
Or with Fluent API:
modelBuilder.Entity<SerialNumber>()
.HasRequired(m => m.User)
.WithOptional(m => m.Serial);
(You would need to leave off the [Key] data annotation, so that EF can make the foreign key, they primary key for the table.)
That leaves you without a unique constraint on your Code property, though. You can of course manually set this constraint on your table, or if you're not using automatic migrations, you can alter your migration to create the constraint:
CreateIndex("dbo.SerialNumbers", "Code", unique: true);
The problem here is that Entity Framework will not handle unique validation for you. So, you'll need to maintain integrity manually within your code (i.e., try to lookup a SerialNumber with the Code you're about to save, and only save it if you don't get anything back from the query).
It's kind of a pain, I know, but for the moment, that's all you can do. Hopefully, EF will finally address uniqueness in a future release.