How to model a 1:1 association as class - c#

ASP.NET MVC and I got an issue that I don't know how to solve
I have a Vehicle class in model
VehicleId
Register
.... Etc
And a Driver class
DriverId
FirstName
LastName
I want to create a new table in order to control of what vehicle have a Driver where each Vehicle cannot have more than one Driver
I think of creating a table DriverVehicle with the key from Vehicle and Driver. Any help related with this would be a big help. I am working with ASP.NET MVC and EF.

What you're looking for is a mutually optional 1:1 association between independent entities (more exactly: 0..1-0..1). As I explained here, EF isn't really helpful here, but there are some options.
The first option is to settle with a 1-0..1 association, which is fully supported by EF. But either Car or Driver should have a primary key that's also a foreign key to the other entity. From what I read, this is not what you want. You want both entities to be able to exist independent of the other.
So the only option is, as you describe, introducing a third entity, aka a junction class. Now, Entity Framework only supports this for many-to-many associations, not for 1:1. So you'll have to map the association as many-to-many, but take some special measures to prevent duplicate associations on both ends.
To achieve that, this is what I came up with:
public class Car
{
public Car()
{
CarDrivers = new HashSet<CarDriver>();
}
public int CarId { get; set; }
public string Name { get; set; }
public virtual ICollection<CarDriver> CarDrivers { get; set; }
}
public class Driver
{
public Driver()
{
CarDrivers = new HashSet<CarDriver>();
}
public int DriverId { get; set; }
public string Name { get; set; }
public virtual ICollection<CarDriver> CarDrivers { get; set; }
}
public class CarDriver
{
[Key, Column(Order = 0), Index(IsUnique = true)]
public int CarId { get; set; }
[Key, Column(Order = 1), Index(IsUnique = true)]
public int DriverId { get; set; }
public Car Car { get; set; }
public Driver Driver { get; set; }
}
This is an explicit many-to-many association (i.e. having a visible junction class), but restricted to 1 at both ends by the unique indexes on both foreign keys in CarDriver.
So at least at the database side, the proper multiplicity of the association is enforced. However, in your code, nothing stops you from adding more than one CarDriver to the CarDrivers collections. This will throw an ugly database constraint error, so you'll have to guard this with custom validation.

Add DriverId as a foreign column in Vehicle table and add virtual list of Vehicles for a driver. So each vehicle will have single driver and each driver can drive many vehicles.
Vehicle Class
public virtual Driver Driver { get;set; }
public virtual int DriverId { get;set; } //make it null if you think some vechile dont have driver
Driver Class
public virtual IList<Vehicle> Vehicles { get;set; }

Related

EF6 Many-to-Many on Non-Key Column

