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

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; }
}

Related

How can we add multiple foreign keys from one model to another on ASP.NET Core & EF Core & C#

So let's say I have an ApplicationUser : IdentityUser model class that has the identity relations and it has a String ID by default and roles assigned when the user signs up.
ApplicationUsers will have different roles for example Student and Library.
The Library will have a list of books while the Student will have list of orders.
Now I want to create a List of another model which will have the name Orders, but the Orders model class will have two UserIds as foreign keys from ApplicationUser.
ApplicationUser: IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Libraries> Libraries { get; set; }
public List<Orders> Orders { get; set; }
}
Since the ApplicationUser is connected with ASPNetRoles, what I want to achieve on Orders model class is that I want to have different StudentId and LibraryId from the same table which is ApplicationUser:
public class Orders
{
[Key]
public id OrderId {get; set;}
[ForeignKey("StudentId")]
public string StudentId { get; set; }
public ApplicationUser Student {get; set;}
[ForeignKey("LibraryId")]
public string LibraryId {get; set;}
public ApplicationUser Library {get; set;}
}
Is there any way I can achieve this? What are the best solutions for this case? I did try ICollection and list but still same. Any suggestion about this would be great.
When I run Add-Migrations, I get this error:
System.InvalidOperationException: Unable to determine the relationship represented by navigation 'ApplicationUser.OrdersLists' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Thank you.
Ok, something in you model doesn't look quite right...
In your User class you have a collection of Orders and an collection of a class called Library. Yet within your Order class you have a property called Library, but point that at an ApplicationUser class?
EF does support having multiple references to the same class, though you need to explicitly tell it what the FK names would be. EF's default convention is to base FK names on the type of the navigation property, not the name of the navigation property.
Take the following:
public class Order
{
....
public int CreatedById { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
public int LastModifiedById { get; set; }
public virtual ApplicationUser LastModifiedBy { get; set; }
}
Here by default EF would want to use "ApplicationUser_Id" or "ApplicationUserId" as a FK name for both of the two navigation properties, settling on something like "ApplicationUser_Id" and "ApplicationUser_Id1" if left to its own devices with the schema. In this situation we would need to configure it to use our desired FK properties:
[ForeignKey("CreatedBy")]
public int CreatedById { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
[ForeignKey("LastModifiedBy")]
public int LastModifiedById { get; set; }
public virtual ApplicationUser LastModifiedBy { get; set; }
or the FK attribute can be put on the navigation property:
public int CreatedById { get; set; }
[ForeignKey("CreatedById")]
public virtual ApplicationUser CreatedBy { get; set; }
public int LastModifiedById { get; set; }
[ForeignKey("LastModifiedById")]
public virtual ApplicationUser LastModifiedBy { get; set; }
The ForeignKey Attribute is a bit weird, as it represents either "I am the FK of ..." if on the FK property, or it represents "My FK is ...." if on the navigation property.
With EF Core the FK property can be left off and treated by EF as a shadow property which is recommended to avoid having two sources of truth in the entity.
[ForeignKey("CreatedById")]
public virtual ApplicationUser CreatedBy { get; set; }
[ForeignKey("LastModifiedById")]
public virtual ApplicationUser LastModifiedBy { get; set; }
In situations where you want bi-directional references in the other side of the relationship, you may need to map those out. For instance if I want a "CreatedOrders" in my ApplicationUser class:
public class ApplicationUser
{
// ...
public virtual ICollection<Order> CreatedOrders { get; set; } = new List<Order>();
}
Now it's generally a good idea to tell EF what to relate this back to since Order has two references to the application user. Again, this can be done on either side of the relationship. So in the case of back in our Order class:
[ForeignKey("CreatedById"), InverseProperty("CreatedOrders")]
public virtual ApplicationUser CreatedBy { get; set; }
This tells EF that CreatedBy on this record is the link to use when accessing the orders for CreatedOrders.
Back to your example it is a bit confusing why ApplicationUser would contain a collection of Libraries while an Order expects a "library" to be a User.

Configure One-None/One Relationship with Multiple Tables using Entity

I'm in a situation where one table has two One-None/One Relationships. How do I implement this using Entity Framework Code-First?
I've seen the following links
https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449317867/ch04s07.html
https://cpratt.co/0-1-to-1-relationships-in-entity-framework/
https://www.tektutorialshub.com/one-to-one-relationship-entity-framework/
Where essentially it's said that the dependent end needs to have a primary key that is the same as that of the principal end. But I'm weary of implementing this with more than one One-None/One Relationship without confirmation and proper knowledge of what's going on. Furthermore I am not sure how to construct statements as it does not have a conventional Foreign Key.
I've also seen Configuring multiple 1 to 0..1 relationships between tables entity framework which confused me beyond recognition.
See below for the relevant part of my DB Diagram:
So Essentially, a Player shouldn't be saved without a DKImage, similarly a Product shouldn't be saved without a DKImage.
Below is the code for Models: Players, Products, DKImages (I know it's not correct, I only implemented it this way so I can generate the database and show the diagram)
Player
public enum Positions { PG, SG, SF, PF, C }
public class Player
{
[Key]
[ForeignKey("Images")]
public int PlayerID { get; set; }
[Required]
public string PlayerName { get; set; }
[Required]
public string PlayerLastName { get; set; }
[Required]
public int PlayerAge { get; set; }
[Required]
public Positions Position { get; set; }
[Required]
public bool Starter { get; set; }
[Required]
[Display(Name = "Active / Not Active")]
public bool Status { get; set; }
//Foreign Keys
public int PlayerStatsID { get; set; }
//Navigation Properties
[ForeignKey("PlayerStatsID")]
public virtual IQueryable<PlayerStats> PlayerStats { get; set; }
public virtual DKImages Images { get; set; }
}
DKImages
public class DKImages
{
[Key]
public int ImageID { get; set; }
[Required]
public string ImageURL { get; set; }
[Required]
public DateTime DateUploaded { get; set; }
//Foreign Keys
[Required]
public int CategoryID { get; set; }
//Navigation Properties
public virtual Products Products { get; set; }
public virtual Category Category { get; set; }
public virtual Player Player { get; set; }
}
Products
public class Products
{
[ForeignKey("Images")]
[Key]
public int ProductID { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
public DateTime DateAdded { get; set; }
//Foreign Keys
[Required]
public int ProductTypeID { get; set; }
//Navigation Properties
[ForeignKey("ProductTypeID")]
public virtual ProductType ProductType { get; set; }
public virtual DKImages Images { get; set; }
}
Edit
I have been told that the code above is correct. If so then how do I create CRUD LINQ Statements (Or any method of constructing CRUD statements for that matter) with the above code.
What you want here is referred to as polymorphic associations: several entities having child entities of one type. They're typically used for comments, remarks, files etc. and usually applied to 1:n associations. In your case there are polymorphic 1:1 associations. Basically these associations look like this (using a bit more generic names):
How to implement them?
Entity Framework 6
In EF6 that's problem. EF6 implements 1:1 associations as shared primary keys: the child's primary key is also a foreign key to its parent's primary key. That would mean that there should be two FKs on Image.ID , one pointing to Person.ID and another one pointing to Product.ID. Technically that's not a problem, semantically it is. Two parent entities now own the same image or, stated differently, an image should always belong to two different parents. In real life, that's nonsense.
The solution could be to reverse the references:
But now there's another problem. The entity that's referred to is named the principal, the other entity is dependent. In the second diagram, Image is the principal, so in order to create a Person, its image must be inserted first and then the person copies its primary key. That's counter-intuitive and most likely also impractical. It's impossible if images are optional.
Nevertheless, since in your case you want images to be required let me show how this association is mapped in EF6.
Let's take this simple model:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Image Image { get; set; }
}
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Image Image { get; set; }
}
public class Image
{
public int ImgID { get; set; } // Named for distinction
public string Url { get; set; }
}
The required mapping is:
modelBuilder.Entity<Image>().HasKey(pd => pd.ImgID);
modelBuilder.Entity<Person>().HasRequired(p => p.Image).WithRequiredDependent();
modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithRequiredDependent();
As you see, Image has two required dependents. Perhaps that's better than two required parents, but it's still weird. Fortunately, in reality it's not a problem, because EF doesn't validate these associations. You can even insert an image without a "required" dependent. I don't know why EF doesn't validate this, but here it comes in handy. The part WithRequiredDependent might as well have been WithOptional, it doesn't make a difference for the generated data model, but at least this mapping conveys your intentions.
An alternative approach could be inheritance. If Person and Product inherit from one base class this base class could be the principal in a 1:1 association with Image. However, I think this is abusing a design pattern. People and products have nothing in common. From a design perspective there's no reason for them to be part of one inheritance tree.
Therefore, in EF6 I think the most feasible solution is to use the third alternative: separate image tables per entity.
Entity Framework Core
In EF-core 1:1 associations can be implemented the EF6 way, but it's also possible to use a separate foreign key field in the dependent entity. Doing so, the polymorphic case looks like this:
The Image class is different:
public class Image
{
public Image()
{ }
public int ImgID { get; set; }
public int? PersonID { get; set; }
public int? ProductID { get; set; }
public string Url { get; set; }
}
And the mapping:
modelBuilder.Entity<Person>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Person>()
.HasOne(p => p.Image)
.WithOne()
.HasForeignKey<Image>(p => p.PersonID);
modelBuilder.Entity<Product>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Product>()
.HasOne(p => p.Image)
.WithOne()
.HasForeignKey<Image>(p => p.ProductID);
modelBuilder.Entity<Image>().HasKey(p => p.ImgID);
Watch the nullable foreign keys. They're necessary because an image belongs to either a Person or a Product. That's one drawback of this design. Another is that you need a new foreign key field for each new entity you want to own images. Normally you want to avoid such sparse columns. There's also an advantage as compared to the EF6 implementation: this model allows bidirectional navigation. Image may be extended with Person and Product navigation properties.
EF does a pretty good job translating this into a database design. Each foreign key has a filtered unique index, for example for Person:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Image_PersonID] ON [dbo].[Image]
(
[PersonID] ASC
)
WHERE ([PersonID] IS NOT NULL)
This turns the association into a genuine 1:1 association on the database side. Without the unique index it would be a 1:n association from the database's perspective.
An exemple in your Player table would be this :
public class Player
{
// All the rest you already coded
[Required]
public int ImageID
[ForeignKey("ImageID")]
public virtual DKImage DKImage {get;set;}
}
This would force a player to have a DKImage, but as said in the comments, this create a one to many relationship.
Another way out would be to put all Player fields into the DKImage table, those fields would be null if there is no player associated to this DKImage.
Edit for 1 to 1..0
Ivan Stoev's link got some pretty interesting insight on how to accomplish this :
https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations
It seems like you will have to put a bit more code in your class :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DKImage>().HasOptional(t => t.Player).WithRequired();
}
If the tutorial is correct, this would read as :
"DKImage entity has an optional association with one Player object but this association is required for Player entity".
I have not tested it yet.

