EF Mapping Table Error "Invalid column name XXX_Id" - c#

I am having an issue mapping my tables together. I get the error:
Invalid column name 'Film_Id'.
Here are my Entities:
public class Film
{
[Key]
public Int32 Id { get; set; }
public String Title { get; set; }
public virtual ICollection<NormComparableFilm> NormComparableFilms { get; set; }
}
public class NormComparableFilm
{
[Key]
public Int32 Id { get; set; }
public Int32 FilmId { get; set; }
public Int32 ComparableFilmId { get; set; }
[ForeignKey("FilmId")]
public virtual Film Film { get; set; }
[ForeignKey("ComparableFilmId")]
public virtual Film ComparableFilm { get; set; }
}
Is there a custom mapping in the OnModelCreating() function that I need? I tried adding the following but it fails with a slightly different error:
modelBuilder.Entity<Film>()
.HasMany(f => f.NormComparableFilms)
.WithMany().Map(t => t.MapLeftKey("FilmId")
.MapRightKey("ComparableFilmId")
.ToTable("NormComparableFilms"));
The above gives this error:
Invalid object name 'dbo.NormComparableFilms1'.
I think I'm close but can't seem to get it just right. Any help would be appreciated.

The first error happened because you are creating two relationships between the same entities and Code First convention can identify bidirectional relationships, but not when there are multiple bidirectional relationships between two entities.The reason that there are extra foreign keys (Film_ID) is that Code First was unable to determine which of the two properties in NormComparableFilm that return a Film link up to the ICollection<NormComparableFilm> properties in the Film class. To resolve this Code First needs a little of help . You can use InverseProperty data annotation to specify the correct ends of these relationships, for example:
public class NormComparableFilm
{
[Key]
public int Id { get; set; }
public int FilmId { get; set; }
public int ComparableFilmId { get; set; }
[ForeignKey("FilmId")]
[InverseProperty("NormComparableFilms")]
public virtual Film Film { get; set; }
[ForeignKey("ComparableFilmId")]
public virtual Film ComparableFilm { get; set; }
}
Or remove the data annotation you already are using and add just these configurations:
modelBuilder.Entity<NormComparableFilm>()
.HasRequired(ncf=>ncf.Film)
.WithMany(f=>f.NormComparableFilms)
.HasForeignKey(ncf=>ncf.FilmId);
modelBuilder.Entity<NormComparableFilm>()
.HasRequired(ncf=>ncf.ComparableFilm)
.WithMany()
.HasForeignKey(ncf=>ncf.ComparableFilmId);
If in the second relationship, the ComparableFilm navigation property is optional, you need to change the type of the corresponding FK as nullable:
public class NormComparableFilm
{
//...
public int? ComparableFilmId { get; set; }
}
And use this configuration:
modelBuilder.Entity<NormComparableFilm>()
.HasOptional(ncf=>ncf.ComparableFilm)
.WithMany()
.HasForeignKey(ncf=>ncf.ComparableFilmId);
About the second error, you are trying to call the Film table as NormComparableFilms that is the default name that EF will give by convention to the table represented by the NormComparableFilm entity.
if you need to rename one of your tables, you can use this configuration:
modelBuilder.Entity<Film>().ToTable("Films"));

Related

EF Core - ID1 property added when adding a migration

