How do you make foreign key as NOT NULL in EF Core migrations with Data Annotations? - entity-framework-core

I'm using ASP.NET Core and EF Core and I have the two following parent and child classes. Each gift card can have many transactions:
public class GiftCard
{
public int Id { get; set; }
public string BarCode { get; set; }
public DateTime PurchaseDate { get; set; }
public string Comments { get; set; }
public byte[] Timestamp { get; set; }
public List<Transaction.Transaction> Transactions { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public DateTime TransactionDate { get; set; }
public decimal TransactionAmount { get; set; }
public TransactionType TransactionType { get; set; }
public byte[] Timestamp { get; set; }
public GiftCard.GiftCard GiftCard { get; set; }
}
Based on what I read, this is the way to do it, by having navigation property on the parent and reference navigation in child. When I add my migrations and update the database using the command line, everything seemed ok in the database except that the GiftCardId foreign key in the Transactions table is nullable. I want to make sure this is NOT NULL. Am I missing a Data Annotation attribute?

Put the following property on your Transaction entity, and it should be resolved.
public int GiftCardId { get; set; }
What is happening with your definition is that a shadow property is being created and EF's Change Tracker is maintaining the relationships.
See here.

Related

Entity Framework Core error on Insert when exists a foreign key to a View

This is my model (semplified):
PRAT is the main table
public partial class PRAT
{
public int ID { get; set; }
public string PRATICA { get; set; }
public int ANNO { get; set; }
public string VARIANTE { get; set; }
[ForeignKey("ID")]
public VW_PRATICHE_CONTIPO VW_PRATICHE_CONTIPO { get; set; }
}
VW_PRATICHE_CONTIPO is a View (not a table!) in the database that contains some data related to PRAT table
public class VW_PRATICHE_CONTIPO
{
public int ID { get; set; }
public DateTime? DATAPRES { get; set; }
public string PROTGEN { get; set; }
public string TIPO { get; set; }
public string TIPOEXTRA { get; set; }
public string TIPOISTANZA { get; set; }
public string TIPOPRAT { get; set; }
}
The one-to-one relation between the table and the View is based on the ID field.
I need this because I want to do a query like this:
context.PRAT.Include(x=> x.VW_PRATICHE_CONTIPO)
This query works as exptected.
The problem happens when I try to save a new entity in PRAT.
When i do this:
context.PRAT.Add(prat);
await context.SaveChangesAsync();
I got this error:
The property 'ID' on entity type 'PRAT' has a temporary value. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property.
If I remove the navigation property from PRAT all works fine, but I can't do the Include in my Query.
Can anybody help me?
Thank you.

EF Code first : set optional one to one relationship with data annotation

I've the following situation I try to solve : I've 2 tables, a Course table with some fields and a CourseDescription table which is optional (so Course may have a CourseDescription but CourseDescription must have a Course). I'm trying to set this up. So far, here's what I have :
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public virtual CourseDescription CourseDescription { get; set; }
}
public class CourseDescription
{
[Key, ForeignKey("Course")]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
public int CoursesID { get; set; }
[ForeignKey("CoursesID")]
public Course Course { get; set; }
}
This "works" meaning that EF doesn't complains about my model but the relation is not properly done because EF associate the PK of CourseDescription with the PK of Course. In my database, this is not the case (ex : CourseDescription.ID=1 is associated with CourseDescription.CoursesID=3, not 1).
Is there a way to fix that with data annotation ? I know I can use the fluent API but I don't want to override the model building just for that (unless there's no other way).
Thanks
Well, I think you have two choices:
Configure an one to many relationship
If you want to map the FK of the relationship between Course and CourseDescription, and you don't want to declare that FK property as Key of the CourseDescription entity, then, you don't have other choice that configure an one-to-many relationship. In that case your model would be like this:
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<CourseDescription> CourseDescriptions { get; set;}
}
public class CourseDescription
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
[ForeignKey("Course")]
public int CourseID { get; set; }
public Course Course { get; set; }
}
Configure an one-to-one relationship but not map the FK of the
relationship
The only way that EF lets you map the FK in an one-to-one relationship is when the FK is declared as a PK too, so if you want to have diferent Ids in both entities and you want to stablish an one-to-one relationship, then you could do something like this:
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public CourseDescription CourseDescription { get; set;}
}
public class CourseDescription
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
[Required]
public Course Course { get; set; }
}
And work with the navigations properties.
It looks like you should not use ForeignKey attribute for ID property of CourseDescription class as you don't want to have an association between primary keys. Try to remove it.
Edit: It looks like I misunderstood the question previous time.
You can have your CourseDescription this way.
public class CourseDescription
{
[Key, ForeignKey("Course")]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
public Course Course { get; set; }
}
In this case you don't need to have CoursesID field. Entities will be connected by primary keys.

EF Code-First creates some fields ,even I added ForeignKey annotations

can any one help me in this ?
Here is my 2 classes
class Request
{
public Nullable<int> BuyCurrencyId {get ; set;}
public Nullable<int> SaleCurrencyId {get ; set;}
[ForeignKey("SaleCurrencyId")]
public virtual Currency SaleCurrency { get; set; }
[ForeignKey("BuyCurrencyId")]
public virtual Currency BuyCurrency { get; set; }
}
class Currency
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Request> Requests { get; set; }
public virtual ICollection<Request> Requests1 { get; set; }
}
I checked the updated with EF database , and I found out that the EF create Reqyests table like this :
SaleCurrencyId int (Already exists)
BuyCurrencyId int (Already exists)
Currency_Id int (Added by EF)
Currency_Id1 int (Added by EF)
By this not thing I expect. I thing the last tow columns are not correct and they not be exist.
Can any one help me ?
I am using EF 6 alpha to update the existing database with my generated model by T4.Please keep it in mind that I want to use data annotations , not Fluent API
Sorry about my bad English
Update 1 :
I thought if I change the Currency class to this it will resolve my problem , but it did not.
class Currency
{
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("SaleCurrencyId")]
public virtual ICollection<Request> Requests { get; set; }
[InverseProperty("BuyCurrencyId")]
public virtual ICollection<Request> Requests1 { get; set; }
}
Your Update1 is almost the correct solution, but the parameter of the [InverseProperty] attribute must be the navigation property in Request, not the foreign key property:
[InverseProperty("SaleCurrency")]
public virtual ICollection<Request> Requests { get; set; }
[InverseProperty("BuyCurrency")]
public virtual ICollection<Request> Requests1 { get; set; }

