Entity Framework 1-To-* and 1-to-1 - c#

(Entity Framework 6, .NET 4, VS 2010)
I have created a small Blog project to illustrate the problem. This is a Blog that has many posts but only one of the posts act as the main post.
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> PostEntities { get; set; }
public int? MainPostId { get; set; }
[ForeignKey("MainPostId")]
public virtual Post MainPostEntity { get; set; } // Problem here
}
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; }
[ForeignKey("BlogId")]
public virtual Blog BlogEntity { get; set; }
public string Title { get; set; }
}
modelBuilder.Entity<Blog>()
.HasOptional(b => b.MainPostEntity)
.WithRequired(p => p.BlogEntity);
static void Main(string[] args)
{
Database.SetInitializer<EFTestContext>(null);
EFTestContext db = new EFTestContext();
Post[] posts = db.Posts.ToArray(); // Error here
}
If I remove the navigation property public virtual Post MainPostEntity everything works as expected. However, when I add it, I get:
base {System.SystemException} = {"The ForeignKeyAttribute on property 'MainPostEntity' on type 'EFTest.Blog' is not valid. The foreign key name 'MainPostId' was not found on the dependent type 'EFTest.Post'. The Name value should be a comma separated list of foreign key property names."}
If I remove the fluent API call, I get {"Invalid column name 'Blog_Id'."}
If I change the attribute from [ForeignKey("MainPostId")] to [ForeignKey("Id")] I get the following error {"Invalid column name 'Blog_Id'."}
What am I doing wrong?
How do I enable the navigation property from Blog to the Main Post?

The problem you are having is that you're creating two relationships between the same two tables and EF can't distinguish which relationship the navigation property BlogEntity is part of. Using the fluent api you can explicitly tell it so, the data annotations are then not needed.
modelBuilder.Entity<Blog>().HasMany(b => b.PostEntities).
WithRequired(p => p.BlogEntity).
HasForeignKey(p => p.BlogId).
WillCascadeOnDelete(true);
modelBuilder.Entity<Blog>().HasOptional(b => b.MainPostEntity).
WithMany().
HasForeignKey(b => b.MainPostId).
WillCascadeOnDelete(false);

Related

entities that do not expose foreign key properties for their relationships (EF) [duplicate]

This question already has answers here:
An error occurred while saving entities that do not expose foreign key properties for their relationships
(16 answers)
Closed 2 months ago.
When trying to call SaveChanges(), I get the following error:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.'
SqlException: Invalid column name 'Artwork_Id'
I am using Entity Framework.
I'm trying to add an artworkImage that has the Id of an artwork as a reference. All information is being passed correctly but it's not saving.
I've tried adding foreign keys to my models and dbcontext but I've not gotten further than the code below.
public partial class ArtworkImage
{
[Key]
public int Id { get; set; }
public string ImageURL { get; set; }
public Artwork Artwork { get; set; }
}
public partial class Artwork
{
[Key]
public int Id { get; set; }
public string Category { get; set; }
public ICollection<ArtworkImage> ArtworkImage { get; set; }
}
My DbContext:
public DbContext()
: base("name=DbConnection")
{
this.Configuration.AutoDetectChangesEnabled = false;
}
public virtual DbSet<Artwork> Artworks { get; set; }
public virtual DbSet<ArtworkImage> ArtworkImages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Artwork>()
.Property(e => e.Category)
.IsFixedLength();
modelBuilder.Entity<Artwork>()
.HasKey(b => b.Id);
modelBuilder.Entity<ArtworkImage>()
.HasKey(b => b.Id);
Database.SetInitializer<DbContext>(null);
base.OnModelCreating(modelBuilder);
}
I believe I should be adding something like this to my dbcontext but I haven't quite figured it out yet.
modelBuilder.Entity<ArtworkImage>()
.HasRequired(p => p.Artwork)
.WithMany(d => d.ArtworkImage)
.HasForeignKey(p => p.Artwork);
If any information is missing please point it out and I'll add it.
You have to declare primary key on each table. it is a rare occasion when a table has no PK. almost never.
[Key]
public int Id { get; set; }
So part of your problem might be that you don't define the relationship in reverse which I believe is important in how it establishes if the relationship is one-to-one or one-to-many. So you will likely need to add a property on the Artwork class that is of type ArtworkImage (if it is one-to-one). if it is one-to-many you will need to make the property some generic collection with the generic of type ArtworkImage.
One-to-one
public partial class Artwork
{
[Key]
public int Id { get; set; }
public string Category { get; set; }
public ArtworkImage ArtworkImage { get; set; }
}
One-to-many
public partial class Artwork
{
[Key]
public int Id { get; set; }
public string Category { get; set; }
public IEnumerable<ArtworkImage> ArtworkImages { get; set; }
}

How to rename an autogenerated n:m table in EF 6? [duplicate]

