Save complexa data using entity framework - c#

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

Related

ServiceStack OrmLite mapping with references not working

I'm trying out OrmLite to see if I can replace Entity Framework in my projects. The speed is quite significant on simple queries. But I tried to map/reference a [1 to many- relation and read the documentation + examined the test code from the github page but without success. This is my example. Is there something I've forgot or should do to get it working like Entity Framework?
Example
// EF: returns +15.000 records + mapped > product.StockItems (slow)
dbContext.Products.Include(x => x.StockItems).ToList();
// OrmLite: returns +100.000 records (NO mapping > product.StockItems)
db.Select<Product>(db.From<Product>().Join<StockItem>());
// OrmLite: +15.000 separate requests to sql server (bad workarround + slow)
foreach (var product in db.Select<Product>())
{
// manual mapping
product.StockItems = db.Select<StockItem>(x => x.ProductId == product.Id);
}
Product.cs
public class Product
{
public int Id { get; set; }
public ProductType ProductType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int DisplayOrder { get; set; }
public bool LimitedToStores { get; set; }
public string Sku { get; set; }
public decimal Price { get; set; }
public decimal OldPrice { get; set; }
public decimal SpecialPrice { get; set; }
public decimal DiscountPercentage { get; set; }
public DateTime? DateChanged { get; set; }
public DateTime? DateCreated { get; set; }
//...
[Reference]
public virtual IList<StockItem> StockItems { get; set; } = new List<StockItem>();
}
StockItem.cs
public class StockItem
{
public int Id {get; set;}
[References(typeof(Product))]
public int ProductId { get; set; }
public string Size { get; set; }
public int TotalStockQuantity { get; set; }
public string Gtin { get; set; }
public int DisplayOrder { get; set; }
// ...
[Reference]
public virtual Product Product { get; set; }
}
Ideally your POCOs/DTOs shouldn't use interfaces and you don't need to use virtual as ORM only populates your own POCOs (i.e. it doesn't create proxies of your models like other Heavy ORMs), I also prefer to use [AutoIncrement] for integer Ids (unless you need to populate specific Ids) so my Models would look like:
public class Product
{
[AutoIncrement]
public int Id { get; set; }
public ProductType ProductType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int DisplayOrder { get; set; }
public bool LimitedToStores { get; set; }
public string Sku { get; set; }
public decimal Price { get; set; }
public decimal OldPrice { get; set; }
public decimal SpecialPrice { get; set; }
public decimal DiscountPercentage { get; set; }
public DateTime? DateChanged { get; set; }
public DateTime? DateCreated { get; set; }
[Reference]
public List<StockItem> StockItems { get; set; }
}
public class StockItem
{
[AutoIncrement]
public int Id { get; set; }
[References(typeof(Product))]
public int ProductId { get; set; }
public string Size { get; set; }
public int TotalStockQuantity { get; set; }
public string Gtin { get; set; }
public int DisplayOrder { get; set; }
}
OrmLite's POCO References only populate 1-level deep and it's not a good idea to have cyclical relationships as they're not serializable so I'd remove the back reference on StockItems as it's not going to be populated.
You also need to use LoadSelect in order to query and return POCOs with references, so to return Product with their StockItem references you can just do:
db.LoadSelect<Product>();
You can also populate this manually with 2 queries by using Merge extension method to merge 2 disconnected record sets, e.g:
var q = db.From<Product>().Join<StockItem>();
var products = db.Select(q.SelectDistinct());
var stockItems = db.Select<StockItem>();
products.Merge(stockItems);
Which will merge Products with their StockItems which you can quickly see by running:
products.PrintDump();

How to create a foreign relation to one Model from multiple other models using Entity framework?

I am trying to create my first app using ASP.NET MVC framework and Entity Framework 6.
I chose to use code first approach and I started by defining my Models.
I have a model called Client with an identity attribute called Id. I have multiple Models that has an attribute called ClientId. The ClientId attribute should have virtual link to the Clients Model.
Here is how my Client model looks like
[Table("clients")]
public class Client
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string status { get; set; }
public DateTime created_at { get; set; }
public DateTime? modified_at { get; set; }
public Client()
{
status = "Active";
created_at = DateTime.UtcNow;
}
}
Then here is how I am creating a belong to relation using other models.
[Table("BaseClientsToUsers")]
public class ClientToUser : ModelDefault
{
[ForeignKey("User")]
public int UserID { get; set; }
[ForeignKey("Client")]
public int ClientId { get; set; }
[ForeignKey("Team")]
public int DefaultTeamId { get; set; }
public DateTime? JoinedAt { get; set; }
public bool IsActive { get; set; }
public virtual User User { get; set; }
public virtual Client Client { get; set; }
public virtual Team Team { get; set; }
public ClientToUser()
{
DateTime UtcNow = DateTime.UtcNow;
IsActive = true;
CreatedAt = UtcNow;
LastUpdatedAt = UtcNow;
}
[Table("BaseTeams")]
public class Team : ModelDefault
{
[MaxLength(250)]
public string Name { get; set; }
[ForeignKey("Client")]
public int ClientId { get; set; }
public bool IsActive { get; set; }
public virtual Client Client { get; set; }
public Team()
{
DateTime UtcNow = DateTime.UtcNow;
IsActive = true;
CreatedAt = UtcNow;
LastUpdatedAt = UtcNow;
}
}
But, when I try to update my databases I get the following error
Introducing FOREIGN KEY constraint
'FK_dbo.BaseTeams_dbo.BaseClients_ClientId' on table 'BaseTeams' may
cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or
ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could
not create constraint or index. See previous errors.
I am not really sure what could be causing the error but it seems it is because I am creating multiple Foreign keys to the same `Clients model.
How can I fix this error?
Hello #Mike A When I started MVC I got this error too, so you need aditional tables that connects your DB items.
So try connect your database items with tables like that:
Here is my working example:
[Table("Products")]
public class Product
{
[Key]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal InternalPrice { get; set; }
public string Url { get; set; }
}
[Table("Categories")]
public class Category
{
[Key]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string Url { get; set; }
}
[Table("ProductCategories")]
public class ProductCategory
{
[Key]
[Column(Order = 0)]
public string ProductId { get; set; }
[Key]
[Column(Order = 1)]
public string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
So you can connect your items without problems hope this will help you.

How to gain access to table data through foreign key reference?

I have a statement in one of my entities which uses a foreign key to return an IEnumerable<CustomField>.
I have used LINQ in my repository to test the below method to see if it works and it does. But when I use the foreign key reference in the entity it returns null. Am I missing something here? How can I use a foreign key to gain access to the data in another entity.
Invoice entity:
[Table("vwinvoice")]
public class Invoice
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Sys_InvoiceID { get; set; }
[DisplayName("Inc.In Turnover")]
public bool Turnover { get; set; }
public int FK_StatusID { get; set; }
[DisplayName("Invoice No.")]
public string InvoiceNumber { get; set; }
[DisplayName("Invoice Date")]
public DateTime InvoiceDate { get; set; }
[DisplayName("Document Type")]
public string DocType { get; set; }
[DisplayName("Supplier Invoice No.")]
[Column("SupplierInvoiceNumber")]
public string SuppInvNumber { get; set; }
public int FK_SupplierID { get; set; }
[DisplayName("Account Number")]
public string AccountNumber { get; set; }
[DisplayName("Order Number")]
public string OrderNumber { get; set; }
[DisplayName("Order Date")]
public DateTime? OrderDate { get; set; }
[DisplayName("Currency Code_Doc")]
public string CurrencyCode_Doc { get; set; }
[DisplayName("Net Amount_Doc")]
public decimal? NetAmount_Doc { get; set; }
[DisplayName("VAT Amount_Doc")]
public decimal? VATAmount_Doc { get; set; }
[DisplayName("Gross Amount_Doc")]
[Required]
public decimal? GrossAmount_Doc { get; set; }
[DisplayName("Currency Code_Home")]
public string CurrencyCode_Home { get; set; }
[DisplayName("Net Amount_Home")]
public decimal? NetAmount_Home { get; set; }
[DisplayName("VAT Amount_Home")]
public decimal? VATAmount_Home { get; set; }
[DisplayName("Gross Amount_Home")]
public decimal? GrossAmount_Home { get; set; }
[DisplayName("Payment Reference")]
public string PaymentReference { get; set; }
[DisplayName("Supplier")]
public string AccountName { get; set; }
[DisplayName("Status")]
public string StatusName { get; set; }
[DisplayName("Auditor Comments")]
public string AuditorComments { get; set; }
[DisplayName("Reviewer Comments")]
public string ReviewerComments { get; set; }
[DisplayName("Data Source")]
[Required]
public string DataOrigin { get; set; }
public int DetailLineCount { get; set; }
public IEnumerable<CustomField> ClientData {
get {
//Use the CustomFields foreign key to gain access to the data returns null.
return GetCustomFieldData(this.CustomFields.Select(r => r));
}
}
private IEnumerable<CustomField> GetCustomFieldData(IEnumerable<Entities.CustomFields> enumerable) {
return (from f in enumerable
select new CustomField {
Name = f.FK_CustomHeader,
Value = f.Value
});
}
//Custom Field Additions
public virtual ICollection<CustomFields> CustomFields { get; set; }
}
CustomFields entity:
[Table("tblCustomFields")]
public class CustomFields
{
[Key]
public int ID { get; set; }
public int? FK_SysInvoiceID { get; set; }
[StringLength(255)]
public string FK_CustomHeader { get; set; }
[StringLength(255)]
public string Value { get; set; }
public virtual Invoice Invoices { get; set; }
public virtual CustomFieldHeaders CustomFieldHeaders { get; set; }
}
I also cannot place a breakpoint in the get statement to see what happens, why is this? It just skips over the breakpoint whenever I try to return a list of Invoices, which can be seen here:
public IQueryable<Invoice> Invoices
{
get
{
var x = _ctx.Invoices.ToList();
return _ctx.Invoices;
}
}
You are using the virtual keyword when declaring your CustomFields property. As such it will be lazy loaded. If you want the property to be populated once returned from the repository you will need to explicitly Include the table in your method:
var x = _ctx.Invoices.Include(i => i.CustomFields).ToList();
return _ctx.Invoices;
Or you can remove the virtual keyword and the property will always be populated, with the consequent performance hit of the database join and the extra data being returned whenever you access Invoices.

Only mapping to subset of a table

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?

Last inserted id with EF 4.1 , MVC 3

I'm using asp.net mvc 3 and EF 4.1 with visual studio 2010 and im new to it.
i have a problem about last inserted id. i have two table in my db "points_Order" and "points_OrderDetails".
db.Order.Add(po);
db.SaveChanges();
//second thing i have tried
var orderID = (from oi in db.Order
select oi.ID)
.Max(i => i);
// and this first thing
pod.OrderID = po.ID;
pod.OrderID = orderID;
db.OrderDetails.Add(pod);
db.SaveChanges();
context class:
public class points_Order {
[Key]
public int ID { get; set; }
public string Owner { get; set; }
public DateTime Date { get; set; }
public byte Status { get; set; }
public int Total { get; set; }
//public virtual points_OrderDetails OrderDetail { get; set; }
}
public class points_OrderDetails {
[Key]
public int OrderID { get; set; }
public string OrderType { get; set; }
public int Amount { get; set; }
public int ItemID { get; set; }
public int ItemRecord { get; set; }
public string ItemCode { get; set; }
public byte Plus { get; set; }
public byte Degree { get; set; }
public string Kind { get; set; }
public string Sex { get; set; }
public string Race { get; set; }
public string Tier { get; set; }
public string SetItem { get; set; }
public string SOX { get; set; }
//public Blues Blue { get; set; }
}
public DbSet<points_Order> Order { get; set; }
public DbSet<points_OrderDetails> OrderDetails { get; set; }
im getting the error "{"Cannot insert the value NULL into column 'OrderID', table 'db.dbo.points_OrderDetails'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}"
what can i do? am i doing something wrong?
If you define a relationship from order -> order details in your model, it should work like this:
po.Details.Add(pod); // or pod.Order = po;
db.Order.Add(po);
db.SaveChanges();
EF should figure out the relations for you, no need to set ID's manually.
UPDATE: you're trying to do something that is not supported. You have a one-to-one mapping; EF does not support it the way you intend to (shared primary key). See http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx for an explanation on how to model one-to-one in EF.

Categories

Resources