I am having trouble connecting a many-to-many relationship in EF6 using code first, then creating a pass-through association beyond that.
There are three classes: Person, Tag, and Passing.
Each Person has an optional Bib.
Each Tag has an optional Bib, not unique.
Each Passing has a required TagId.
I want to access all Passings linked to a Person by getting all Tags with the same Bib, then getting all Passings associated with each of those Tags.
I have tried using the DBModelBuilder in my DBContext class but can't get it to work properly, and EF6 seems to try to generate an intermediate table anyways, which seems unnecessary.
public class Person
{
[Key]
public int PersonId { get; set; }
...
public string Bib { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual ICollection<Passing> Passings
}
public class Tag
{
[Key]
public string TagId { get; set; }
public string Bib { get; set; }
public virtual ICollection<Passing> Passings { get; set; }
public virtual Person Person { get; set; }
}
public class Passing
{
[Key]
public int PassingId { get; set; }
...
public string TagId { get; set; }
public virtual Tag Tag { get; set; }
}
IT IS necessary, when you have a * to * multiplicity into a table, it automaticly creates another table that links these, elseway you cannot put an infinite and variable number of foraign key in one of your table
Entity Framework uses navigation properties to represent database relationships. If you don't want an additional table, what you have here is not a database relationship since keys aren't involved.
You should be able to use some kind of function (or extension function) to get what you want:
IQueryable<Passing> PersonPassings(YourContext db, Person p)
{
return db.Passings.Where(pa => pa.Tag.Bib == p.Bib);
}
On the other hand, if you want to create a proper relationship, you'll need an intermediate Bibs table to connect Person and Tag.

How to set up Navigation Properties to enforce deleting related records

I am using MVC 4 and VS2010 with Entity Framework 6.1.x. I am working with a Code First fairly easy database but there is a slightly more complex part of it. First of all, two tables Person and Recording have relationship 1 to many.
public class Person {
public int PersonID { get; set; }
public int GenderID { get; set; }
// Navigation property
public virtual List<Recording> Recordings { get; set; }
}
public class Recording {
public int RecordingID { get; set; }
// ...
public int PersonID { get; set; }
// Navigation properties
public virtual Person Person { get; set; }
public virtual List<Junction> Junctions { get; set; }
}
By default, when I delete a Person, Entity Framework deletes all recordings related to Person. That's what I expect. But the records from Recording table are also placed in different set of tables. Let's say I have table called Applicant, Application and Junction. When I delete any recording either by deleting Person or Recording, I would like all records in Junction which are related to records in Recording to be deleted, as well. In my project it wouldn't make sense if there are zero number of recordings associated with particular application.
There is a combined Primary Key, that is ApplicantID, ApplicationID, and RecordID in Junction make the complex Key.
Is it possible to enforce constraints via Entity Framework or I'd rather have to provide my own custom function?
How to set up Navigation properties in corresponding tables: Recording and Junction so that related recordings are deleted?
public class Applicant
{
public int ApplicantID { get; set; }
// Navigation property
public virtual List<Junction> Junctions { get; set; }
}
public class Application {
public int ApplicationID { get; set; }
// Navigation property
public virtual List<Junction> Junctions { get; set; }
}
public class Junction
{
public int ApplicationID { get; set; }
public int ApplicantID { get; set; }
public int RecordingID { get; set; }
public virtual Application Application { get; set; }
public virtual Applicant Applicant { get; set; }
public virtual Recording Recording { get; set; }
}
Thanks for any help.
EDIT
#Chris. From what I have understood, if a Foreign Key is not nullable, records in the secondary table are deleted if a record in the Primary table with the corresponding PrimaryID are deleted. On the other hand, if the Foreign Key is nullable, records in the secondary table, would become null but not deleted.
I made this project and two tables and I filled the database. In the class Student, if I leave DepartmentID not nullable,
public int DepartmentID { get; set; }
records are deleted when a record with primary DepartmentID is deleted. This is what I expect but if I make DepartmentID nullable
public int? DepartmentID { get; set; }
then I've got this error:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Student_dbo.Department_DepartmentID". The conflict occurred in database "TestDB", table "dbo.Student", column 'DepartmentID'.
The statement has been terminated.
In test project I made two tables with relationship 1 to many.
public class Department
{
public int DepartmentID { get; set; }
public string DepartmentName { get; set; }
public virtual List<Student> Students { get; set; }
}
and
public partial class Student : IDisposable
{
public int StudentID { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
Is this what I should expect?
The delete cascade is not intended to just delete items on a whim. It exists out of necessity. In your example above, Recording has a non-nullable foreign key to Person. If that Person is deleted, the foreign key would either have to be set to null (which can't happen) or all related Recordings must be deleted to preserve referential integrity.
In the second example with Junction, there's a non-nullable foreign key to Record. So if you delete a Person all related Records and all Junctions related to those records should be deleted. However, if you delete a Junction no further action is taken. There is nothing inherently dependent on a Junction so its deletion goes unnoticed. Any Records it once referenced are still valid. Remember this is all about maintaining referential integrity. As long as integrity is intact, nothing will ever be deleted.

Determining if a model should have foreign keys / navigation properties

I'm building a fairly simple MVC project and still getting my head around where to use navigation properties and foreign keys with code first.
This is the main model class:
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
public GroceryCategory Category { get; set; }
public QualityProfile Quality { get; set; }
public GroceryStore BestStore { get; set; }
public double BestPrice { get; set; }
public double LastSeenPrice { get; set; }
//Navigation Properties
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
}
and these are the relating classes:
public class GroceryStore
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Uri Website { get; set; }
}
public class QualityProfile
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
/// <summary>
/// Rank out of 1-10, 10 being the best
/// </summary>
public byte Ranking { get; set; }
}
public class GroceryCategory
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Which brings me to my question, is the navigation property of SimilarItems I have in the GroceryItem class sufficient to represent a list of multiple grocery items or does this not work as it is referring to itself?
Additionally...do the Category, Quality and BestStore properties require ID properties to represent a foreign key inside of the GroceryItem class (e.g. CategoryID), or is the way I have this represented OK?
----EDIT----
--Refactored Code--
I've re-factored my model based on the suggestions below, which I think better accommodates the suggestions you've made (yes a 2nd time), realised my model was a little flawed and extracted out the price component into a separate purchases Model.
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
[ForeignKey("Quality")]
public int QualityID { get; set; }
//Navigation Properties
public virtual QualityProfile Quality { get; set; }
public virtual GroceryCategory Category { get; set; }
}
However the last thing I'm uncertain about which is on topic to this post, is if I have a collection as a part of the model (one that does not reference itself like in the first example), can I just represent that with a navigation property or does an extra step need to be taken?
Ie. If I was to allow multiple different categories on a GroceryItem, instead of looking like this:
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual GroceryCategory Category { get; set; }
it would look like this:
public virtual ICollection<GroceryCategory> Categories { get; set; }
The best answer to your question(s) is, "It depends". Navigation properties are one way of informing Entity Framework that there's a relationship between entities. By convention, if you have a navigation property such as:
public Category Category { get; set; }
Entity Framework will create a column on the table named in the form of [RelatedPropertyName]_[RelatedPK]. Given your classes, the property above would cause a column named Category_ID. There's nothing more you need to do make it work. The relationship will automatically be handled by EF.
However, doing it this way, you won't have access to this foreign key property. It's not exposed in the public API of your entity. Often, especially when selecting related items from a select list and similar such scenarios, this becomes problematic, as you must store the selected value some place else, usually a property on a view model, and then use this to query the related thing from the database before setting it on the entity it belongs to and finally saving the entity. Whereas, with an actual foreign key property, you can simply post directly back to this and Entity Framework will automatically wire up the related entity. As a result, I tend to always follow the following pattern with my navigation properties:
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
In most scenarios, Entity Framework will automatically connect those two, such that FooId will hold the foreign key relationship for the Foo navigation property. However, occasionally, EF will trip up and try to create the implicit foreign key behind the scenes, still, but you can correct that behavior by explicitly telling EF that this is the foreign key:
[ForeignKey("Foo")]
public int FooId { get; set; }
Roughly the same applies with collection navigation properties. EF will see this as an indication that there's a one-to-many relationship in play and add the implicit foreign key on the opposite entity. Given your collection:
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
The opposite entity is actually the same entity, which presents an interesting use case. Typically, EF would handle this by assuming there's a one-to-many relationship. You'd end up with a column named GroceryItem_ID on your dbo.GroceryItems table. Here, though, you would not only have no access to the foreign key directly, but you also have no public API for accessing the parent GroceryItem either. That may not be a problem, but it's something to be aware of. The only way you'd be able to manage the relationship is through the collection on the parent, not through a child item in that collection.
However, since this is self-referential and you have not specify a foreign key or instance navigation property, all EF will see is a collection on both sides of the relationship, so my guess is that you'll actually end up with an M2M with an intermediary table. I can't test that theory out myself at the moment, and I haven't tried this particular scenario myself previously.
To create a true one-to-many, you would need to create another navigation property similar to:
public virtual GroceryItem ParentGroceryItem { get; set; }
And, even, then, I don't think EF will get the point without a little Fluent configuration:
HasMany(m => m.SimilarItems).WithOptional(m => m.ParentGroceryItem);
You could also use WithRequired in other scenarios instead of WithOptional, which would obviously make the relationship a required one, but since this is self-referential, it's impossible to have it required, because there will have to be at least one root node with no parent.