My scenario is for investment in properties (buildings). An investor makes an investment in a property group, and that group can contain several individual properties. As investors can invest in multiple groups, and any group can have multiple investors, the investment itself is a many-to-many relationship between the investors and property groups.
Ignoring the simple properties for clarity, the models look like this...
public class Investor {
public int ID { get; set; }
public ObservableCollection<Investment> Investments { get; set; }
}
public class Investment {
public int ID { get; set; }
public int InvestorID { get; set; }
public virtual Investor Investor { get; set; }
public int PropertyGroupID { get; set; }
public virtual PropertyGroup PropertyGroup { get; set; }
}
public class PropertyGroup {
public int ID { get; set; }
public ObservableCollection<Property> Properties { get; set; }
public ObservableCollection<Investment> Investments { get; set; }
}
public class Property {
public int ID { get; set; }
public int PropertyGroupID { get; set; }
public virtual PropertyGroup PropertyGroup { get; set; }
}
This all works fine.
I now have the requirement to generate and store documents. These will always be associated with a specific investor, but may also be associated with a property or a group.
I added the following class...
public class Document {
public int ID { get; set; }
// Other simple properties omitted for clarity
public int InvestorId { get; set; }
public Investor Investor { get; set; } = null!;
public int? PropertyGroupId { get; set; }
public PropertyGroup? PropertyGroup { get; set; }
public int? PropertyId { get; set; }
public Property? Property { get; set; }
}
In order to provide the navigation links into the documents, I added the following line into the Investor, PropertyGroup and Property models...
public ObservableCollection<Investment> Documents { get; set; }
When I try to add a migration, I get an error "Unable to determine the relationship represented by navigation 'Investment.Investor' of type 'Investor'."
Reading the output from the migration, I see a message "No relationship from 'Investment' to 'Investor' has been configured by convention because there are multiple properties on one entity type - {'Investor'} that could be matched with the properties on the other entity type - {'Documents', 'Investments'}."
I don't understand either message, as I do have an explicit relationship defined from Investment to Investor as you can see above. I have an int property called InvestorId and a navigation property of type (and name) Investor. I'm not sure what the last bit of the second message means.
However, I added the following to OnModelCreating...
builder.Entity<Investment>()
.HasOne<Investor>()
.WithMany(i => i.Investments)
.HasForeignKey(i => i.InvestorID)
.OnDelete(DeleteBehavior.Restrict);
This got rid of that error, but gave a similar one, "Unable to determine the relationship represented by navigation 'Investment.PropertyGroup' of type 'PropertyGroup'." I added the following...
builder.Entity<Investment>()
.HasOne<PropertyGroup>()
.WithMany(i => i.Investments)
.HasForeignKey(i => i.PropertyGroupID)
.OnDelete(DeleteBehavior.Restrict);
This allowed the migration to be added, but it included the following in the Up method...
migrationBuilder.AddColumn<int>(
name: "InvestorID1",
table: "Investments",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "PropertyGroupID1",
table: "Investments",
type: "int",
nullable: false,
defaultValue: 0);
I can see the migration output contains a warning "The foreign key property 'Investment.InvestorID1' was created in shadow state because a conflicting property with the simple name 'InvestorID' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type." but I don't understand what it means.
Anyone able to explain what I'm doing wrong?
Thanks
There is something missed(typo), to make relation with Document you have to define collection props of Document not Investments so change
public ObservableCollection<Investment> Documents { get; set; }
to
public ObservableCollection<Document> Documents { get; set; }

double relationship between entities in EntityFramework 6

My problem looks simple. I need to implement a relationships between items in the database. For example: relationship between entities like computer and software shows users that computer stores a specific software and similarly - a software is installed in the specific computer. I think I should implement an entity with source id and target id or something similar. I wrote some code using code first in EntityFramework 6. Here are two classes:
public class ConfigurationItem
{
public int Id { get; set; }
public String Name { get; set; }
public String DeploymentState { get; set; }
public String IncidentState { get; set; }
[DataType(DataType.MultilineText)]
public String Description { get; set; }
[DataType(DataType.MultilineText)]
public String Note { get; set; }
public virtual ICollection<Relationship> Relationship { get; set; }
}
public class Relationship
{
[Key]
public int RelationshipId { get; set; }
[ForeignKey("ConfigurationItem")]
public int SourceId { get; set; }
[ForeignKey("ConfigurationItem")]
public int TargetId { get; set; }
public String Type { get; set; }
public virtual ConfigurationItem Source { get; set; }
public virtual ConfigurationItem Target { get; set; }
}
This solution doesn't work. I need a tip or something what should I try to make it work properly. EF throws an error about foreign key:
The ForeignKeyAttribute on property 'SourceId' on type 'cms_1.Models.Relationship' is not valid. The navigation property 'ConfigurationItem' was not found on the dependent type 'cms_1.Models.Relationship'. The Name value should be a valid navigation property name.
When I try to resolve it EF throws an error about cascade deleting. I know how to disable it but I just don't want to. I need a proper solution with that feature but I think I don't know how to do a model representing given scenario.
Simply - I need to store two foreign keys from entity "A" in the entity "B". How is it possible?
from a quick review , I can tell that you need 3 tables :
first : Computer
second : Software
third : a table , lets call it ComputerSoftware which tell which software has in what computer ( or you can also see it - which computer use what software ), which has ComputerID column and SoftwareID column.
example (source)
class Country
{
public int Id { get; set; }
public virtual ICollection<CountryCurrency> CountryCurrencies { get; set; }
}
class Currency
{
public int Id { get; set; }
}
class CountryCurrency
{
[Key, Column(Order=0)]
public virtual int CountryId { get; set; }
[Key, Column(Order=1)]
public virtual int CurrencyId { get; set; }
public virtual Country Country { get; set; }
public virtual Currency Currency { get; set; }
}
Your issue could be that in the migration file creating those tables, it will have something like
.ForeignKey("dbo.Relationship", t => t.Id, cascadeDelete: true)
This will be set on both tables, ConfigurationItem and Relationship of their Primary Key fields. When you delete one, that config tells SQL Server to delete the relationships as well and the relationship probably has a cascadeDelete: true to the parent. This will cause the cyclical cascading delete issue you are experiencing.
After the migration has been generated, go in and change one or all to cascadeDelete: false and this will fix that issue. This is what EF generates by default if I recall.