Table with foreign keys missing members in generated class in edmx

I have a table in the database that has 4 foreign keys referencing to it. When I add the table to the edmx, the table and navigational properties are there. However, the foreign key ids from this table is missing and only the virtual objects are there.
This is the following table that is generated in the .tt file:
public partial class Device
{
public int SolutionId { get; set; }
public string SiteId { get; set; }
public string Name { get; set; }
public int SysId { get; set; }
public Nullable<int> SysType { get; set; }
public string SerialNumber { get; set; }
public Nullable<int> ParentId { get; set; }
public virtual DeviceModel DeviceModel { get; set; }
public virtual DeviceType DeviceType { get; set; }
public virtual SolutionApplication SolutionApplication { get; set; }
public virtual SolutionType SolutionType { get; set; }
}
There are a few members missing:
DeviceModelId, DeviceTypeId, SolutionApplicationId, and SolutionTypeId
Why is it missing? Is there any way to get those keys actually be part of the partial class?
using EntityFrameworks v6.0.2. Lazy Loading
In short, Entity Framework 'abstracts that away'.
Its clever enough to recognise that your FKs represent relationships and so allows you to work with the objects themselves. So instead of having you worry about checking the FK constraint, etc. for, say, SolutionTypeId - you just need to add a SolutionType object to your Device object and let Entity Framework sort it out. (Of course, this causes problems if you try to add a new SolutionType that violates the SolutionType PK so maybe you need to first find an existing object from the SolutionTypes table).
So, instead of thinking of it as a Device table linked to a SolutionType table via a FK - just think of it as a Device object with a SolutionType object as a property. EF sorts out the db for you when you save changes (assuming your model is accurate!)

