Double column with code first EF - c#

Doing code first try with EF in C# and I got stuck because I declared
public class Members
{
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public string Surname { get; set; }
[Display(Name = "someDisplayName")]
public bool ABool{ get; set; }
public ReferenceClass ReferenceClass { get; set; }
public byte ReferenceClassId { get; set; }
}
Entity framework adding migration after creating this i got
ID
NAME
SURNAME
ABool
ReferenceClassID
ReferenceClass_ID
in my SQL database. Now nothings maps correctly. How to fix it?!

Your model is unclear to entity framework.
If I look at the names of your identifiers, it seems that you want to model a one-to-many relationship between ReferenceClasses and Members: every ReferenceClass has zero or moreMembers, everyMemberbelongs to exactly oneReferenceClass`.
This is not how you designed your model.You told entity framework that Members is a composition of the complex type ReferenceClass and a byte property ReferenceClassId.
See:
Stack Overflow: Entity Framework Complex Type
Entity Framework: model various types of relations (a.o. one-to-many)
If you look at the column names that entity framework modelled for you, it is clear that it sees ReferenceClass as a complex type (used for composition)
If you want one-to-many you'll have to add the proper navigation properties. You were half way, except for the byte foreign key.
class Member
{
public int Id {get; set;}
...
// Navigation properties:
// a member belongs to exactly one ReferenceClass via foreign key
public int ReferenceClassId {get; set;}
public ReferenceClass ReferenceClass {get; set;}
}
class ReferenceClass {get; set;}
{
public int Id {get; set;}
...
// Navigation Properties:
// A reference class has zero or more Members:
public virtual ICollection<Member> Members {get; set;}
}
If you follow the code-first conventions, then this is enough for entity framework to recognize the one-to-many. Don't forget the virtual near the ICollection!
If you want special column names or table names, you could use Attributes to name them. Although I prefer fluent API, because that would allow you to use the same classes in different databases with different tables.
One-to-many in fluent api: in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// a ReferenceClass has zero or more Members
// every member belongs to exactly one ReferenceClass
// via foreign key ReferenceClassId
modelBuilder.Entity<ReferenceClass>()
.HasMany(refClass => refClass.Members)
.WithRequired(member => member.ReferenceClass)
.HasforeignKey(member => member.ReferenceClassId)
You can use this if you have different names in your properties. You could also use ToTable and HasColumName if you want to specify different names
modelBuilder.Entity<ReferenceClass>()
.ToTable("MyTable")
.Property(refClass => refClass.Name)
.IsRequired()
.HasColumnName("MyColumnName")
.HasMaxLength(40);
A very good tutorial for entity framework code first

as #BobMeijwaard pointed out it is likely that you want your class definition to be
public class Members
{
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public string Surname { get; set; }
[Display(Name = "someDisplayName")]
public bool ABool{ get; set; }
public ReferenceClass ReferenceClass { get; set; }
public int ReferenceClassId { get; set; }
}
changing public byte ReferenceClassId { get; set; } to public int ReferenceClassId { get; set; }
if more information can be provided i can update my answer if this is not correct

Related

Mapping 1-0..1 Relationship with Navigation Property Without FK

I've got 2 entities with a 1-0..1 relationship between them, but restrictions on what the generated DB schema can look like.
So 1 Vehicle to 0 or 1 RecVehicle entity
I need to be able to have a navigation property from Vehicle to RecVehicle, but without the DB Schema for the Vehicles table having a FK to RecVehicle. The PK of the RecVehicle table should be the Id of the Vehicle entity it relates to.
We are using EF code first
public class Vehicle
{
[Key]
public int Id { get; set; }
public virtual RecVehicle RecVehicle { get; set; } // Need to be able to use as navigation
}
public class RecVehicle
{
[Key]
public int VehicleId { get; set; }
[ForeignKey("VehicleId")]
public Vehicle Vehicle { get; set; }
}
The generated schema needs to be something like this:
Vehicles
[ Id(int, pk, not null), ...] <-- no FK column to RecVehicles
RecVehicles
[ VehicleId(int, pk, fk, not null), ...]
Originally what I had tried something like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
[InverseProperty("Vehicle")]
public virtual RecVehicle RecVehicle { get; set; } // Need to be able to use as navigation
}
but this causes this exception:
Unable to determine the principal end of an association between the types 'Contract.Entities.Vehicle' and 'Contract.Entities.RecVehicle'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I'm not sure what fluent API relationships to setup to make this work, nor the correct set of data annotations to make this work, or if it's even possible.
Reasoning
The reason there is strict limitations on the DB schema is our Data team has a migration/data import process that we can not alter
We have an existing code base that uses the navigation property in many places (2 teams, desync in schema) so changing to use a lookup in code requires many changes in the code base that we are trying to avoid.
The RecVehicle can be connected to multiple Vehicles
Can you try the following navigation property?
public virtual ICollection<RecVehicle> RecVehicle { get; set; }
instead of
public virtual RecVehicle RecVehicle { get; set; }
Due to the RecVehicle primary key this list only maximum contains one element
Ended up being able to get this relationship to work like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
public virtual RecVehicle RecVehicle { get; set; }
}
public class RecVehicle
{
[Key]
public int VehicleId { get; set; }
[ForeignKey("VehicleId"), Required] //<--- Required attr fixed the principal/dependent confusion EF was having
public virtual Vehicle Vehicle { get; set; }
}

EF Core multiple realationship of a single column

I am using EF Core 3.1 and I have five Models: Plant, Area, Unit, Schema, and EntitiesSchema.
In the EnititiesSchema, the EntityId may be a foreign key of Plant(PlantId), Area(AreaId), Unit(UnitId) tables.
How to handle this optional Relationship between these tables?
Thanks
public class EntitiesSchema
{
public int Id { get; set; }
public int EntityId { get; set; }
public int TopicId { get; set; }
public int SchemaId { get; set; }
public string Description { get; set; }
public Schema Schema { get; set; }
public ICollection<Topic> Topic { get; set; }
}
No, you can't relate a foreign key to multiple tables. But you can put another property named EntityType to store the type of entity. Then on the client-side, you can handle it. The EntityType can be an enum type.
Another approach is that storing "EntitesSchemaId" in the Plant, Area, Unit, etc models and relate them to the EntitiesSchema.
You can create an intermediary entity to map to different entity types. :
Public class EntityMap
{
public int Id {get;set;}
public string EntityKind {get;set;} // could be "Plant", "Area", "Unit", "Schema"
}
public class Plant
{
public int Id {get;set;}
public string EntityKind {get;set;} = "Plant";
}
public class EntitySchema
{
public int Id {get;set;}
public int EntityMapId {get;set;}
public EntityMap Map {get;set;}
}
The logic to read data from individual schema, has to be implemented in the client,but common properties of the entities can be added in EntityMap.
Here's a similar answer you might want to reference : https://stackoverflow.com/a/53649452/7491048

Invalid column name error from SQL generated by EF

Below you can see the SQL should join by using [ClassId1] instead of [Class1_ClassId] since the latter doesn't exist.
I'm pretty sure I can use Fluent API to correct this but not sure what methods.
Generated SQL
SELECT ...
FROM [dbo].[School] AS [Extent1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON [Extent1].[SchoolId] = [Extent2].[SchoolId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent3] ON [Extent2].[Class1_ClassId] = [Extent3].[ClassId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent4] ON [Extent2].[Class2_ClassId] = [Extent4].[ClassId]
WHERE ...
Tables
School
- SchoolId
- Name
- StudentId
Student
- StudentId
- Name
- Class1Id
- Class2Id
Class
- ClassId
- Name
Models
public class School
{
[Required]
public long SchoolId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long StudentId { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
[Required]
public long StudentId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
}
public class Class
{
[Required]
public long ClassId { get; set; }
[Required]
public string Name { get; set; }
}
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
You haven't setup any relationship between these properties. Since you haven't defined foreign key columns for Class1 or Class2, it'll create them for you: Class1_ClassId and Class2_ClassId. Creating a migration should create those columns for you; but you'd end up with duplicates (Class1Id and Class1_ClassId for example).
I believe EntityFramework will resolve relationships between properties if the name ends with Id. Which means your setup should be:
[Required]
public long Class1Id { get; set; }
public long? Class2Id { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
However, I find it's better to be explicit - purely for readability and to ensure EF doesn't try to get too smart. I'd write it like this:
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
[ForeignKey("ClassId1")]
public virtual Class Class1 { get; set; }
[ForeignKey("ClassId2")]
public virtual Class Class2 { get; set; }
This should properly setup your foreign key relationships in the database.
I think Entity Framework constructed this SQL from your linq, because the relations between the classes in your model are unclear.
According to your model a School has only one Student, a Student doesn't know which School he attends, and is obliged to have one Class, and possibly a second one. A class does not know in which School it is, nor which Students are in the Class.
Are you sure about your model?
I'd gather that a School would have zero or more Students. A School also has zero or more Classes. Each class is a class in a School.
In database terms this is a typical one-to-many relationship. See Entity Framework Configure One-to-Many Relationship
Furthermore a Student attends zero or more Classes, a Class has one or more Students.
In database terms this is a typical many-to-many relationship. See: Entity Framework configure many-to-many relationship
These articles also describe schools, students and couses. Summarized the class definitions ought to be:
class School
{
public int Id {get; set;}
// a School has many Students:
public virtual ICollection<Student> Students {get; set;}
// a School has many Classes:
public virtual ICollection<Class> Classes {get; set;}
...
}
public class Student
{
public int Id {get; set;}
// A student belongs to one School via Foreign Key
public int SchoolId {get; set;}
public virtual School School {get; set;}
// A student attends many classes
public virtual ICollection<Class> Classes {get; set;}
...
}
class Class
{
public int Id {get; set;}
// a class belongs to one School via foreign key:
public int SchoolId {get; set;}
public virtual School School {get; set;}
// a class has many Students
public virtual ICollection<Student> Students {get; set;}
...
}
After this the DbContext will be as follows:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
}
If you model it like this, entity framework will automatically recognize the one-to-many relationships between School and Students, and create proper foreign keys for it. It will also recognize the many-to-many relationship between Students and Classes. It will even create a table for the many-to-many, which you won't need in your LINQ queries.
Entity Framework uses default conventions If you follow them, you won't need to tell the model about Table names and column names, about primary keys and foreign keys etc.
Back to your question
You want to tell your model that it should use a certain column name for a property instead of the column name it constructed from your class relations.
This can be done using Data Annotations within your class, or using Fluent API within your DbContext. I prefer using Fluent Api, because it allows you to use the same classes in different database structures without having to change the classes. If you want different table names, or different names for primary keys, different precision for decimals, etc, all you have to do is create a new DbContext. You don't have to change your classes, users of your classes won't notice the changes.
Fluent API is described here.
In you case: specify a table name instead of the default table name.
In my example, A Class would be put in table Classs, while of course you'd want it in table Classes:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity Class should be put in table Classes
modelBuilder.Entity<Class>().ToTable("Classes");
// property Student.ClassId in column "ClassId1"
modelBuilder.Entity<Student>() // from class Student
.Property(student => student.ClassId) // take property ClassId
.HasColumnName("ClassId1"); // give it the column name "ClassId1"
}
}

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

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.

Categories