breeze.js one to many

I'm currently building an SPA with Web API and knockout etc. So far i worte my own simple datacontext and it worked pretty well.
The I bumped in to breeze and thought it might be worth a try. especially I hoped to get a simpler approach on navigation between the entities...
to load a entities or a single entity with breeze worked fine. Working with navigation properties seems not to work. The navigation property is always empty, even though it's a one to many relationship.
Here is my model (simplified):
public class WorkdayHours
{
public int Id { get; set; }
public bool IsWorkDay { get; set; }
...
public Byte WeekDay { get; set; }
}
public class Service
{
public int Id { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<WorkdayHours> BookableDays { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<WorkdayHours> BookableDays { get; set; }
}
public class Shop
{
public int Id { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<WorkdayHours> BookableDays { get; set; }
}
Then I fetch the entity service ind my SPA as follow:
var query = EntityQuery
.from('Services')
.where('id', 'eq', serviceId)
.expand('BookableDays');
As when teh query is executed I get as result the requested service entity with all the data except the bookableDay property is always an empty array.
When I check the Json answer I see that also the workdayHours are transmitted and breeze even calls my defined ctors for this entities. However they are not linked to the bookableDays property itself.
When checking the genrated DB model, EF generated foreignkeys for service, employee and shop in workdayHours as expected.
Is breeze not capable with having several optional foreignkeys?
Suggestion and ideas highly apprechiated.
Breeze is dependent on Foreign Keys. I had a similar problem. This should solve it:
EF was generating the ForeignKeys for me too and the related Entites where still empty. As far as i know breeze needs the explicit Annotation/Configuration of ForeignKey Fields.
public class Mvl
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long MvlId{ get; set; }
public DateTime CreatedAt { get; set; }
[InverseProperty("Mvl")]
public ICollection<MvlOP> MvlOps { get; set; }
public DateTime? ReleasedAt { get; set; }
public DateTime? LockedAt { get; set; }
public DateTime? ClosedAt { get; set; }
//[ConcurrencyCheck]
//public int? RowVersion { get; set; }
[Timestamp]
public byte[] TimeStamp { get; set; }
}
public class MvlOP
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long MvlOpId { get; set; }
public long MvlId { get; set; }
[ForeignKey("MvlId")]
public Mvl Mvl { get; set; }
...
}

Why am I getting an extra foreign key column with Entity Framework Code First Foreign Key Attributes?

I recently came across this strange problem with Entity Framework Code First.
My class looks like this
public class Status
{
[Key]
public int StatusID { get; set; }
public string Name { get; set; }
public int MemberID { get; set; }
[ForeignKey("MemberID")]
public virtual Member Member { get; set; }
public int PosterID { get; set; }
[ForeignKey("PosterID")]
public virtual Member Poster { get; set; }
public virtual ICollection<StatusLike> StatusLikes { get; set; }
public virtual ICollection<StatusComment> StatusComments { get; set; }
}
My Member class looks like this
public class Member
{
[Key]
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Bio { get; set; }
public virtual ICollection<MemberCourseTaken> MemberCourseTakens { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
public virtual ICollection<Club> FoundedClubs { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public int AccountSourceID { get; set; }
public AccountSource AccountSource { get; set; }
public int AddressID { get; set; }
public Address Address { get; set; }
public string ProfilePhoto { get; set; }
public int MemberRankID { get; set; }
public MemberRank MemberRank { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}
And for whatever reason the database table that is created has the following columns
StatusID
Name
MemberID
PosterID
Member_MemberID
with MemberID, PosterID, and Member_MemberID being foreign keys.
How can I keep Member_MemberID from being generated?
Your Member_MemberID column is created because of the Member.Statuses property. I can imagine that this is not what you want. Probably members and statuses should exist independent of each other, so you need a junction table.
I don't know if you already use the OnModelCreating override of the DbContext, but that's the place to change the mapping between Member and Status:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Member>().HasMany(m => m.Statuses).WithMany();
}
This will create a table MemberStatuses table with the two Id columns as foreign keys. This is a way to model a many-to-many relationship without a navigation property on the "other" side of the association. (I don't think you want a Members property in Status).
I've seen this before. In my case (Using EF 6.1), it was because my Fluent API Mapping was set up like so:
// In my EntityTypeConfiguration<Status>
HasRequired(x => x.Member).WithMany().HasForeignKey(x => x.MemberID);
That code works perfectly fine, but it doesn't tell EF that my Member class's Collection Navigational Property Status ha been taken into account. So, while I explicitly handled the existence of a Member Navigational Property in my Status Class, I now left an orphaned related collection property. That orphaned property, being a collection, tells EF that my Status class needs to have a Foreign Key to it. So it creates that on the Status Class.
To fix it, I had to be 100% explicit.
HasRequired(x => x.Member).WithMany(x => x.Statuses).HasForeignKey(x => x.MemberID)
It could bee that your Statuses Collection property in Member needs an attribute telling it that it is already considered, and not to go auto-creating mappings. I don't know that attribute.