EF6 Multiple foreign keys for ApplicationUser Error - c#

I'm pretty new to Entity Framework and the code first approach and I'm stuck. Sorry if it's a dumb question.
I found some questions around here that look the same, but I get an other error then the ones other people get and I would love to solve it without the need to add other parameters if possible.
So Basically, I have my ApplicationUser(load in from Identity) that looks like this:
public class ApplicationUser : IdentityUser
{
public virtual Province Provincie { get; set; }
[ForeignKey("From")]
public virtual ICollection<Message> SentMessages { get; set; }
[ForeignKey("To")]
public virtual ICollection<Message> ReceivedMessages { get; set; }
}
And I have a Message Class that looks like:
public class Message
{
[Key]
public int ID { get; set; }
public virtual ApplicationUser From { get; set; }
public virtual ApplicationUser To { get; set; }
public String MessageContent { get; set; }
public DateTime Date { get; set; }
}
Now, when I try to add a migration i get the following error:
The ForeignKeyAttribute on property 'ReceivedMessages' on type 'EF_CF_Basics.Models.ApplicationUser' is not valid. The foreign key name 'To' was not found on the dependent type 'EF_CF_Basics.Models.Message'. The Name value should be a comma separated list of foreign key property names.
So actually, visual studio tells me it can't find the To in Message, but it is really there.

You probably want to use [InverseProperty("From")] instead of [ForeignKey("From")] and [InverseProperty("To")] instead of [ForeignKey("To")]. Foreign key properties must be scalars (int, string, Guid, etc.) but your From and To properties are actually entities, i.e. they are navigation properties.

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

EF Code First 1:0..1 relationship error: Multiplicity is not valid in Role 'EFEmployee_Identity_Source' in relationship 'EFEmployee_Identity'

Full error:
One or more validation errors were detected during model generation:
EFEmployee_Identity_Source: : Multiplicity is not valid in Role 'EFEmployee_Identity_Source' in relationship 'EFEmployee_Identity'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I am dealing with three types of entities: EFEmployee, EFPerson, and EFOffice. It's kind of weird that I'm getting this error because the code I'm testing only creates an instance of an EFOffice entity. Anyway, here is the EFEmployee entity class:
[Table("employee_entity")]
public class EFEmployee : EFBusinessEntity
{
[ForeignKey("Office")]
public Guid OfficeID { get; set; }
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
[Column("hire_date")]
public DateTime HireDate { get; set; }
[Column("job_title")]
public byte[] JobTitle { get; set; }
[Column("salary")]
public int Salary { get; set; }
[Column("certifications")]
public byte[] Certifications { get; set; }
[Column("vacation_time")]
public int VacationTime { get; set; }
[Column("sick_time")]
public int SickTime { get; set; }
public virtual EFOffice Office { get; set; }
public EFPerson Identity { get; set; }
public virtual EFEmployee ReportingTo { get; set; }
}
And this is my EFPerson entity class:
[Table("person_entity")]
public class EFPerson : EFBusinessEntity
{
[Column("first_name")]
[StringLength(50)]
public string FirstName { get; set; }
[Column("last_name")]
[StringLength(50)]
public string LastName { get; set; }
[Column("phone_num")]
public uint? PhoneNum { get; set; }
[Column("date_of_birth")]
public DateTime DateOfBirth { get; set; }
public EFEmployee Employee { get; set; }
}
You can see that they both inherit from EFBusinessEntity, which is here:
[Table("business_entity")]
public abstract class EFBusinessEntity : IBusinessEntity
{
[Column("tenant_id")]
public Guid TenantId
{
get;
set;
}
[Column("id")]
[Key]
public Guid Id
{
get;
set;
}
}
As you can see, there is a one-to-zero-or-one relationship between EFEmployee and EFPerson, with EFEmployee being the dependent side since there can be a person who is not an employee, but there can't be an employee who is not a person too. Since EFEmployee is the dependent side, I have added a PersonID in EFEmployee with the data annotation (attribute?) above denoting that it's the foreign key to Person:
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
I think I've made it pretty clear for Entity Framework that this is a 1:0..1 relationship. Does anyone know how to solve this error using data annotations (or attributes, whatever those square bracket things above properties are). I can't use fluent API for reasons I'm not getting into.
Generally, with 1:0..1 relationships in Entity Framework, the dependent side needs to use its primary key as the foreign key. Fortunately, for your case, this doesn't seem like it would be a bad idea. You would need to:
Remove the EFEmployee.PersonID property
Add [ForeignKey("Id")] to EFEmployee.Identity
Edit: May not work because key and navigation property are on separate classes. See this.
Having EFEmployee inherit from EFPerson seems like it might be a viable option as well. Inheritance uses TPH by default, but if you want to use TPT (table-per-type), add the [Table] attribute to your type.
I did some more playing around with the models and found out what was wrong. So I kept the foreign key attribute with EFPerson.Identity like jjj suggested:
[ForeignKey("PersonID")]
public virtual EFPerson Identity { get; set; }
Then the other change I had to make was in the EFPerson class. In my EFPerson class I had the navigation property to EFEmployee:
public virtual EFEmployee Employee { get; set; }
However, since this is a 1:0..1 relationship with EFEmployee being the dependent side (i.e. the non-essential side), I removed that navigation property, and when I ran my test it worked.

