Entity Framework Core - optional foreign key - .net-core

I am creating a web api that needs to return details about vehicles. The first part works fine, just returning data from my vehicles table. Now I have another table which may or may not contain additional data about vehicles in the first table. So when I get vehicle data, I want all of the vehicle data, and any additional data from the second table if it exists, like a left join in SQL.
Here are my classes (very much abridged for readability):
public class Vehicle
{
[Key]
[Required]
public string registrationNo { get; set; }
public string capacity{ get; set; }
public string maxGross{ get; set; }
}
public class VehicleDvlaDetail
{
[ForeignKey("Vehicle")]
public string? registrationNumber { get; set; }
public int? co2Emissions { get; set; }
}
And in my context class OnModelCreating I have (again, very abridged):
modelBuilder.Entity<Vehicle>(entity =>
{
entity.HasOne(dvlaRec => dvlaRec.dvlaDetail).WithMany().HasForeignKey(dvla => dvla.registrationNo);
});
This works fine when there is an associated record in the DVLA table, but that isn't always the case. I need to keep them as separate entities as my API will be required to return details from the DVLA table separately as well. Is there any way to create an optional foreign key, as clearly, what I am doing is wrong.

Friendly advice:
Primary key as a string is not a good practice because of performance issues when data table has lots of data in it.
It would be better if you create your model like this:
public class Vehicle
{
public long Id { get; set; }
public string RegistrationNo { get; set; }
public string Capacity { get; set; }
public string MaxGross { get; set; }
public List<VehicleDvlaDetail> VehicleDvlaDetails { get; set; }
}
public class VehicleDvlaDetail
{
public long? VehicleId { get; set; }
public int? Co2Emissions { get; set; }
public Vehicle Vehicle { get; set; }
}
Vehicle and VehicleDvlaDetail are now connected without additional code in OnModelCreating method and it is possible to fetch vehicles with details like this (this is assuming you have named properties in dbcontext Vehicles and VehicleDvlaDetails):
_dbContext.Vehicles.Include(x => x.VehicleDvlaDetails).ToList();
Also as foreign key VehicleId is nullable - this allows for vehicles not to have any dvla details.

Wow. I spent about 3 hours looking for the answer, just posted the question and came across this:
Create an optional foreign key using the fluid-API for Entity Framework 7
So simple...

Related

Cannot insert duplicate key. Composite Primary key. ASP.NET Entity Framework

I'm trying to create application using Entity Framework.
There's what I want to do (every entity has Id as well):
I want to use composite primary key in this case (PatientId + DiagnosisId).
There's a collection of Diagnoses in Patient model class:
public ICollection<Diagnosis> Diagnoses { get; set; }
public class Diagnosis
{
[Required]
[MaxLength(200)]
public String Type { get; set; }
public String Complications { get; set; }
public String Details { get; set; }
public Int32 DiagnosisId { get; set; }
public Patient Patient { get; set; }
public Int32 PatientId { get; set; }
}
Also in the database context I defined
public DbSet<Diagnosis> Diagnoses { get; set; }
and
modelBuilder.Entity<Diagnosis>().HasKey(x => new { x.DiagnosisId, x.PatientId });
in OnModelCreating method to create the composite primary key.
In an ASP.NET MVC CRUD controller I create Diagnosis and every time DiagnosisId is the same = 0. And I can't paste new data to database because it's like duplicate. That's my create post method on Pastebin if it could help
The parent key goes first:
modelBuilder.Entity<Diagnosis>().HasKey(x => new { x.PatientId, x.DiagnosisId });
Because you want all the Diagnoses for a particular Patent to be stored together.
And you need to request that the key is generated by the database. For EF Core its:
modelBuilder.Entity<Diagnosis>().Property(r => r.DiagnosisId).ValueGeneratedOnAdd();
for EF6 it's:
modelBuilder.Entity<Diagnosis>().Property(r => r.DiagnosisId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

Is ok to add [BsonAttribute] to POCOs?

I have an application, structured like this:
Application.Domain
Application.Web.Mvc
Application.MongoDb
In Application.Domain i keep all the POCOs of the application (the domain models).
public class Product
{
public string Id { get; set; }
public string ProductName { get; set; }
public DateTime CreatedDate { get; set; }
}
Now, because i am using MongoDb, i also need to use some of the [BsonAttribute], in order to customize the serialization process.
For example:
public class Product
{
[BsonId]
public string Id { get; set; }
public string ProductName { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local, DateOnly = true)]
public DateTime CreatedDate { get; set; }
}
If i add these attributes, i will need to also add a reference to MongoDB.Bson.Serialization.Attributes in the Application.Domain project, which i want to avoid.
I think the correct way to do this is to create mapping objects in the Application.MongoDb project, and always map them from POCO to MongoObjects and the other way around every time i work with MongoDb repos.
If this is the correct solution, isn't this a bit overkill?