EF abstract base class with key where derived class has different key

Time for a dumb question. I think the database design is screwy, but there isn't much I can do about that part of it. I have a table in the database "Table1" and then "Table2" which is essentially an extension of Table1 (I also have Table3, Table4, etc). My problem is that Table2 has it's own unique key, even though it's a one for one relationship. Then Table2Component uses Table2Id as it's foreign key. However, when I try to use that in my code I think it's pointing to Table1Id. I get the error message:
System.Data.Entity.Edm.EdmAssociationConstraint: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'Table2Id' on entity 'Table2Component' does not match the type of property 'Table1Id' on entity 'Table2' in the referential constraint 'Table2Component_Table2'.
Here is the code
[Table("Table1")]
public abstract class Table1
{
[Key]
[Column("table1_id")]
public string Table1Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("type_cd")]
public string TypeCode { get; set; }
}
[Table("Table2")]
public class Table2 : Table1
{
[Key]
[Column("table2_id")]
public int Table2Id { get; set; }
[ForeignKey("Table1Id")]
public virtual Table1 Table1 { get; set; }
// this table also has a table1_id column
// but I guess I don't need it here, correct?
[Column("column1")]
public string Column1 { get; set; }
public virtual ICollection<Table2Component> Table2Components { get; set; }
}
[Table("Table2Component")]
public class Table2Component : ISubItem
{
[Key]
[Column("table2_component_id")]
public int Table2ComponentId { get; set; }
[Column("table2_id")]
public int Table2Id { get; set; }
[Column("description")]
public string Description { get; set; }
public bool Required { get { return true; } }
[ForeignKey("Table2Id")]
public virtual Table2 Table2 { get; set; }
}
Any suggestions? Should I be more forceful in trying to get the database changed?
Started as comment.... finish as simple answer, since no one else jumped in.
Search for Entity Framework 1:1 relationship eg https://stackoverflow.com/a/14997417/1347784 the restriction is both tables must have the same foreign key when using 1:1
No not necessarily better database design. It is Just the why the EF team built the framework. Ive learnt to live with the restrictions. In code first scenario, no big deal. Try the powertool to reverse engineer the alternative approach when you start with the DB. EF will use 1:M even though you may see it as 1:1. Also OK in my view.

