Entity Framework Core Two Objects as Primary Key - c#

I have a model which is used for managing friend relationships. It looks as follows:
public class Relationship
{
[Required]
public User User { get; set; }
[Required]
public User Friend { get; set; }
[Required]
public DateTimeOffset RelationshipInitializationDate { get; set; }
}
Users will have multiple records for their ID and there will be multiple records with the same FriendID so defining either of these as a key is a no-go. I would like the key to be a composite between User and Friend but when I define it like this:
modelBuilder.Entity<Relationship>().HasKey(r => new { r.User, r.Friend });
I get an error that states:
The property 'Relationship.User' is of type 'User' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
How should I go about this to create the primary key that will link with a user and friend object. I didn't have any issues with my other objects having typed properties and I don't have an issue if I add an arbitrary key to the Relationship model. Thanks in advance

The basic idea here is that your adding properties to the model that EF can use to make a relationship. Right you're trying to create a relationship of type User and that is creating an error. To assign a composite key each key needs to be a type compatible with a Key field, not a navigation property. So we add UserId and FriendId of type int, string or GUID etc. and create a relationship off those properties.
public class Relationship
{
public User Friend { get; set; }
public User User { get; set; }
public int FriendId { get; set; }
public int UserId { get; set; }
public DateTimeOffset RelationshipInitializationDate { get; set; }
}
public class User
{
public int UserId { get; set; }
}
You can now define a composite key across UserId and FriendId. Something like this should do:
public class NorthwindContext : DbContext
{
public NorthwindContext(DbContextOptions<NorthwindContext> options):base(options) { }
public NorthwindContext() { }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Relationship>().HasKey(table => new {
table.FriendId, table.UserId
});
}
public DbSet<Relationship> Relationships { get; set; }
public DbSet<User> Users { get; set; }
}
Source: Medium - How To: Entity Framework Core relationships, composite keys, foreign keys, data annotations, Code First and Fluent API

Related

Receiving error "The entity type requires a primary key to be defined" for a list with a key defined

