Retrieving the value in an association table in Entity Framework code first - entity-framework

I am using EF 4.1 code first and I am struggling with the association entity and getting the value that was set in the association table. I tried to follow the post on: Create code first, many to many, with additional fields in association table.
My tables are as follows (all are in plural form):
Table: Products
Id int
Name varchar(50)
Table: Specifications
Id int
Name varchar(50)
Table: ProductSpecifications
ProductId int
SpecificationId int
SpecificationValue varchar(50)
My related classes:
public class Product : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductSpecification> ProductSpecifications { get; set; }
}
public class Specification : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductSpecification> ProductSpecifications { get; set; }
}
public class ProductSpecification
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int SpecificationId { get; set; }
public virtual Specification Specification { get; set; }
public string SpecificationValue { get; set; }
}
My context class:
public class MyContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Specification> Specifications { get; set; }
public DbSet<ProductSpecification> ProductSpecifications { get; set; }
protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
{
}
}
My repository method where I do my call (not sure if it is correct):
public class ProductRepository : IProductRepository
{
MyContext db = new MyContext();
public Product GetById(int id)
{
var product = db.Products
.Where(x => x.Id == id)
.Select(p => new
{
Product = p,
Specifications = p.ProductSpecifications.Select(s => s.Specification)
})
.SingleOrDefault();
return null; // It returns null because I don't know how to return a Product object?
}
}
Here is the error that I am getting back:
One or more validation errors were detected during model generation:
System.Data.Edm.EdmEntityType: : EntityType 'ProductSpecification' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ProductSpecifications� is based on type �ProductSpecification� that has no keys defined.
What does it mean that no keys are defined? Won't the ProductId and SpecificationId map to Id of Product and Id of Specification respectively?
How would I return a single product with the all the specifications for it?

