EF relation - cannot map due to list<guid> being primitive - c#

I'd want to store in User class collection of Items
But I cannot overcome this problem:
System.InvalidOperationException: 'The property 'User.ItemsIds' could not be mapped, because it is of type 'List' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.'
I'm using ASP.NET Core 2.0 + EFCore
public class User
{
[Key]
public Guid Id { get; set; }
[ForeignKey("Items")]
public virtual List<Guid> ItemsIds{ get; set; }
(...)
}
public class Item
{
[Key]
public Guid Id { get; set; }
public string ItemName { get; set; }
public Item(string name)
{
Id = new Guid();
ItemName = name;
}
}
public class AppContext : DbContext
{
public AppContext(DbContextOptions<AppContext> options) : base(options)
{
Database.SetCommandTimeout(35000);
}
public DbSet<User> Users { get; set; }
public DbSet<Item> Items { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Item>().ToTable("Items");
}
}
Here's similar problem, but still it hasn't been "fixed"?
Entity Framework - Code First - Can't Store List<String>

You can't store dependent entity identifiers only.
Principal entity User must have collection of Item:
public class User
{
[Key]
public Guid Id { get; set; }
public virtual List<Item> Items { get; set; }
// ...
}

how about use Item object in the collection instead of itemIDs and use the Fluent API to map the relationship one to many. use Has/With pattern to configure One To Many Relationships in Entity Framework Core
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Company Company { get; set; }
}
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasMany(c => c.Employees)
.WithOne(e => e.Company);
}

try to use icollection instead list and map to explicit code
class Item{public virtual User User{get;set;};public Guid UserId{get;set;}}
modelbuilder.Entity<Item>.HasOne(s => s.User)
.WithMany(n => n.ItemsIds).HasForeignKey....

Related

Reuse table from multiple entities in entity framework core 3.1