Asp.net child object with two parents

Let s say i have two tables, "article" and "event". They both have comments so they both need access to the "comments" entity.
How do i declare this relationship? having two foreign keys in "comments" is problematic since one of the foreign keys will be null in every case.
Can i just not declare any foreign key? Is there a convention on how to deal with this situation?
public class Article
{
public int Id { get; set; }
public virtual IEnumerable<Comment> comments { get; set; }
}
public class Event
{
public int Id { get; set; }
public virtual IEnumerable<Comment> comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public Article article { get; set; }
public Event event{ get; set; }
}
I would maybe consider a dedicated comment table per entity. One comment table for article and another for event. Especially if individual comments never apply to both articles and events.
However, there is nothing that prevents you from making a shared one with two FKs as you described.

Is it OK to declare a DBSet in the context for both a base table and a derived table?

I have a SalesOrder table which inherits from a SalesDocument table using Table Per Type Inheritance
The ( simplified) table classes are;
[Table("SalesDocumentHeaders")]
public abstract class SalesDocumentHeader
{
[ForeignKey("CreatedByUserId")]
public virtual User CreatedBy { get; set; }
[Required]
public int CreatedByUserId { get; set; }
[Required]
public virtual DateTime? DocumentDate { get; set; }
[Required]
public String ReferenceNumber { get; set; }
}
[Table("SalesOrders")]
public class SalesOrder : SalesDocumentHeader
{
[Required]
public String CustomerOrderNumber { get; set; }
public DateTime? DeliverBy { get; set; }
public virtual SortableBindingList<SalesOrderLine> Lines { get; set; }
}
The context contains
public DbSet<SalesOrder> SalesOrders { get; set; }
public DbSet<SalesDocumentHeader> SalesDocumentHeaders { get; set; }
It doesn't strictly need the SalesOrders DBSet, since SalesOrder inherits from SalesDocumentHeader however I find it convenient.
It seems to work OK, but I am worried that there are 2 ways of reaching the same record , am I doing something wrong?
Usually you only need to keep the DBSet for the base table. This helps when you have multiple derived tables (call them A and B) and you need to decide the actual type dynamically.
For example if you have another entity which references type A or B (like a user can have different types of contact information), you can reference the base table and EF will resolve the correct concrete type at runtime. Though of course this adds some extra casting code.

make both models aware of each other with a one-to-one relationship in asp.net entity framework

I am unable to get the one to one relationship with EF to work properly. I've scored blogs, SO, and msdn docs, but nothing I do seems to work.
I have two models, a Class and an Exam that look like the following:
[Table("classes")]
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(255), Required]
public string Title { get; set; }
public virtual Exam Exam { get; set; }
}
[Table("exams")]
public class Exam
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:h:mm tt}")]
public DateTime? Time { get; set; }
public int ClassId { get; set; }
public virtual Class Class { get; set; }
}
I want to be able to access the exam from the Class and the Class from the Exam, but no matter what I do, I find some error.
Trying to create/run migrations I get the following.
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
If I add this to my context:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<Exam>()
.HasRequired(e => e.Class)
.WithOptional(c => c.Exam);
}
I get the following error:
System.Data.Entity.Infrastructure.DbUpdateException: Entities in 'BenchContext.Exams' participate in the 'Class_Exam' relationship. 0 related 'Class_Exam_Source' were found. 1 'Class_Exam_Source' is expected. ---> System.Data.UpdateException: Entities in 'BenchContext.Exams' participate in the 'Class_Exam' relationship. 0 related 'Class_Exam_Source' were found. 1 'Class_Exam_Source' is expected.
I'm not sure how to tell the fluent api how to correctly foreign key between the two models and nothing I do seems to affect it.
What am I missing?
You are trying to build one-to-one relation through ClassId property in Exam class. That requires ClassId to be unique (= unique constraint) but unique constraint are not supported by EF yet.
The only way EF currently supports real one-to-one relation is by sharing primary key:
[Table("classes")]
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(255), Required]
public string Title { get; set; }
public virtual Exam Exam { get; set; }
}
[Table("exams")]
public class Exam
{
[Key, ForeignKey("Class")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:h:mm tt}")]
public DateTime? Time { get; set; }
public virtual Class Class { get; set; }
}
The Id in Exams table is both PK and FK to Classes table. It cannot have autogenerated value. While this is one-to-one relation it still has 1 - 0..1 multiplicity (Class can exist without Exam but Exam must have Class).

Resources