Entity Framework will recognize that ProductId is a foreign key property for the Product navigation property and SpecificationId is a foreign key property for the Specification navigation property. But the exception complains about a missing primary key ("Key" = "Primary Key") on your ProductSpecification entity. Every entity needs a key property defined. This can happen either by conventions - by a specific naming of the key property - or explicity with data annotations or Fluent API.
Your ProductSpecification class doesn't have a property which EF would recognize as a key by convention: No Id property and no ProductSpecificationId (class name + "Id").
So you must define it explicitely. Defining it with data annotations is shown in the post you linked:
public class ProductSpecification
{
[Key, Column(Order = 0)]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
[Key, Column(Order = 1)]
public int SpecificationId { get; set; }
public virtual Specification Specification { get; set; }
public string SpecificationValue { get; set; }
}
And in Fluent API it would be:
modelBuilder.Entity<ProductSpecification>()
.HasKey(ps => new { ps.ProductId, ps.SpecificationId });
Both ways define a composite key and each of the parts is a foreign key to the Product or Specification table at the same time. (You don't need to define the FK properties explicitely because EF recognizes it due to their convention-friendly names.)
You can return a product including all specifications with eager loading for example:
var product = db.Products
.Include(p => p.ProductSpecifications.Select(ps => ps.Specification))
.SingleOrDefault(x => x.Id == id);

Related

How to create a self referencing table using code first?

I have an entity that has a reference to itself in a parent - child relationship. I need to find out how to implement this using code first and fluent API. Below is my entity class.
public class MenuItem
{
public int Id { get; set; }
public string LinkText { get; set; }
public string ControllerName { get; set; }
public string ActionName { get; set; }
public MenuItem Parent { get; set; }
public int ParentId { get; set; }
private IList<Role> Roles;
private IList<MenuItem> ChildMenuItems;
public MenuItem()
{
Roles = new List<Role>();
ChildMenuItems = new List<MenuItem>();
}
}
I tried using the below code in my entity configuration.
HasOptional(m => m.Parent)
.WithMany(m => m.ChildMenuItems)
.HasForeignKey(m => m.ParentId)
.WillCascadeOnDelete(false);
but I got this error -
One or more validation errors were detected during model generation:
Vantage.Data.EF.MenuItem_Parent: : Multiplicity conflicts with the
referential constraint in Role 'MenuItem_Parent_Target' in
relationship 'MenuItem_Parent'. Because all of the properties in the
Dependent Role are non-nullable, multiplicity of the Principal Role
must be '1'.
All help appreciated.
Thank You.
ParentId field should be nullable. You are not able to create any record if parentid is required.
Just change public int ParentId { get; set; }
to public int? ParentId { get; set; }

Entity Framework 1:0 or 1 Relationship

I have the following model:
public class User
{
public int Id { get; set; }
public Customer Customer { get; set; }
...
}
public class Customer
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
...
}
What I want to have is the Customer has to have an User, but can only have one, and the User does not have to have a Customer.
I would like to do it with the Fluent API, but I can't manage to get it to work so that both Customer and User have their Id properties be Identity Fields.
When you are configuring an one-to-one relationship, Entity Framework requires that the primary key of the dependent also be the foreign key, in your case it would be:
public class User
{
public int Id { get; set; }
public virtual Customer Customer { get; set; }
...
}
public class Customer
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public virtual User User { get; set; }
...
}
But you want each entities with its own PK, so, EF lets you do that but you should delete the UserId property from Customer entity, because, as I said before, in this kind of relationship the FK must be PK too. To configure properly your relationship use the Required data annotation as #Claies recommend you in his comment:
public class User
{
public int Id { get; set; }
public virtual Customer Customer { get; set; }
...
}
public class Customer
{
[Key]
public int Id { get; set; }
[Required]
public virtual User User { get; set; }
...
}
Or you can use Fluent Api, the configuration would be:
modelbuilder.Entity<Customer>().HasRequired(c=>c.User).WithOptional(u=>u.Customer);
Another thing, I recommend you define the navigation properties as virtual. This way, when you consult those properties the first time, they will be lazy loaded. Check this post for more info.
Update 1:
When the key property is an integer, Code First defaults to
DatabaseGeneratedOption.Identity. If you want, you can configure explicitly what you need using the [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)] attributes on the Customer Id.
public class Customer
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
Or you can use Fluent Api:
modelbuilder.Entity<Customer>().HasKey(t => t.Id)
.Property(t => t.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Update 2:
I don't understand why is throwing you that exception. I just tested with both variants (Data Annotations and Fluent Api) and everything works well. This is the code generated by Migrations:
public partial class changeCustomerIdToIdentity : DbMigration
{
public override void Up()
{
DropIndex("dbo.Customers", new[] { "Id" });
DropPrimaryKey("dbo.Customers");
AlterColumn("dbo.Customers", "Id", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.Customers", "Id");
CreateIndex("dbo.Customers", "Id");
}
public override void Down()
{
DropIndex("dbo.Customers", new[] { "Id" });
DropPrimaryKey("dbo.Customers");
AlterColumn("dbo.Customers", "Id", c => c.Int(nullable: false));
AddPrimaryKey("dbo.Customers", "Id");
CreateIndex("dbo.Customers", "Id");
}
}
I'm afraid your error is happened due to your DB schema. The Id on your Customers table must be FK too. The error means that you have some relation between your entities where foreign key property in dependent entity is defined as store generated, and that is because you are trying change the Id of your Customer entity as Identity, which is FK in your DB.

Entity Framework one-to-one relationship - Unable to determine the principal

I have 2 models:
public class TransactionHistory : IDbEntity {
[Key]
public int ID { get; set; }
public ItemHistory ItemHistory { get; set; }
}
public class ItemHistory : IDbEntity {
[Key]
public int ID { get; set; }
public int TransactionHistoryID { get; set; }
public TransactionHistory TransactionHistory { get; set; }
}
There's a one to one relationship between TransactionHistory and ItemHistory, ItemHistory MUST have a TransactionHistory but TransactionHistory may or may not have an ItemHistory.
I want to be able to do this in code:
var test = db.ItemHistory.Include(x => x.TransactionHistory).ToList();
As well as:
var test2 = db.TransactionHistory.Include(x => x.ItemHistory).ToList();
But I only want a single FK on the ItemHistory table.
With the code I've listed I get this error:
Unable to determine the principal end of an association between the types 'InventoryLibrary.DomainModels.TransactionHistory' and 'InventoryLibrary.DomainModels.ItemHistory'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
How is this achieved in Entity Framework code first data annotations?
Firstly, you have to mark foreign keys by virtual keyword to enable overrides.
public class TransactionHistory : IDbEntity
{
[Key]
public int ID { get; set; }
public virtual ItemHistory ItemHistory { get; set; }
}
public class ItemHistory : IDbEntity
{
[Key]
public int ID { get; set; }
public int TransactionHistoryID { get; set; }
[Required]
public virtual TransactionHistory TransactionHistory { get; set; }
}
If HistoryItem must have Transaction History, add DataAnnotation [Required], which makes it non-nullable.
Finally, wonder, if you want to have one-to-one relationship. I imagine you'd like to have many transaction history entries. Am I right? If not - let me know.
To create one-to-many relationship, use IEnumerable<> type.

Entity Framework CF Fluent API mapping

I have 2 entities:
public class User
{
public int userId { get; set; }
public string name { get; set; }
public Guid userGuid { get; set; }
}
public class Absence
{
public int absenceId { get; set; }
public Guid applicantId { get; set; }
public User applicant { get; set; }
public Guid permitterId{ get; set; }
public User permitter{ get; set; }
...
}
AbsencesConfiguration:
this.HasRequired(u => u.Applicant).WithMany().HasForeignKey(d => d.ApplicantId);
this.HasRequired(u => u.Permitter).WithMany().HasForeignKey(d => d.PermitterId);
I'd like a Fluent API mapping between the two classes, but it gives this error message:
Blockquote\tSystem.Data.Entity.Edm.EdmAssociationConstraint: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicantId' on entity 'Absences' does not match the type of property 'UserId' on entity 'User' in the referential constraint 'Absences_Applicant'.
I think this is because EF try to join the two tables with the UserId of the User entity and not with UserGuid column. I thought I would make these two columns of Absence entity unique, but how I can map them together?
Thanks in advance.
The problem is your User primary key is an int, but your foreign key is a Guid.
You need to alter either your User class to have a guid for userId:
public Guid userId { get; set; }
or, update your Absence class to use an int:
public int applicantId { get; set; }

Self referencing / parent-child relationship in Entity Framework

I read quite a number of posts of programmers that run into the Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values -exception when using a self-referencing relationship in Entity Framework.
I am trying to get a parent-child relationship to work:
public class Category {
public int CategoryId { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
public Category Parent { get; set; }
public List<Category> Children { get; set; }
}
This is the configuration I use (Fluent API):
Property(c => c.ParentId).IsOptional();
HasMany(c => c.Children).WithOptional(c => c.Parent).HasForeignKey(c => c.ParentId);
//HasOptional(c => c.Parent).WithMany(c => c.Children).HasForeignKey(c => c.ParentId);
Both the HasMany() and HasOptional() configurations result in a "Unable to determine a valid ordering for dependent operations..." exception when I try to save a new category like this:
context.Categories.Add(new Category { Name = "test" });
I don't understand why EF doesn't insert the Category with a null parentId. The database allows the ParentId foreign key to be null.
Would you be able to tell me how to do this?
You must define the ParentId in the category class as nullable to use it as the foreign key property for an optional relationship:
public int? ParentId { get; set; }
An int property cannot take the value null and therefore cannot represent a NULL as value in a database column.
Since someone asked in a comment about doing this with attributes. You can also utilize data annotations to set this up. Using the same example as above:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Category {
// You can also add [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
// as an attribute, if this field is to be generated by the database
[Key] // Define this as the primary key for the table
public int CategoryId { get; set; }
public string Name { get; set; }
[ForeignKey(nameof(Parent))] // Link the Parent object to the ParentId Foreign Key
public int? ParentId { get; set; }
public Category Parent { get; set; }
public List<Category> Children { get; set; }
}
This is tested and works in EF 6.