I'm trying to learn to use Entity Framework and am working through the Entity Framework Recipes 6 Book (not even going to try and hide that).
Working on 2-4:
3 tables:
Order: OrderID, OrderDate, (Nav Properties OrderItems)
OrderItem: OrderId, SKU, Count (Nav Properties, Item, Order)
Item: SKU, Description, Price (Nav Properties, OrderItems)
using (var context = new EFRecipesContext()) {
var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) };
var item = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
var oi = new OrderItem { Order = order, Item = item, Count = 1 };
item = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
oi = new OrderItem { Order = order, Item = item, Count = 3 };
item = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };
oi = new OrderItem { Order = order, Item = item, Count = 1 };
context.Orders.Add(order);
context.SaveChanges();
}
The only thing that gets added to the database is the order and nothing else, none of the items, etc... I had a problem with the previous example and had to add the "other" items individually but I thought that point was that you could just do the one "Add" and it would add all the objects to the Database?
Thanks in advance!
UPDATEOk I made the following changes based on the suggestions below and now it's working
using (var context = new EFRecipesContext()) {
var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) };
var item = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
var oi = new OrderItem { Order = order, Item = item, Count = 1 };
order.OrderItems.Add(oi); // suggestion from Stackoverflow.
item = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
oi = new OrderItem { Order = order, Item = item, Count = 3 };
order.OrderItems.Add(oi); // suggestion from Stackoverflow.
item = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };
oi = new OrderItem { Order = order, Item = item, Count = 1 };
order.OrderItems.Add(oi); // suggestion from Stackoverflow.
context.Orders.Add(order);
context.SaveChanges();
}
At least now I know what to change in their code going forward to get it working. Thanks!!
You never actually add the OrderItems to the DbContext collection.
There is also a problem with your code, which means you are overwriting your items and oi variable values multiple times.
The below code makes some assumptions, since you haven't supplied more of your code, but should be easy to adapt.
using (var context = new EFRecipesContext()) {
var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) };
context.Orders.Add(order);
var item1 = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
var oi1 = new OrderItem { Order = order, Item = item, Count = 1 };
context.OrderItems.Add(oi1);
var item2 = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
var oi2 = new OrderItem { Order = order, Item = item, Count = 3 };
context.OrderItems.Add(oi2);
var item3 = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };
var oi3 = new OrderItem { Order = order, Item = item, Count = 1 };
context.OrderItems.Add(oi3);
context.SaveChanges();
}
Unless you are using the variables again, you could do the definition of the objects inline like this aswell, which is slightly less repetitive:
context.OrderItems.Add(new OrderItem {
Order = order,
Item = new Item {
SKU = 1729,
Description = "Backpack",
Price = 29.97M
},
Count = 1
});
This is not really about many to many, but about the way how EF discovers related data.
You need to explicitely add the entry to the database, that is actually referencing the related data. In your case, this is oi, so if you add it
context.OrderItems.Add(oi);
context.SaveChanges();
the related io.Order and io.Item will be considered by EF. But EF has no way of knowing about this relation when you only provide order without providing navigation information from order to io.
Another way of solving the issue (instead of adding io explicitely) would be to update order.OrderItems before saving:
oi = new OrderItem { Order = order, Item = item, Count = 1 };
order.OrderItems.Add(oi);
context.Orders.Add(order);
context.SaveChanges();
You must add an entry to the database to be invoked on appropriate data. In this case, it's the OI.
Related
My first Stack Question so let me know if I need to revise anything.
Update
Thanks to Mithgroth's help, I was able to read up on group by and figure out how to put non bulk and bulk items on their own truck:
private IEnumerable<string> AddItems(IEnumerable<DispatchItemViewModel> items, string dispatchId, string userId, bool forRigWalk)
{
int Ordinal = 1;
var isBulk = from item in items
group item by item.Product.IsBulk == true into newGroup
select newGroup;
foreach (var bulk in isBulk){
var newTruck = new DispatchTruckViewModel
{
Ordinal = Ordinal++,
Items = bulk.ToList()
};
AddItems(new List<DispatchTruckViewModel> { newTruck }, dispatchId, userId, forRigWalk);
};
return bulk;
}
HOWEVER I still need each bulk product on their own truck. Here is the current outcome vs the desired outcome:
Current Outcome based on current code:
Desired Outcome:
Original Post
Our system takes in a list of products, then auto creates a truck dispatch to take those products to where they need to go. Some of these products are bulk and need their own truck. So I want to be able to iterate through the list and create a new truck for each bulk product that has only that one bulk product in it. Then I want all non bulk products to get put on a truck together.
The current result:
current list
Desired Outcome: desired outcome
My current code does create a new truck for each bulk product however I can not get it to disperse the products the correct way, it simple creates multiple trucks and add all the products to every truck
private IEnumerable<string> AddItems(IEnumerable<DispatchItemViewModel> items, string dispatchId, string userId, bool forRigWalk)
{
foreach (var bulk in items){
if(bulk.Product.IsBulk == true){
var item = bulk.Product;
var newTruck = new DispatchTruckViewModel{
Ordinal =+ 2,
Items = items.ToList()
};
AddItems(new List<DispatchTruckViewModel> { newTruck }, dispatchId, userId, forRigWalk);
}
};
var singleTruck = new DispatchTruckViewModel{
Ordinal = 1,
Items = items.ToList(),
};
return AddItems(new List<DispatchTruckViewModel>{singleTruck}, dispatchId, userId, forRigWalk);
}
To show my work, I also tried this here but I am running into a type issue:
error message: type issue
{
foreach (var bulk in items){
if(bulk.Product.IsBulk == true){
var item = bulk.Product;
var newTruck = new DispatchTruckViewModel{
Ordinal =+ 2,
Items = (ICollection<DispatchItemViewModel>)bulk
};
AddItems(new List<DispatchTruckViewModel> { newTruck }, dispatchId, userId, forRigWalk);
}
};
var singleTruck = new DispatchTruckViewModel{
Ordinal = 1,
Items = items.ToList(),
};
return AddItems(new List<DispatchTruckViewModel>{singleTruck}, dispatchId, userId, forRigWalk);
}
Here is where the add items function goes:
private IEnumerable<string> AddItems(IEnumerable<DispatchTruckViewModel> Trucks, string dispatchId, string userId, bool forRigWalk)
{
var retList = new List<string>();
foreach (var truck in Trucks)
{
var dTruck = new DispatchTruck{
Id = _idService.GetIdString(),
CreateUserId = userId,
DispatchId = dispatchId,
Ordinal = truck.Ordinal,
EstimatedArrival = truck.EstimatedArrival == null || truck.EstimatedArrival == "" ? null : (DateTime?)(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local)
.AddSeconds(Convert.ToInt64(truck.EstimatedArrival))),
TenantId = TenantId,
UpdateUserId = userId,
DriverName = truck.DriverName,
SwamperName = truck.SwamperName,
};
if (forRigWalk)
{
dTruck.Status = ServiceConstants.DELIVERED_STATUS;
}
_dispatchTruckRepo.Add(dTruck);
foreach (var item in truck.Items)
{
if (item.ProductId == "" && item.Product != null) {
item.ProductId = _productService.AddForInventory(item.Product, item.Product.Categorys.First().Id, userId);
retList.Add(item.ProductId);
}
var dItem = new DispatchItem{
Id = _idService.GetIdString(),
DispatchTruckId = dTruck.Id,
ExpenseId = item.ExpenseId == "" ? null : item.ExpenseId,
ProductId = item.ProductId == "" ? null : item.ProductId,
Quantity = item.Quantity,
Price = item.Price,
TenantId = TenantId,
};
_dispatchItemRepo.Add(dItem);
}
}
return retList;
}
i need to add an object to a database several times with a different Sku code.
I have the Sku code changing in the for each loop but i don't know how to add the products to the database in the same method without getting this error,
i think i might need to make it async but not sure how.
here is the code
public static PetersContext db = new PetersContext();
static void Main(string[] args)
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
Product p1 = new Product()
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 1,
SellPrice = 2,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
};
foreach (var size in sizeList)
{
p1.ProductSkus = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + size.ToString());
Console.WriteLine(p1.ProductSkus);
db.Products.Add(p1);
db.SaveChanges();
}
Console.ReadLine();
}
}
There are three things I would do differently in your approach.
You should create your context within a using statement.
Move the save changes function outside your for loop.
Create your p1 within the loop.
See the changes below:
using(var db = new PetersContext()) //open connection
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
foreach (var size in sizeList)
{
Product p1 = new Product() //Moving to inside of loop creates new instance every time
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 1,
SellPrice = 2,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
};
p1.ProductSkus = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + size.ToString());
Console.WriteLine(p1.ProductSkus);
db.Products.Add(p1);
}
db.SaveChanges(); //save changes after everything is done.
}
Console.ReadLine();
I think your issue is the fact your foreach loop is evaluating the query during the loop when you are trying to call Savechanges() which wants to generate another transaction.
If you just change your SizeList and Product queries to have .ToList() at the end, this will force evaluation and you will then be using lists in your foreach, not a transactional query.
Updated to reflect the comments:
Looking at your code it looks like the ProductSku is a property of Product - you should perhaps consider making SKU a separate table so that you don't need to repeat all the standard product properties. However, to give what I think you are asking for you need something like this;
static void Main(string[] args)
{
using (PetersContext db = new PetersContext()) {
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var products = from Product in db.Products
select Product;
foreach (var size in sizeList)
{
foreach (var product in products)
{
Product newProduct = new Product()
{
ProductSkus = (product.ProductCode + product.ProductBrand.ToString() + product.ProductColour.ToString() + size.ToString()),
ProductBrand = product.ProductBrand,
ProductCode = product.ProductCode,
CostPrice = product.CostPrice,
SellPrice = product.SellPrice,
ProductDescription = produce.ProductDescription,
ProductSeason = product.ProductSeason,
ProductType = product.ProductType
};
Console.WriteLine(p1.ProductSkus);
db.Products.Add(newProduct);
}
}
db.SaveChanges();
Console.ReadLine();
}
}
There are other changes you could make, but the the big one I'm seeing is there's only one product reference. That same product object is added to the product collection several times in a loop. Each time, the loop also sets a new Sku... but since they are all the same object, the references from prior iterations of the loop reflect the new data.
To fix this, you need a new product object each time through the loop. You can offset that performance by moving the db.SaveChanges() call to after the loop.
Thanks for yer help people,
I created a new object in the for each, changed the sku in the new object, i forgot i need to give the products a size so i did that in a linq query in the same loop and added it to a product list, the product was then looped through in another for each and added to the db, it could do with a refactoring but it will do for now, thanks again guys
public static PetersContext db = new PetersContext();
static void Main(string[] args)
{
var sizeList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1//product.SizeScale.SizeScaleId
select (ProductSizes.SizeDesc);
var sizeIdList = from ProductSizes in db.Sizes
where ProductSizes.SizeScale == 1
select (ProductSizes.SizeId);
var products = from Product in db.Products
select Product;
Product p1 = new Product()
{
ProductBrand = 1,
ProductCode = "Ts102",
CostPrice = 27,
SellPrice = 79,
ProductDescription = "Ted Smith Shirt",
ProductSeason = 1,
ProductType = 1,
ProductColour=1
};
IList<Product> newProductList = new List<Product>();
foreach (var size in sizeList)
{
string newSku = (p1.ProductCode + p1.ProductBrand.ToString() + p1.ProductColour.ToString() + (size.ToString()));
Product newProduct = new Product()
{
ProductBrand = p1.ProductBrand,
ProductCode = p1.ProductCode,
CostPrice = p1.CostPrice,
SellPrice = p1.SellPrice,
ProductDescription = p1.ProductDescription,
ProductSeason = p1.ProductSeason,
ProductType = p1.ProductType,
ProductColour = p1.ProductColour,
ProductSkus= newSku,
};
newProduct.ProductSkus = newSku;
var SizeId =(from ProductSize in db.Sizes
where ProductSize.SizeDesc == size
select ProductSize.SizeId).First();
newProduct.ProductSize = SizeId;
newProductList.Add(newProduct);
}
foreach (var product in newProductList)
{
db.Products.Add(product);
db.SaveChanges();
}
Console.ReadLine();
I'm a student, new to LINQ and we've been given an assignment to deal with LINQ queries.
My problem is I've been struggling the past days to figure out the correct way to perform this step: print the customers name that has "Milk" inside their orders.
Write a LINQ query to select all customers buying milk.
Print the Name of each customer in the query.
For the sake of time, here is the structure of the data so that you can understand it:
Product milk = new Product { Name = "Milk", Price = 13.02m };
Product butter = new Product { Name = "Butter", Price = 8.23m };
Product bread = new Product { Name = "Bread", Price = 17.91m };
Product cacao = new Product { Name = "Cacao", Price = 25.07m };
Product juice = new Product { Name = "Juice", Price = 17.03m };
Customer c1 = new Customer { Name = "x", City = "g", Orders = new Order[] {
new Order { Quantity = 2, Product = milk },
new Order { Quantity = 1, Product = butter },
new Order { Quantity = 1, Product = bread }
}
};
Customer c2 = new Customer { Name = "y", City = "g", Orders = new Order[] {
new Order { Quantity = 1, Product = cacao },
new Order { Quantity = 1, Product = bread },
new Order { Quantity = 2, Product = milk },
new Order { Quantity = 2, Product = butter },
}
};
Customer c3 = new Customer { Name = "z", City = "g", Orders = new Order[] {
new Order { Quantity = 3, Product = juice }
}
};
Customer[] customers = new Customer[] { c1, c2, c3 };
As an example of the syntax I'm using with LINQ here is a reference of working code:
var QueryCustomerByCity = from cus in customers.AsEnumerable()
where cus.City == "g"
select cus;
foreach (Customer c in QueryCustomerByCity)
Console.WriteLine("Customer {0} lives in {1}", c.Name, c.City);
I'm really trying hard to understand what's happening, so if you can help me please explain me how you reached such conclusion :)
Thank you a lot for your time!
Your current query of:
var QueryCustomerByCity = from cus in customers.AsEnumerable() //for each customer in customers
where cus.City == "g" // where the Customer is from the City "g"
select cus; // select the customer
reads as "for each customer represented as cus in the customers array where the customers City is "g" then retain the customer". So, as a result you'll have a sequence of customers where their City is "g".
As for your task, you're after is this:
var result = from cus in customers // for each customer in customers
where cus.Orders.Any(o => o.Product.Name == "Milk") // where the product name is "Milk"
select cus; // select the customer
This essentially goes over the customers in the customers array and checks their orders and if there's any product which has the name "Milk" retains that specific customer.
A LINQ query is a pipeline: Data flows in a stream, element by element, from one clause to the next. FROM some data source you can get a flow of elements (e.g., via AsEnumerable). From a flow of elements you can filter on some predicate (function on an element returning a boolean), keeping in the flow only those elements that the predicate accepts (returns true for) and throwing away the others. The filter is called WHERE and the predicate is the rest of the WHERE clause, e.g., WHERE cus.City == "G". And also, from elements in the flow, you can select a single field, changing the flow from a flow of the (original) element to a flow of the field. This is a SELECT, as in SELECT g.City where the flow would change from a stream of customers to a stream of strings (each of which was a city name).
From this (and the other LINQ clauses you can learn) you can construct whatever data pipeline you wish. Specifically, one that returns a stream of customer names given that the customer has ordered milk.
I am working to create a sales order with a single product added to the sales order detail and attach that to the sales order.
It is throwing me an error and I am wondering if there is a proper way to performing this action?
Thanks!
public void Create(CrmContextCore _crmContext, Guid productId, UserEntityModel currentuser)
{
var detail = new Entity("salesorderdetail");
{
detail["productid"] = new EntityReference("product", productId);
}
var salesorder = new Entity("salesorder");
{
salesorder["accountid"] = new EntityReference("account", currentuser.AccountId);
salesorder["contactid"] = new EntityReference("contact", currentuser.ContactId );
salesorder["emailaddress"] = currentuser.Email;
salesorder["name"] = "DealerPO123";
salesorder["salesorderdetail"] = detail;
}
_crmContext.ServiceContext.AddObject(salesorder);
_crmContext.ServiceContext.SaveChanges();
}
Sample: Set negative prices in opportunities, quotes, and sales orders.
// Create the sales order.
SalesOrder order = new SalesOrder()
{
Name = "Faux Order",
DateFulfilled = new DateTime(2010, 8, 1),
PriceLevelId = new EntityReference(PriceLevel.EntityLogicalName,
_priceListId),
CustomerId = new EntityReference(Account.EntityLogicalName,
_accountId),
FreightAmount = new Money(20.0M)
};
_orderId = _serviceProxy.Create(order);
order.Id = _orderId;
// Add the product to the order with the price overriden with a
// negative value.
SalesOrderDetail orderDetail = new SalesOrderDetail()
{
ProductId = new EntityReference(Product.EntityLogicalName,
_product1Id),
Quantity = 4,
SalesOrderId = order.ToEntityReference(),
IsPriceOverridden = true,
PricePerUnit = new Money(-40.0M),
UoMId = new EntityReference(UoM.EntityLogicalName,
_defaultUnitId)
};
_orderDetailId = _serviceProxy.Create(orderDetail);
I have a list of Objects and one of the item is another list. How can I Group them based on the inner list.
Here is an example of what I wish to do.
class Student
{
public string Name;
public int Age;
public List<GroupInfo> GroupList; // This is the inner list
}
class GroupInfo
{
public string GroupName;
public int GroupId;
}
static void Main()
{
GroupInfo firstGroup = new GroupInfo
{
GroupId = 1,
GroupName = "First group"
};
GroupInfo secondGroup = new GroupInfo
{
GroupId = 2,
GroupName = "Second group"
};
GroupInfo thirdGroup = new GroupInfo
{
GroupId = 3,
GroupName = "Third group"
};
GroupInfo fourthGroup = new GroupInfo
{
GroupId = 4,
GroupName = "Fourth group"
};
List<Student> studentList = new List<Student>();
Student firstStudent = new Student();
firstStudent.Name = "Name1";
firstStudent.Age = 15;
firstStudent.GroupList = new List<GroupInfo>();
firstStudent.GroupList.Add(firstGroup);
firstStudent.GroupList.Add(secondGroup);
studentList.Add(firstStudent);
Student secondStudent = new Student();
secondStudent.Name = "Name2";
secondStudent.Age = 17;
secondStudent.GroupList = new List<GroupInfo>();
secondStudent.GroupList.Add(firstGroup);
secondStudent.GroupList.Add(thirdGroup);
studentList.Add(secondStudent);
Student thirdStudent = new Student();
thirdStudent.Name = "Name3";
thirdStudent.Age = 18;
thirdStudent.GroupList = new List<GroupInfo>();
thirdStudent.GroupList.Add(secondGroup);
thirdStudent.GroupList.Add(thirdGroup);
thirdStudent.GroupList.Add(fourthGroup);
studentList.Add(thirdStudent);
List<GroupInfo> groupInfoList = new List<GroupInfo>();
// Now What I want is to get a group List Where...
foreach (var student in studentList)
{
// ...First Group Should contains firstStudent and secondStudent
// Second group Should firstStudent & thirdStudent
// Third group Should contains secondStudent & thirdStuden
// Fourth Group Should contains only thirdStudent
}
}
One way is to iterate on the whole List and populate the GroupInfo List. Just wondering is there any other way to do this task.
You can do this with SelectMany like this:-
var result = studentList.SelectMany(x => x.GroupList,
(studentObj, groups) => new { studentObj, groups })
.GroupBy(x => new { x.groups.GroupId, x.groups.GroupName })
.Select(x => new
{
GroupId = x.Key.GroupId,
GroupName = x.Key.GroupName,
Students = x.Select(z => z.studentObj).ToList()
});
Since your GroupInfo class only has two properties i.e. GroupId & GroupName, you won't be able to fetch the Students associated with it. This is the reason I am fetching anonymous type out of it.
I am getting following output with this query:-