I try to create the following database design with EF Core 3.1 (code-first)
Entity "Recipe" can have a list of type "Resource" and a single type of "NutritionFacts"
Entity "Ingredient" can have a single "NutritionFacts"
Entity "Instruction" can have a list of type "Resource"
But I found no way to implement this without having multiple "NutritionFacts" or "Resource" tables. (RecipeNutritionFacts-/IngredientNutritionFacts)
And I also don't want to blow up my "Recipe" or "Ingredient" tables with the columns from the "NutritionFacts-/Resource" entities. (owned types)
Goal: I would like to reuse the tables "Resource" and "NutritionFacts" in multiple entities.
If I delete a "Resource" from the collection in entity "Recipe" or "Instrucion" then the corresponding "Resource" entity should be also deleted. (recipe.Resource.remove(x))
Same for a "NutritionFacts" from entity "Recipe" or "Ingredient" (recipe.NutritionFacts = null)
I already tried several combinations with owned types, etc... but with no success.
Any ideas for a good implementation to reach this goal?
Example Classes:
public class NutritionFacts
{
public int NutritionFactsId { get; set; }
public decimal Kcal { get; set; }
public decimal Fat { get; set; }
}
public class Resource
{
public int ResourceId { get; set; }
public string Path { get; set; }
}
public class Ingredient
{
public int IngredientId { get; set; }
public string Title { get; set; }
public virtual NutritionFacts NutritionFacts { get; set; }
}
public class Recipe
{
public int RecipeId { get; set; }
public string Title { get; set; }
public virtual NutritionFacts NutritionFacts { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
public class Instruction
{
public int InstructionId { get; set; }
public string Title { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
Example Context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// variant-1 owned
modelBuilder.Entity<Ingredient>().OwnsOne(x => x.NutritionFacts).HasKey(r => r.UniqueIdentifier);
modelBuilder.Entity<Entities.Recipe>().OwnsOne(x => x.NutritionFacts).HasKey(r => r.UniqueIdentifier);
// variant-2
modelBuilder.Entity<Entities.Recipe>()
.HasOne(p => p.NutritionFacts).WithOne();
modelBuilder.Entity<Ingredient>()
.HasOne(p => p.NutritionFacts).WithOne();
}

How to define a second Entity Framework Core relationship to the same foreign key?

I have the following entity relation diagram, I have to translate to a C# model, later used within an ASP.NET Core 3.1 web application and handled by Entity Framework Core.
There are two entities (Product, and ProductVersion).
A Product may have none, one or multiple ProductVersion; this relation is called Versions and Product (the right side of the diagram).
A Product may also have none or one ProductVersion which represent the current version; this relation is called CurrentVersion and Product (the left side of the diagram).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.InMemory;
public static class Program
{
public static void Main()
{
Console.WriteLine("Hello, World!");
var optBuilder = new DbContextOptionsBuilder<MyContext>();
optBuilder.UseInMemoryDatabase("MyContext");
MyContext context = new MyContext(optBuilder.Options);
context.Add(new Product { Name = "First Product" });
}
}
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<ProductVersion> ProductVersions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasOne(p => p.CurrentVersion).WithOne(pv => pv.Product);
modelBuilder.Entity<Product>().HasMany(p => p.Versions).WithOne(pv => pv.Product);
}
}
public class Product
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid CurrentVersionId { get; set; }
[ForeignKey("CurrentVersionId")]
public virtual ProductVersion CurrentVersion { get; set; }
[ForeignKey("ProductId")]
public virtual List<ProductVersion> Versions { get; set; }
}
public class ProductVersion
{
[Key]
public Guid Id { get; set; }
public Guid ProductId { get; set; }
public Version Version { get; set; }
public virtual Product Product { get; set; }
}
Here is a .NET Fiddle link for quick experiments: https://dotnetfiddle.net/r1bhGs
The error message is the following:
Unhandled exception. System.InvalidOperationException: Cannot create a
relationship between 'Product.Versions' and 'ProductVersion.Product',
because there already is a relationship between
'ProductVersion.Product' and 'Product.CurrentVersion'. Navigation
properties can only participate in a single relationship.
Is there really no way around a duplicate field for ProductVersion, lets call it CurrentVersionProductId, or is there a fluent solution to this?
In a second attempt, I tried to implement only a CurrentProduct navigational property.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasOne(p => p.CurrentVersion).WithOne(pv => pv.CurrentProduct);
modelBuilder.Entity<Product>().HasMany(p => p.Versions).WithOne(pv => pv.Product);
}
public class Product
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid CurrentVersionId { get; set; }
[ForeignKey("CurrentVersionId")]
public virtual ProductVersion CurrentVersion { get; set; }
[ForeignKey("ProductId")]
public virtual List<ProductVersion> Versions { get; set; }
}
public class ProductVersion
{
[Key]
public Guid Id { get; set; }
public Guid ProductId { get; set; }
public Version Version { get; set; }
public virtual Product Product { get; set; }
public virtual Product CurrentProduct { get; set; }
}
Here is the .NET Fiddle for this code: https://dotnetfiddle.net/zkZwgF
However, I ran into the following error:
Unhandled exception. System.InvalidOperationException: The entity type
'Version' requires a primary key to be defined. If you intended to use
a keyless entity type call 'HasNoKey()'.
Why have to be Version the primary key, when Id is already marked as the primary [Key]?
Try adding to the latter:
modelBuilder.Entity<ProductVersion>().OwnsOne(p => p.Version);
Owned Entity Types
Your Version property is of type Systme.Version which EF recognizes as a navigation property and it can't determine a primary key for this entity, cause you have not defined it in any way.

How to make Entity Framework use table names instead of class names when naming foreign keys?