This question already has an answer here:
EF 6, code first junction table name
(1 answer)
Closed 2 years ago.
I set up a database using EF 6. I configured a many-to-many relationship successfully with the code seen below:
public class Software
{
public string Id { get; set; }
public string Version { get; set; }
public string Name { get; set; }
// Navigation property for machineSoftware
public virtual ICollection<Machine> MachineSoftwares { get; set; }
}
public class Machine
{
public int MachineId { get; set; }
public string Model { get; set; }
public string Customer { get; set; }
public virtual ICollection<Software> MachineSoftwares { get; set; }
}
EF 6 created a third table MachineSoftwares automatically. So far so good.
Now I want to rename that table, but I don't have a corresponding class for that table that I could rename via migration. So how can I rename the table MachineSoftwares?
The name of the implicit many-to-many junction table (and the names of its columns) are configured through the Map Fluent API.
But in order to get access to that API, you have to first map the relationship using a HasMany / WithMany pair:
modelBuilder.Entity<Software>()
.HasMany(e => e.MachineSoftwares)
.WithMany(e => e.MachineSoftwares)
.Map(config => config
.ToTable("the_desired_table_name")
//.MapLeftKey("SoftwareId") // in case you need different column names
//.MapRightKey("MachineId")
);
you can add Table attribute to your code, EF have more options for attributes, for example the name of column:
[Table("custom_name_table")]
public class Software
{
public string Id { get; set; }
[Column("the_version_name")]
public string Version { get; set; }
public string Name { get; set; }
// Navigation property for machineSoftware
public virtual ICollection<Machine> MachineSoftwares { get; set; }
}
check this link https://www.entityframeworktutorial.net/code-first/table-dataannotations-attribute-in-code-first.aspx

EF Mapping Table Error "Invalid column name XXX_Id"

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"));

Entity Framework: Two foreign keys connected by one collection

I know that there is related topic: two Foreign Keys from same table, but I can't find there fix to my problem. I am pretty new to EF.
I have the following model classes (Code-First):
public class Member
{
[Key]
public int MemberID { get; set; }
public string Name {get; set;}
public string Surname { get; set; }
public virtual ICollection<Marriage> Marriages { get; set; }
}
public class Marriage
{
[Key]
public int MarriageID { get; set; }
public string MarriagePlace { get; set; }
public DateTime MarriageDate { get; set; }
[ForeignKey("Husband")]
public int HusbandID { get; set; }
[ForeignKey("Wife")]
public int WifeID { get; set; }
public virtual Member Husband { get; set; }
public virtual Member Wife { get; set; }
}
My problem is that both Husband and Wife should be connected to the same Marriage collection in Member class. I did that:
modelBuilder.Entity<Marriage>()
.HasRequired<Member>(m => m.Husband)
.WithMany(m => m.Marriages)
.HasForeignKey(m => m.HusbandID)
.WillCascadeOnDelete(false);
And husband is now connected to Merriages collection. But everything breaks when I'm trying to add the same thing for Wife property:
modelBuilder.Entity<Marriage>()
.HasRequired<Member>(m => m.Wife)
.WithMany(m => m.Marriages)
.HasForeignKey(m => m.WifeID)
.WillCascadeOnDelete(false);
and I am getting an error:
Error 1 Schema specified is not valid. Errors: The relationship 'FamilyTree.Models.Marriage_Husband' was not loaded because the type 'FamilyTree.Models.Member' is not available. C:\Users\Sumiteru\Documents\Visual Studio 2013\Projects\FamilyTree\FamilyTree\App.xaml 9 21 FamilyTree
That error occurs becouse EF didn't know what navigation property he must use. In OnModelBuliding method you are setting a Member's foreign key to Marriages twice, so EF is confused about what navigation property should use when it will populating Marriages and throws an exception.
There's a topic on EF codeplex forum, where user named 'moozzyk' explains that EF behavior more clearly (in comment): Link.
As a solution, You should do another collection navigation property in Member class and map Wife's or Husbend's foreign key to it. You can find the same solution on that SO anwser: Link.

Relationship troubles with Entity Framework

I need help creating the relationship in entity framework as everything I have tried gives me errors when trying to add the migration or if I get passed that then I try to update the database and get an error about indexes with the same name.
public class Profile
{
public Profile()
{
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string VersionCreated { get; set; }
public string DiskLocation { get; set; }
public string Name { get; set; }
public DateTime DateTime { get; set; }
public virtual Product Product { get; set; }
public virtual Instance OriginalInstance { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
public class Instance
{
public Instance()
{
TestResults = new HashSet<TestResult>();
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string UserFriendlyName { get; set; }
public virtual Product Product { get; set; }
public virtual Profile LastKnownProfile { get; set; }
public virtual Computer Computer { get; set; }
public virtual ICollection<TestResult> TestResults { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
The problem with the above classes is that the OrginalInstance property on the Profile class and the LastKnownProfile in the Instance class are supposed to just be foreign keys to those specific tables and they probably won't be the same very often. They can also both possibly be null.
I have tried:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile);
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance);
This gave me an Unable to determine the principal end of an association between the types 'EcuWeb.Data.Entities.Instance' and 'EcuWeb.Data.Entities.Profile'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. error.
and with:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile).WithOptional();
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance).WithOptional();
The database adds a foreign key reference back to itself.
...that the OrginalInstance property on the Profile class and the
LastKnownProfile in the Instance class are supposed to just be foreign
keys to those specific tables and they probably won't be the same very
often. They can also both possibly be null.
In this case you actually want two one-to-many relationships between Profile and Instance if I don't misunderstand your quote above. It would mean that many Profiles can have the same OriginalInstance and that many Instances can have the same LastKnownProfile. The correct mapping would look like this then:
modelBuilder.Entity<Profile>()
.HasOptional(p => p.OriginalInstance)
.WithMany()
.Map(m => m.MapKey("OriginalInstanceId"));
modelBuilder.Entity<Instance>()
.HasOptional(i => i.LastKnownProfile)
.WithMany()
.Map(m => m.MapKey("LastKnownProfileId"));
The lines with MapKey are optional. Without them EF will create a foreign key with a default name.
Also note that you must use HasOptional (instead of HasRequired) if "both can possibly be null".

Categories