Invalid Column Name Error When Trying to Associate Objects with EntityFramework

The entity framework is giving me some trouble.
Here are my tables:
Products
Related Products
And my DB context (part of it, anyway)...
ApplicationContext.cs
public class ApplicationContext : DbContext
{
public ApplicationContext() : base("DefaultConnection")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<RelatedProduct> RelatedProducts { get; set; }
}
And the models...
Product.cs
public class Product
{
public int ID { get; set; }
public string Manufacturer { get; set; }
public string Model { get; set; }
public string PartNumber { get; set; }
public int CategoryID { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public virtual ICollection<RelatedProduct> RelatedProducts { get; set; }
}
RelatedProduct.cs
public class RelatedProduct
{
public int ID { get; set; }
public int OwnerID { get; set; }
public int ProductID { get; set; }
public virtual Product Owner { get; set; }
public virtual Product Product { get; set; }
}
What I am trying to do
Loop through a list of related products like this:
<ul class="activity">
#foreach (var related in #Model.RelatedProducts)
{
<li>
<i class="fa fa-chevron-right red"></i> <strong>#related.Product.Manufacturer:</strong> #related.Product.Model
</li>
}
</ul>
The Problem
I keep getting this errorL
{"Invalid column name 'Product_ID'.\r\nInvalid column name 'Product_ID'.\r\nInvalid column name 'Product_ID'."}
Any ideas?
You must tell EF if Product.RelatedProducts is the inverse navigation property of RelatedProduct.Owner or of RelatedProduct.Product. Both would be possible and valid and EF can't decide it on its own.
Solution with data annotations (assuming Owner is the inverse of RelatedProducts):
[InverseProperty("RelatedProducts")]
public virtual Product Owner { get; set; }
public virtual Product Product { get; set; }
Or with Fluent API:
modelBuilder.Entity<RelatedProduct>()
.HasRequired(r => r.Owner)
.WithMany(p => p.RelatedProducts)
.HasForeignKey(r => r.OwnerID);
modelBuilder.Entity<RelatedProduct>()
.HasRequired(r => r.Product)
.WithMany()
.HasForeignKey(r => r.ProductID)
.WillCascadeOnDelete(false);
Probably only the Fluent API solution will work because you also need to disable cascading delete for one of the relationships which isn't possible with data annotations.
Actually, the problem here was Entity Framework's "conventions over configuration". The convention for a foreign key column, when it is not explicitly added as a property, is to name it [RelatedProperty]_[RelatedClassPK]. In your situation that would be Owner_ID and Product_ID. However, these columns don't exist on your tables, so you get an error. The usual fix is to just tell EF explicitly what your foreign key properties are:
[ForeignKey("Owner")]
public int OwnerID { get; set; }
public virtual Product Owner { get; set; }
With that, Entity Framework looks for an OwnerID column, instead, and all is good. #Slauma inadvertently solved the underlying problem with the Fluent API approach, which explicitly declares the properties to use for the foreign keys with HasForeignKey. This is an equally valid approach, but I felt you should know what was actually your problem.

How to make proper code-first relations

I'm fairly new to Entity Framework and feel more in control using the Code-First pattern rather than DB-First.
I was wondering what is more preferred when it comes to programmatically setting up ForeignKey relations between the entities.
Is it better to declare a FK_ property in the class which relates to the another class or is it better to declare an IEnumerable<> property in the class that gets related to?
public class IRelateToAnotherClass
{
...
public int FK_IGetRelatedToByAnotherClass_ID { get; set; }
}
or
public class IGetRelatedToByAnotherClass
{
...
public IEnumerable<IRelateToAnotherClass> RelatedTo { get; set; }
}
It all depends on what type of relationships you want between your entities (one-to-one, one-to-many, many-to-many); but, yes, you should declare foreign key properties. Check out this site for some examples.
Here's a one-to-many for your two classes:
public class IRelateToAnotherClass
{
public int Id { get; set; } // primary key
public virtual ICollection<IGetRelatedToByAnotherClass> IGetRelatedToByAnotherClasses { get; set; }
}
public class IGetRelatedToByAnotherClass
{
public int Id { get; set; } // primary key
public int IRelateToAnotherClassId { get; set; } // foreign key
public virtual IRelateToAnotherClass IRelateToAnotherClass { get; set; }
}
and with some Fluent API mapping:
modelBuilder.Entity<IGetRelatedToByAnotherClass>.HasRequired<IRelateToAnotherClass>(p => p.IRelateToAnotherClass).WithMany(p => p.IGetRelatedToByAnotherClasses).HasForeignKey(p => p.Id);
If I understand what you're asking correctly, you'd want both. You want an int FK property and an object property to use as the navigation property.
The end result would look something like this:
public class Employee
{
[Key]
public int EmployeeID { get; set; }
[ForeignKey("Store")]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual Store Store { get; set; }
}
public class Store
{
[Key]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual List<Employee> Employees { get; set; }
}
If you haven't already, take a look at navigation properties and lazy-loading. Note that EF is clever enough to figure out that an int StoreID property corresponds to an object Store property, but if they are named differently (such as without the ID suffix), you must use the [ForeignKey] annotation.

Issue with relational tables in Entity Framework

I have a one-to-many relationship of order to payment:
public class Order
{
[Key]
public Guid SerialNumber { get; set; }
public string OrderNumber { get; set; }
...
[ForeignKey("OrderNumber")]
public virtual ICollection<Payment> Payments { get; set; }
}
public class Payment
{
[Key]
public string SerialNumber { get; set; }
public string OrderNumber { get; set; }
...
public decimal Amount { get; set; }
}
Despite the records being available, the Payments collection always shows 0 elements. I've looked at the trace and the problem seems to be with the generated SQL query - it's trying to match Order.SerialNumber to Payment.OrderNumber.
How can I resolve this, preferably with data annotations? Thanks in advance!
it's trying to match Order.SerialNumber to Payment.OrderNumber.
Yes because that is exactly what you have modeled. ForeignKey attribute doesn't describe the name of foreign key property in the related table. It describes the name of foreign key property in the same table and it is used to pair navigation property with its key so the correct usage is:
public class Order
{
[Key]
public Guid SerialNumber { get; set; }
public string OrderNumber { get; set; }
...
public virtual ICollection<Payment> Payments { get; set; }
}
public class Payment
{
[Key]
public string SerialNumber { get; set; }
// Foreign key must have the same type as primary key in the principal table
public Guid OrderNumber { get; set; }
...
public decimal Amount { get; set; }
// Reverse navigation property to principal associated with foreign key
[ForeignKey("OrderNumber")]
public virtual Order Order { get; set; }
}
If you don't want navigation property in Payment you have to use fluent API to describe the mapping:
modelBuilder.Entity<Order>()
.HasMany(o => o.Payments)
.WithRequired()
.HasForeignKey(p => p.OrderNumber);
If you have existing database and your relation really targets OrderNumber column in the Order table it means it must be marked with unique constraint - such relation currently cannot be mapped in EF because it doesn't support unique constraints yet.

Categories