Entity framework one to zero or one relationship without navigation property

I hit an issue when trying to delete records due to FK constraints. I therefore went back to the drawing board and am trying to specify how the relationship should work.
Here are my code first classes:
public class MemberDataSet
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? DeferredDataId { get; set; }
[ForeignKey("DeferredDataId")]
public virtual DeferredData DeferredData { get; set; }
}
public class DeferredData
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//other properties
}
What I am looking to achieve is that the MemberDataSet has zero or one DeferredData. I can access DeferredData from MemberDataSet but DeferredData does not need a navigation property back to MemberDataSet. DeferredData should strictly require a MemberDataSet. In an ideal world deleting MemberDataSet will therefore delete DeferredData if assigned.
What seemed to me to be what I wanted to specify is this:
modelBuilder.Entity<MemberDataSet>().HasOptional(d => d.DeferredData).WithRequired().WillCascadeOnDelete(true);
i.e. MemberDataSet has an option DeferredData but DeferredData has a required MemberDataSet and this relationship should cascade on delete.
However, I then get an error:
The ForeignKeyAttribute on property 'DeferredData' on type 'MemberDataSet' is not valid. The foreign key name 'DeferredDataId' was not found on the dependent type 'DeferredData'. The Name value should be a comma separated list of foreign key property names.
Edit
After feeling happy with Sam's answer below I went ahead and changed a few other ForeignKey attributes. MemberDataSet has another property called SignedOffBy that is a userProfile. This previously looked like this:
public class MemberDataSet
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? DeferredDataId { get; set; }
[ForeignKey("DeferredDataId")]
public virtual DeferredData DeferredData { get; set; }
public int? SignedOffById { get; set; }
[ForeignKey("SignedOffId")]
public virtual UserProfile SignedOffBy { get; set; }
}
After discussion below on what ForeignKey attribute is actually doing I changed this to:
public class MemberDataSet
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? DeferredDataId { get; set; }
[ForeignKey("Id")]
public virtual DeferredData DeferredData { get; set; }
public int? SignedOffById { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile SignedOffBy { get; set; }
}
However, I now get a very similar error message:
The ForeignKeyAttribute on property 'SignedOffBy' on type 'MemberDataSet' is not valid. The foreign key name 'UserId' was not found on the dependent type 'MemberDataSet'. The Name value should be a comma separated list of foreign key property names.
The difference here is that this relationship is Many to One i.e. 1 user can have several signedoff datasets. Is this what makes the difference? i.e. the UserProfile is now the principal object so the ForeignKey is on the MemberDataSet?
Many thanks again for any and all help.
The error
The ForeignKeyAttribute on property 'DeferredData' on type
'MemberDataSet' is not valid. The foreign key name 'DeferredDataId'
was not found on the dependent type 'DeferredData'.
is telling you exactly what is wrong.
DeferredData.Id is not DeferredData.DeferredDataId
This is your problem.
Just removing the attribute will solve your problem as Entity Framework figures out foreign keys based on the name of your entities. If you want to keep the attributes, use:
[ForeignKey("Id")]
instead of
[ForeignKey("DeferredDataId")]
So:
public class MemberDataSet
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int? DeferredDataId { get; set; }
[ForeignKey("Id")]
public virtual DeferredData DeferredData { get; set; }
}
or change the Id of DeferredData to be DeferredDataId and not Id
A few notes about EF:
Properties with names Id are automatically Keys, so no need for the Key attribute
When you define a relationship using code first you don't need to manually decorate things with attributes, EF figures it out based on the structure.
Edit:
For a One-to-many relationship you need an ICollection<T>
public virtual ICollection<MemberDataSet> MemberDataSets { get; set; }
Does UserProfile have a UserId property?

How to infer relationships and cardinality from a database when generating code from the tables?

I'm working on generating code (models) from my database. I'm not sure how to, if possible, setup my database to create a relationship including cardinality. Is this something that can be done, or am I stuck with simply generating models that match a table without relationships or cardinality because I did a data-first design?
Oh, and in case I'm using the incorrect term, cardinality is the type of relationship (one-to-one, one-to-many, many-to-many). That way my models will generate with a reference to another model, or to an ICollection<T> of models.
For example:
public class OrderInformation
{
public virtual int OrderId { get; set; }
public virtual DateTime OrderDate { get; set; }
public virtual BillingInformation BillingInfo { get; set; }
public virtual Address DeliveryAddress { get; set; }
public virtual ICollection<ItemInformation> ShoppingCart { get; set; }
}

Categories