Entity Framework getting confused about navigation property

I'm using Entity Framework 6.1.1 and I have a Users table and a User_Documents table (1:many). I already had a navigation property from User_Documents to User were things were working fine.
public partial class User_Document
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_Document_ID { get; set; }
public long User_ID { get; set; }
[ForeignKey("User_ID")]
public virtual User User { get; set; }
}
I added a navigation property from Users to User_Documents
public partial class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_ID { get; set; }
[StringLength(50)]
public string Username { get; set; }
public virtual List<User_Document> Documents { get; set; }
}
and now I'm getting an error when I try to run the application:
System.Data.Entity.ModelConfiguration.ModelValidationException: One or
more validation errors were detected during model generation:
User_Documents: Name: Each member name in an EntityContainer must be
unique. A member with name 'User_Documents' is already defined.
Of course there is a table called User_Documents but no other property with that name. I'm not sure what's it getting confused by. Maybe it's taking the table name "User" and the property name "Documents" and trying to create something called "User_Documents" out of it? If I rename it to from Documents
to Some_Documents like this
public virtual List<User_Document> Some_Documents { get; set; }
then I get a different error stating
System.InvalidOperationException: The model backing the
'PipeTrackerContext' context has changed since the database was
created. Consider using Code First Migrations to update the database
So I run Add-Migration and I get this:
public override void Up()
{
AddColumn("dbo.User_Documents", "User_User_ID", c => c.Long());
CreateIndex("dbo.User_Documents", "User_User_ID");
AddForeignKey("dbo.User_Documents", "User_User_ID", "dbo.Users", "User_ID");
}
Why is it trying to add a new column called User_User_ID? Why can't I just add the Document navigation property like I want?
use InverseProperty Like this :
public partial class User_Document
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_Document_ID { get; set; }
public long User_ID { get; set; }
[ForeignKey("User_ID")]
[InverseProperty("Documents")]
public virtual User User { get; set; }
}
And :
public partial class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long User_ID { get; set; }
[StringLength(50)]
public string Username { get; set; }
[InverseProperty("User")]
public virtual List<User_Document> Documents { get; set; }
}
Why is it trying to add a new column called User_User_ID? Why can't I
just add the Document navigation property like I want?
By convention, it will create the foreign key as tablename_columnName which is User_User_ID. This happens when you remove the [ForeignKey("User_ID")] attribute OR don't have foreign key property.
If you change the property name Documents to something else (likeUserDocuments) you wont face this conflict of names.

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.

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