If I have the following model:
[Table("Person")]
public class PersonDao
{
public Guid Id { get; set; }
public ICollection<Address> { get; set; }
// other properties
}
[Table("Address")]
public class AddressDao
{
public Guid Id { get; set; }
public PersonDao Person { get; set; }
// other properties
}
Entity Framework uses Person and Address correctly for the table names but the foreign key in Address is called PersonDao_Id. I want it to be Person_Id.
Is this a bug or am I supposed to write a custom convention for the property names?
NOTE: I use MySQL with Entity Framework, I don't know if that matters.
EDIT: I know that I can specify the column name manually using the ForeignKey attribute or the fluent API. I need this to work automatically and globally.
Use attributes just like you did to have different names for the table and class:
[Table("Address")]
public class AddressDao
{
public Guid Id { get; set; }
[ForeignKey("Person_Id")]
public PersonDao Person { get; set; }
// other properties
}
If you don't want to use the default convention you could just remove Dao from your class names:
public class Person
{
public Guid Id { get; set; }
public ICollection<Address> { get; set; }
// other properties
}
public class Address
{
public Guid Id { get; set; }
public Person Person { get; set; }
// other properties
}
If you want make your own name of column in database, you can use Fluent API in protected override void OnModelCreating(DbModelBuilder modelBuilder) method in your database context. Add to your DAO class properties with column name.
[Table("Person")]
public class PersonDao
{
public Guid Id { get; set; }
public ICollection<Address> Addresses { get; set; }
// other properties
}
[Table("Address")]
public class AddressDao
{
public Guid Id { get; set; }
public Guid MyPersonDaoColumnName { get; set; }
public PersonDao Person { get; set; }
// other properties
}
and then in Fluent API write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AddressDao>().HasRequired(x => x.Person)
.WithMany(x => x.Addresses)
.HasForeignKey(x => x.MyPersonDaoColumnName);
}
but it is ugly to mix Fluent API with Attribute so you need also:
modelBuilder.Entity<AddressDao>.ToTable("Address");
modelBuilder.Entity<PersonDao>.ToTable("Person");

Composite key Entity Framework 7 "The Property Cannot Be added to Entity Type

I'm trying to make a enitity that manages membership of a user in a organization with a role. I want to restrict a user to have only one membership in an organization. I'm doing this by creating a composite key. However i get the error when i try to create the initial migrations:
InvalidOperationException: The property 'User' cannot be added to the entity type 'OrganizationLogin' because a navigation property with the same name already exists on entity type 'OrganizationLogin'.
The entity for membership
public class OrganizationLogin
{
public int OrganizationLoginId { get; set; }
public OrganizationRole Role { get; set; }
public Organization Organization { get; set; }
public OmegaUser User { get; set; }
}
My DBContext where I try to define the composite key:
public class OmegaContext : IdentityDbContext<OmegaUser,OmegaRole,int>
{
public DbSet<Log> Logs { get; set; }
public DbSet<Organization> Organizations { get; set; }
public DbSet<OrganizationLogin> OrganizationLogins { get; set; }
public DbSet<OrganizationRole> OrganizationRoles { get; set; }
public OmegaContext()
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<OrganizationLogin>(orgLogin =>
{
orgLogin.HasAlternateKey(o => new {o.User, o.Organization});
});
}
}
If i remove the OnModelCreating code, the migrations are created succesfully.
EDIT: As mentioned in the comments, the problem was that i was referencing the class and not a property that had the key of the entities
As requested, here is my solution:
public class OrganizationUnitMember
{
public int OrganizationUnitMemberId { get; set; }
public int UserId { get; set; }
public int OrganizationUnitId { get; set; }
[ForeignKey("UserId")]
public virtual OmegaUser User { get; set; }
[ForeignKey("OrganizationUnitId")]
public virtual OrganizationUnit OrganizationUnit { get; set; }
public int RoleId { get; set; }
[ForeignKey("RoleId")]
public virtual OrganizationRole Role { get; set; }
}
And the DbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationUnit>(
orgUnit =>
{
orgUnit.HasOne(ou => ou.Parent)
.WithMany(ou => ou.Children)
.OnDelete(DeleteBehavior.Restrict)
.HasForeignKey(ou => ou.ParentId);
});
builder.Entity<OrganizationUnitMember>(member =>
{
member.HasAlternateKey(m => new {m.OrganizationUnitId, m.UserId});
});
}
I had to add the ids of the referenced entities

How do I configure a navigation property to the same table in Entity Framework?

How do I configure Entity Framework using fluent configuration to behave the same way that I would do this with attributes:
public class Product
{
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public virtual Product Parent { get; set; }
}
Supposing that you want to create a self referencing entity, I assume that you have a Product class like this:
public class Product
{
public int Id { get; set; }
public int? ParentId { get; set; }
public virtual Product Parent { get; set; }
}
In the context, you need to implement the OnModelCreating method in order to configure the self reference.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().
HasOptional(e => e.Parent).
WithMany().
HasForeignKey(m => m.ParentId);
}

Categories

Resources