I have two model classes Category and Recipe and their relationship in one to many. I want to Edit the Recipe and also change the category that the recipe belongs to.Thanks in advance.
public class CookContext : DbContext
{
public CookContext(): base("cookContext")
{
}
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Recipe> Recipes { get; set; }
}
public class Recipe
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Ingridients { get; set; }
public string Image { get; set; }
public Category category { get; set; }
}
[HttpPost]
[ValidateInput(false)]
public ActionResult EditRecipe(Recipe recipe, int? categoryName)
{
var category = context.Categories.Where(c => c.Id ==
(int)categoryName).FirstOrDefault();
context.Entry(recipe).State = EntityState.Modified;
recipe.category = category;
context.SaveChanges();
}
The error messages i get are:
1.
[DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types.
2.
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
Try by adding the field CategoryId to your Recipe class, also, the category property should begin with a capital "C".
The collection property should be marked virtual if you want the data to be lazy loaded (only loaded when needed) otherwise, you may load all recipes for a given category every time you make a query:
public class Recipe
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Ingridients { get; set; }
public string Image { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
If it doesn't work try by setting up the ForeignKey attribute:
public class Recipe
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Ingredients { get; set; }
public string Image { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
Related
I'm trying to add a record in a table, but it's not working. The error message tells me that I cannot insert an explicit value in identity column for ProductCategories, but I don't know that I'm doing that. Maybe there is something I'm not getting about entity navigation, or my models are somehow not correctly linked?
SqlException: Cannot insert explicit value for identity column in table 'ProductCategories' when IDENTITY_INSERT is set to OFF.
Cannot insert explicit value for identity column in table 'Products' when IDENTITY_INSERT is set to OFF.
System.Data.SqlClient.SqlCommand+<>c.b__108_0(Task result)
DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch+d__32.MoveNext()
This is the code that fails (at await _context.SaveChangesAsync();):
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> FrontPageProduct(ViewModelFrontPageProduct frontPageProduct)
{
var fpp = new FrontPageProduct()
{
ProductCategoryId = frontPageProduct.ProductCategoryId,
ProductId = frontPageProduct.ProductId,
SortOrder = 0
};
_context.Add(fpp);
await _context.SaveChangesAsync();
return View("Index", new { id = fpp.ProductCategoryId, tab = 2 });
}
These are the involved entity models:
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public List<FrontPageProduct> InFrontPages { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
public class ProductCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; }
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public List<ProductInCategory> ProductInCategory { get; set; }
}
public class FrontPageProduct
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
[ForeignKey("ProductCategoryId")]
public virtual ProductCategory ProductCategory { get; set; }
}
The debug inspection of the fpp-object shows that the values for ProductCategoryId and ProductId are correct:
Where is/are my mistake/s?
EDIT I added the suggested [Key] and [DatabaseGenerated(DatabaseGeneratedOption.Identity)] to all the models, but I still get the same error.
I think that the Id field is always set to be primary key as default in EF anyway.
EDIT 2 I tried adding [FromBody] in my controller method, but that only resulted in a blank screen, no error message, and no changes being made to the database.
EDIT 3 I added [ForeignKey("ProductId")] and [ForeignKey("ProductCategoryId")] to the FrontPageProduct model, but still get the same SqlException.
EDIT 4 These are the foreign keys of my four tables:
FrontPageProducts:
FK_FrontPageProducts_ProductCategories_ProductCategoryId
FK_FrontPageProducts_Products_ProductId
ProductCategories:
FK_ProductCategories_ProductCategories_ParentId
Products:
none
ProductsInCategories:
FK_ProductsInCategories_FrontPageProducts_FrontPageProductId
FK_ProductsInCategories_ProductCategories_ProductCategoryId
FK_ProductsInCategories_Products_ProductId
Looks like you don't need to set DatabaseGenerated attribute for "int Id" fields, it is a convention and assumed that it is Identity Column.
Btw, are you sure you are getting the same error? The imgur link does not work for me. Do you really need Product and ProductCategory on your FrontPage class, if you can easily comment them out and test, please do so, if not you can try setting the values for them like below.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> FrontPageProduct(ViewModelFrontPageProduct frontPageProduct)
{
var fpp = new FrontPageProduct()
{
ProductCategoryId = frontPageProduct.ProductCategoryId,
ProductId = frontPageProduct.ProductId,
Product = _context.Set<Product>().Find(frontPageProduct.ProductId),
ProductCategory = _context.Set<ProductCategory>().Find(frontPageProduct.ProductCategoryId),
SortOrder = 0
};
_context.Add(fpp);
await _context.SaveChangesAsync();
return View("Index", new { id = fpp.ProductCategoryId, tab = 2 });
}
Apply Key and DatabaseGenerated attributes to every Id column on your models, so EF knows that it is an identity column and it needs to get back the generated id value.
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
[InverseProperty("Product")]
public IEnumerable<FrontPageProduct> InFrontPages { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
public class ProductCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; }
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public List<ProductInCategory> ProductInCategory { get; set; }
}
public class FrontPageProduct
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
[ForeignKey("ProductCategoryId ")]
public virtual ProductCategory ProductCategory { get; set; }
}
Edit: you should add navigation properties, just like I added above. Making them virtual also helps with lazy loading.
The Id field should be the primary key in the database so you can specify that in the entity models. - Add the [Key]
public class Product
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public IEnumerable<FrontPageProduct> InFrontPages { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
I have Country, City, Region and "Account Address" tables.
I want to create foreign key columns in "Account Address" pointing to Country, City, Region tables.
I have this code but it throws an error on creating database
The property \u0027Account_Id\u0027 cannot be configured as a
navigation property. The property must be a valid entity type and the
property should have a non-abstract getter and setter. For collection
properties the type must implement
After New Edit
public class Cities
{
[Key]
public int City_Id { get; set; }
public string City_name { get; set; }
public int Country_Id { get; set; }
[ForeignKey("Country_Id")]
public Countries countries { get; set; }
}
public class Region
{
[Key]
public int Region_Id { get; set; }
public string Region_name { get; set; }
public int City_Id { get; set; }
[ForeignKey("City_Id")]
public Countries countries { get; set; }
}
public class Accounts
{
[Key]
public int Account_Id { get; set; }
public string Fullname { get; set; }
public string Email { get; set; }
public string password { get; set; }
public int Cell_phone { get; set; }
public string Role { get; set; }
public int? estate_office_Id { get; set; }
[ForeignKey("estate_office_Id")]
public Estate_office estate_office { get; set; }
public List<Ads> ads { get; set; }
}
public class Account_address
{
[Key]
public int Id { get; set; }
[ForeignKey("Account_Id"), Column(Order = 0)]
public int Account_Id { get; set; }
[ForeignKey("Country_Id"), Column(Order = 1)]
public int Country_Id { get; set; }
[ForeignKey("City_Id"), Column(Order = 2)]
public int City_Id { get; set; }
[ForeignKey("Region_Id"), Column(Order = 3)]
public int Region_Id { get; set; }
public Accounts accounts { get; set; }
public Countries countries { get; set; }
public Cities cities { get; set; }
public Region region { get; set; }
}
You need to define public properties as shown below on the Account_address class.Then only EF will know how to map those navigation properties correctly.
public class Account_address
{
......
......
public Accounts accounts { get; set; } //like this
public Countries countries { get; set; } //like this
public Cities cities { get; set; } //like this
public Region region { get; set; } //like this
}
Update :
Hence you're not using singular naming convention for the classes,you have encountered this issue.Either you have to change the name of classes as singular or need to change the navigational property names a shown below.You have to do this for all the places.Here I have shown only for the Accounts class related navigational property.
[ForeignKey("Accounts_Id"), Column(Order = 0)]
public int Accounts_Id { get; set; }
My Advice is to follow the basic naming conventions.Then you can avoid lot of above kind of weird errors.
Hi every one I want to save complex data using Entity Framework and C#. I have 2 classes Product and Order defined as follows
Product Class
public class Product
{
[Key]
public int Id { get; set; }
public string SKU_Code { get; set; }
public string Product_Name { get; set; }
public string Quantity { get; set; }
public string Price { get; set; }
public string Image { get; set; }
public DateTime Created_Date { get; set; }
public DateTime Modified_Date { get; set; }
}
Order Class
public class Order
{
[Key]
public long ID { get; set; }
public string Order_Id { get; set; }
public string Payment_Type { get; set; }
public string Customer_Name { get; set; }
public string Shipping_Address { get; set; }
public DateTime Order_Date { get; set; }
public DateTime Modified_Date { get; set; }
public bool Flag { get; set; }
public List<Product> ProductDetails { get; set; }
}
And I want to save data Order details and my piece of code is as follows.
public Order Add(Order odrerDetails)
{
using (var context = new EcommerceDBContext())
{
var MyOrder_Id = Helper.Random(7); //Generate random orderID from my class
foreach (var detail in odrerDetails.ProductDetails)
{
odrerDetails.Order_Id = MyOrder_Id;
odrerDetails.Quantity = Convert.ToInt32(detail.Quantity);
odrerDetails.Amount = Convert.ToDouble(detail.Price);
//Other Details
context.objOrderListing.Add(odrerDetails);
}
context.SaveChanges();
return odrerDetails;
}
}
This gives me perfect data but when it comes to context.SaveChanges(); it return's me error.
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types.
To me you domain model seems all wrong. The order should just be used for grouping, its a typical e-commerce scenario.
When you get a receipt of your purchases, you get one receipt with every Item and price listed next to it. Its considered as one order of multiple things, not multiple orders of multiple things.
Reading your last comment, you cant have multiple orders with the same order id. Try to understand the domain first before trying to solve it with code. Also,you have no notion of a Customer with an Order.
public class Product
{
[Key]
public int Id { get; set; }
public string SKU_Code { get; set; }
public string Product_Name { get; set; }
public string Price { get; set; }
public string Image { get; set; }
public DateTime Created_Date { get; set; }
public DateTime Modified_Date { get; set; }
}
public class Order
{
[Key]
public long ID { get; set; }
public string Order_Id { get; set; }
public string Payment_Type { get; set; }
public string Customer_Name { get; set; }
public string Shipping_Address { get; set; }
public DateTime Order_Date { get; set; }
public DateTime Modified_Date { get; set; }
public bool Flag { get; set; }
public List<OrderLineItem> Items { get; set; }
}
public class OrderLineItem
{
[Key]
public long ID { get; set; }
public long Order_Id { get; set; }
public long Product_Id {get; set;}
public int Quantity {get; set;}
}
I'm working with code first for a ecommerce project,
I have 2 classes: Category and Products
Relationship is one to many, Category has many products,
I want to make the foreign key as required (not null) so, if I add a product, I must enter the categoryid.
When I do so, I get this error:
Introducing FOREIGN KEY constraint 'FK_dbo.Products_dbo.Categories_categoryId' on table 'Products' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Any ideas?
public class Category :IObjectWithState
{
[Key]
public int CategoryId { get; set; }
[Required]
public string Discription { get; set; }
public string Notes { get; set; }
public int ParentCategoryId { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Discount> Discounts { get; set; }
public virtual ICollection<ProductList> ProductLists { get; set; }
[NotMapped]
public State state { get; set; }
}
public class Product :IObjectWithState
{
[Key]
public int ProductId { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
public string ShortDiscription { get; set; }
public string LongDiscription { get; set; }
[Required]
public bool Active { get; set; }
[Required]
public int categoryId { get; set; }
[ForeignKey("categoryId")]
public Category Category { get; set; }
public ICollection<ProductImage> ProductImage { get; set; }
public ICollection<Discount> Discount { get; set; }
public ICollection<Discussion> Duscussion { get; set; }
public ICollection<ProductAttributeValue> ProductAttributeValue { get; set; }
public ICollection<ProductListItem> ProductListItem { get; set; }
public ICollection<ProductSKU> ProductSKU { get; set; }
public ICollection<RelatedProduct> RelatedProduct { get; set; }
public ICollection<Review> Review { get; set; }
public ICollection<ShoppingCart> ShoppingCart { get; set; }
}
i set cascadeDelete: to false in the Migration class
Weird question here.
I have a bad situation with a database I can't change.
[Table("PROJTABLE")]
public class Certifikat {
[Key]
public long Recid { get; set; }
public String DATAAREAID { get; set; }
public String Projid { get; set; }
public virtual StandardAndScope StandardInfo { get; set; }
}
[Table("DS_CRT_PROJSTANDARDSCOPE")]
public class StandardAndScope {
[Key]
public long RECID { get; set; }
public String DATAAREAID { get; set; }
public String Standard { get; set; }
public String Scope { get; set; }
}
I have an optional one-to-many relationship from Certifikat to StandardAndScope. However! It's only one-to-many if the DATAAREAID column is a certain value ("crt").
Do I have any way of telling EntityFramework only to map rows where the value of that column is crt?