I have two classes Pick and PickList. I have successfully added a single Pick to the database, but now I want to add multiple Picks in a PickList to be added to the database in a single call. I keep receiving the error
The entity type 'PickList' requires a primary key to be defined. If
you intended to use a keyless entity type, call 'HasNoKey' in
'OnModelCreating'. For more information on keyless entity types, see
https://go.microsoft.com/fwlink/?linkid=2141943.
I tried making PickList Keyless, which did not work and resulted in the error
System.InvalidOperationException: Unable to track an instance of type
'PickList' because it does not have a primary key. Only entity types
with a primary key may be tracked.
I cannot find an example in the Microsoft documentation of adding a List to the database.
Pick
public class Pick
{
[Key]
public string? Username { get; set; }
public string? Game { get; set; }
public string? Selection { get; set; }
}
PickList
public class PickList
{
[Key]
public List<Pick>? Picks { get; set; }
}
DBContext
public DbSet<PickList>? Selections { get; set; }
Program
app.MapPost(
"/selections", async (PickList pick, DataContext db) => {
db.Selections?.Add(pick);
await db.SaveChangesAsync();
return Results.Ok();
}
The best option for the Primary keys is numbers. So it would be better to fix the models a bit and make a new migration. Try using the models this way.
Of course, you can also use strings for primary keys, but you have no benefit from this, except slower search in the database. If the entities are too many and the int or long are too shorts, you can use Guid
public class Pick
{
[Key]
public int Id { get; set; }
public string? Username { get; set; }
public string? Game { get; set; }
public string? Selection { get; set; }
}
public class PickList
{
public PickList()
{
this.Picks = new HashSet<Pick>();
}
[Key]
public int Id { get; set; }
public IColection<Pick>? Picks { get; set; }
}

Entity Framework - How to apply constraint to model properties?

I am using Entity Framework 6.1.3 and have the two models as shown below. However, when I run migration I get the below error:
Unable to determine the principal end of an association between the
types 'Example.Models.GiftVoucher' and
'Example.Models.Payment'. The principal end of this
association must be explicitly configured using either the
relationship fluent API or data annotations.
I have searched for it and found this question. One of the solution was to use the Required attribute.
However, I don't know how I can do the following in Entity Framework:
Rename GiftVoucherId and PaymentId in both models that are as foreign keys to Purchase_Id and Redemption_Idas shown in the image.
Then do something equivalent in Entity Framework like this CONSTRAINT fk-giftvoucher-table FOREIGN KEY (Purchase_Id) REFERENCES PAYMENT (PaymentId).
Payment Model
public class Payment {
public int Id { get; set; }
[Required]
public DateTime DateTime { get; set; }
public double Amount { get; set; }
[Required]
public int GiftVoucherId { get; set; }
public GiftVoucher GiftVoucher { get; set; }
}
Gift Voucher Model
public class GiftVoucher
{
public int Id { get; set; }
public double Amount { get; set; }
[Required]
public int PaymentId { get; set; }
public Payment Payment { get; set; }
}
I don't tend to use one-to-one relationships a lot... Unless i want to Vertically Partition for performance needs or there is an incessant OCD need to separate the concerns.
*Note: One-to-one relationship is technically not possible in SQL Server. It will always be one-to-zero-or-one. EF forms One-to-One relationships on entities not in DB.*
However, documentation is your friend
Configuring a Relationship Where Both Ends Are Required (One-to-One)
In most cases the Entity Framework can infer which type is the
dependent and which is the principal in a relationship. However, when
both ends of the relationship are required or both sides are optional
the Entity Framework cannot identify the dependent and principal. When
both ends of the relationship are required, use WithRequiredPrincipal
or WithRequiredDependent after the HasRequired method.
...
Given the following
public class GiftVoucher
{
// your primary key
public int GiftVoucherId { get; set; }
public virtual Payment Payment { get; set; }
// other properties
public double Amount { get; set; }
}
public class Payment
{
// We need to share the same key
public int GiftVoucherId { get; set; }
public virtual GiftVoucher GiftVoucher { get; set; }
// other properties
public DateTime DateTime { get; set; }
public double Amount { get; set; }
}
We can Fluent API like this
// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<Payment>()
.HasKey(t => t.GiftVoucherId);
// we are essentially making GiftVoucher the principle in the DB
modelBuilder.Entity<GiftVoucher>()
.HasRequired(t => t.Payment)
.WithRequiredPrincipal(t => t.GiftVoucher);
So basically HasRequired is making GiftVoucher required and WithRequiredPrincipal is making Payment required.. In turn, EF will throw if the above is not satisfied

1 to 1 required relationship with database generated identity

I have seen many examples of implementing a one to one relationship, but I failed doing mine, because the requirements are some kind different (Guid with database generated option, foreign key property and so on).
I have 2 classes (Bundesland, Programmkonfiguration) that have a 1:1 relationship (both ends are required in business sense) but cannot be joined into one table
Requirements to Bundesland:
Guid Id as Key but without a DatabaseGenerated Attribute
Navigation Property Programmkonfiguration
Bundesland.cs:
public class Bundesland
{
[Key]
public Guid Id { get; set; }
public virtual Programmkonfiguration Programmkonfiguration { get; set; }
}
Requirements to Bundesland
Guid Id as Key generated from Database
ForeignKey Property Bundesland_Id (needed with _ for interface)
Navigation Property Bundesland
Programmkonfiguration.cs:
public class Programmkonfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Bundesland_Id { get; set; }
public virtual Bundesland Bundesland { get; set; }
}
database schema should look like this
table Bundesland (Id)
table Programmkonfiguration (Id, Bundesland_Id)
Why I failed until now:
EF doesn’t recognize the relationship by itself
if I use either attributes (ForeignKey, Required) or fluent API and the mode builder is not failing, the foreign key property Programmkonfiguration.Bundesland_Id is never set, after context.SaveChanges()
If you want to help me, here are additional classes you may gonna need: https://gist.github.com/anonymous/9cb554cd864e3dbee1ac
I am using .NET 4.5(.1) with EF5, but I failed with EF6 too
Thanks in advance :)
You can use fluent configuration for this:
public class Bundesland
{
[Key]
[ForeignKey("Programmkonfiguration")]
public Guid Id { get; set; }
public virtual Programmkonfiguration Programmkonfiguration { get; set; }
}
public class BundesLandConfiguration: EntityTypeConfiguration<Bundesland>
{
public BundesLandConfiguration()
{
HasProperty(p=>p.Id)
HasRequired(p=>p.Programmkonfiguration).WithRequiredPrincipal(p=>p.Bundesland);
}
}
public class Programmkonfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Bundesland_Id { get; set; }
public virtual Bundesland Bundesland { get; set; }
}
public class ProgrammkonfigurationConfiguration: EntityTypeConfiguration<Programmkonfiguration>
{
public ProgrammkonfigurationConfiguration()
{
HasKey(p=>p.Id);
HasProperty(p=>p.Id)
HasProperty(p=>p.Bundesland_Id)
}
}
Do not forget to add this configurations to EntityModelConfigurations in db context.
Update: because property naming is against convention, you should add [ForeignKey] attribute as I added to property Id of Bundesland class.

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.

How to properly setup model, POCO class?

I'm slightly confused on how to properly setup my model. Below you'll see my POCOs and I'm wondering how I can auto increment the ID's and if it's necessary to set the data annotation [Key]. Is there a naming convention of some sort which makes EF recognize the ID/primary key or do I have to use the [Key] data annotation?
Also is it necessary to set a [Key] data annotation in the child entity?
public class User
{
[Key]
public int UserId { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime Reg { get; set; }
public virtual ICollection<Stats> Stats { get; set; }
}
public class Stats
{
[Key]
public int StatId { get; set; }
public string Age { get; set; }
public string Height { get; set; }
public string Weight { get; set; }
public bool Sex { get; set; }
}
public class BodylogContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Stats> Stats { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<BodylogContext>(null);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
You should look up Entity Framework Code First tutorials for more details.
Specifically, in your case and a few basic rules (disclaimer:) I'm not trying to cover everything just a few basic ones....
You can remove [Key] -
if you use <Entity>Id - or just Id it's made into a PK by default.
Same goes for 'FK-s' and related navigation properties (except that <Property>Id is also mapped by convention),
It's case insensitive.
Identity is by default - for pk types that makes sense - int, long... - not for strings,
If you have more than one pk - then you'd need to 'decorate' it with Key - or in fluent config,
etc...
Note: you can adjust and remove conventions from the fluent configuration.
Also from EF6 you'll be able to define a new ones for your code.
My recommendation: Turn on Migrations and look up the migrations
script code (.cs file) generated file. It always has the clear
description of what are keys, indexes etc. Best way to learn how your
Db is actually created.
I'm just getting with MVC too and I found that this tutorial answered most of the questions you have asked.
By default, the Entity Framework interprets a property that's named ID or classnameID as the primary key. So in your User class, you do not need the [Key] attribute on the UserId property. In your Stats class, the property does not match the name of the class (you have pluralised the name) so here you would need the attribute.
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application

Categories

Resources