Entity Framework - Many to many?

I'm defining a many-to-many relationship as follows:
modelBuilder.Entity<GameSessionEntry>().
HasMany(c => c.Users).
WithMany(p => p.GameSessionEntries).
Map(
m =>
{
m.MapLeftKey("SessionId");
m.MapRightKey("UserId");
m.ToTable("UserSessions");
});
However, I keep getting:
The Foreign Key on table 'UserSessions' with columns 'UserId' could
not be created because the principal key columns could not be
determined. Use the AddForeignKey fluent API to fully specify the
Foreign Key.
I'm new to database work and the EntityFramework in general - what is it asking me to do?
It's the recurring confusion with left and right, see this explanation by Slauma. So you just have to turn around the key names:
m.MapLeftKey("UserId"); // Property in the HasMany call
m.MapRightKey("SessionId"); // Property in the WithMany call
This is how I usually go about creating a many to many table (note this requires no fluent api configuration)
public class User
{
public int Id { get; set; }
public virtual ICollection<UserSession> UserSessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public virtual ICollection<UserSession> UserSessions { get; set; }
}
public class UserSession
{
[Key]
[Column(Order = 1)]
public int UserId { get; set; }
[Key]
[Column(Order = 2)]
public int SessionId{ get; set; }
public virtual User User { get; set; }
public virtual Session Session { get; set; }
}
Instead of fiddling around with a many-many relationship you should rewrite it to a weak entity set.
If you have for instance this relationship:
You can redesign it to a weak entity set:
By doing this you get rid of the many-many relationship and don't have to store the same data in multiple tables.
For more information: http://fileadmin.cs.lth.se/cs/Education/EDA216/lectures/dbtoh4.pdf
Read the lecture slides about "The Relational Data Model" starting on slide 87/360.

Categories

Resources