c# How to add data into existing collection item - c#

I am trying to add data to an existing List item. Currently I have two different collections. This is a subset of what I am working with and what I am trying to achieve.
List<Products> products = new List<Products>();
List<Fitment> fitment = new List<Fitment>();
class Fitment
{
public string Sku { get; set; }
public string Model { get; set; }
public string Years { get; set; }
}
class Products
{
public string Sku { get; set; }
public string Price { get; set; }
public string Vendor { get; set; }
}
What I need to do is match the SKU from the fitment list and add it to the item with the same SKU in the products list. One product can have several fitments (for example, a car seat that is used in multiple vehicles). The products list has 100k+ items and the fitment list has 400k+ items so I am looking for the fastest method to do this. I have tried several methods that time extremely long to process. What is the fastest way?

I solved my problem. I switched the Product to a Dictionary and used this to add the fitment data to the product data
productFitment.ForEach(fitment =>
{
if (dicProducts.ContainsKey(fitment.Sku))
dicProducts[fitment.Sku].ProductFitment.Add(fitment);
else
Console.WriteLine(fitment.Sku);
});

Related

Entity Framework NullReferenceException when accessing lists

I am using Windows Forms and I'm just learning Entity Framework and I have a question: I created a Customer class with a list type of Item and with Entity Framework I created a table for it. But when I try to get the list from it, I get a NullReference exception.
This are my classes:
public class Item
{
public int Id{ get; set; }
public string ItemName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string UserName { get; set; }
public string PassWord { get; set; }
public List<Item> Items { get; set; }
}
And this is the method I created to get the customer list based on the ID of the customer. I get the Id from the login and it works just fine:
public List<Item> CustomerItems()
{
using var context = new CustomerContext();
var customer= context.Customers.Find(Id);
var items = customer.Items;
return items;
}
I wanted to use this method to update the datagrid and add new items to the list.
I added some entries to the Item table but they don't show up in the datagrid.
Please check the loading related data section of the docs for options how to load linked data.
Also in this case you can just use proper query returning only needed data. For example:
var items = context.Customers
.Where(c => c.Id == Id)
.SelectMany(c => c.Items)
.ToList();
In the code snippet presented above a NullReferenceExceptionmay occur in the access to Items in the expression customer.Items when customer is null. The code should be modified to handle the null value, e.g.
var items = (customer == null) ? new List<Item>() : customer.Items;

Assigning products to categories with CategoryName given as foreign key in Product objects

I have a CMS system where a user can create a Category object, then the user can click on a created category and add Product objects to it. When a user is browsing products, they can click on a Category, which sends the CategoryId with the Post request to the products index page. The products index page then gets the category object matching the category Id passed, selects the Products list property belonging to that category and displays the products. Category class has a property of a List<Product> of Products.
I now have a CSV data import form which allows users to do the same thing but with a single CSV file upload.
The CSV file data (shaded grey) is a list of products. Note - category name column for each product (5th shaded column, i.e. "home", "heath&beauty"):
This is the class for Product and Category:
public class Product
{
public int Id { get; set; }
public string Barcode { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public Byte[] Image { get; set; }
[Required]
public Decimal Price { get; set; }
[Required]
public bool ShowOnIndex { get; set; }
}
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Byte[] Image { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
The relationship that I have between Categories and Products is One-to-Many in SQL Server; a Category has many Products, a Product has only one Category.
In the following code I am trying to save the list of products into their correct categories by CategoryId. However, I didn't have the CategoryIds for each product in the CSV data so I'm manually collecting all the ids for the Categories in the loop.
public static async Task SaveProducts()
{
var CategoryIds = new List<int>();
foreach (var item in CategoriesList)
{
db.Categories.Add(item);
var id = db.Categories.Where(c => c.Name == item.Name).Select(x => x.Id).ToString();
CategoryIds.Add(int.Parse(id));
}
foreach (var item in ProductsList)
{
foreach (var id in CategoryIds)
{
var category = await db.Categories.FindAsync(id);
category.Products.Add(item);
}
}
await db.SaveChangesAsync();
}
How do I somehow match the CategoryName value in the Product objects to the correct Category Id.
I think you need to create a transaction scope and save category with SaveChangesAsync() after that you can get the new ID and use in product relation. See this post
How can I get Id of inserted entity in Entity framework?
I figured that I could add each product the right category they belonged to all in one loop and I did it like this:
public static async Task SaveProducts()
{
foreach (var category in CategoriesList)
{
foreach (var product in ProductsList)
{
if (product.CategoryName == category.Name)
{
category.Products.Add(product);
}
}
db.Categories.Add(category);
}
await db.SaveChangesAsync();
}
As you can see, the only entity I added to the database was the Category object category each time. I did not have to add Products individually because I think Entity Framework knows the one-to-Many relationship between Categories and Products, so when I add a Category, Entity Framework will look at any foreign keys (here the List in the Categories class) and it will add a new Product object into the Products entity table in the database for every product object that is inside the Category's list of Products each time the for loop iterates the categories list.

need guidelines in creating/updating related entities (is it possible with automapper?)

I'm new in ASP.NET MVC and would love to improve here. I used ASP.NET MVC + EF Code first approach. But I'm a little confuse on how to create/update related entites. So here's my scenario. Say,
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
public class Stock
{
public int Id { get; set; }
public int ItemId { get; set; }
public int StorageId { get; set; }
public float Amount { get; set; }
public virtual Item Item { get; set; }
public virtual Storage Storage { get; set; }
}
public class Storage
{
public int Id { get; set; }
public Name { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
So an Item has a 1:many relationship with Stock. And Storage has 1:many relationship with Stock
In displaying them I used Automapper which worked perfectly. (Thanks to SO for helping me)
Now, what I'm trying to achieve is.. How to create/update entites? (Is it possible to used Automapper here?)
Say, in one POST it will add an Item, with Stock, and with selected Storage. A sample code would be great for reference.
Any help would be much appreciated. Thanks
AutoMapper is just a tool to map the properties of the View Model to/from your Domain Model.
The View Model is what you use in all of your Views, and your Domain Model is the underlying business model which shouldn't be exposed to the Views.
This is what AutoMapper simplifies, it maps properties of these two models so that we don't have to keep on converting one model to the other.
Now moving on to creating / updating related entities...
Say we want to add new Stock using the navigation property on the Item.
Item item = this.DbSource.Items.First(itemEntity => itemEntity.Id == 5);
if(item.Stocks == null) item.Stocks = new Collection<Stock>();
item.Stocks.Add(new Stock
{
StorageId = 3,
Amount = 123F
});
this.DbSource.SaveChanges();
Another case that you just pointed out was having a new Item and X amount of Stock of that Item, which you want to store in the database in a single operation.
Storage storage = this.DbSource.Storages.First(storageEntity => storageEntity.Id == 3);
if(storage.Stocks == null) storage.Stocks = new Collection<Stock>();
Stock stock = new Stock
{
StorageId = 3,
Amount = 123F,
Item = new Item
{
Name = "Redbull"
}
};
storage.Stocks.Add(stock);
this.DbSource.SaveChanges();
Or if you have no data in your database and you want all 3 models posted in a single go...
Stock stock = new Stock
{
Amount = 123F,
Item = new Item
{
Name = "Redbull"
}
};
Storage storage = new Storage
{
Name = "It's a secret"
};
storage.Stocks.Add(stock);
this.DbSource.Storages.Add(storage);
this.DbSource.SaveChanges();
Also modify all of your Models with a constructor which initializes a Collection on all of your ICollection navigational properties, this way you can avoid the NullReferenceException
So for example modify the Item class to this
public class Item
{
public Item()
{
this.Stocks = new Collection<Stock>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
Using the Item as root property
Collection<Stock> stocks = new Collection<Stock>();
Collection<Stock> stocks.Add(new Stock
{
StorageId = 123,
Amount = 1000F
});
Item item = new Item
{
Name = "Pizza",
Stocks = stocks
};
this.DbSource.SaveChanges();

How to query a flatten sub collection in RavenDb? Index needed?

I am using RavenDb in C# web project. I have an object that I need to query its child collection with 1 row per child object and some of the root/parent object properties.
Note: This is not the actual design, just simplified for this question.
public class OrderLine
{
public string ProductName { get; set; }
public int Quantity { get; set; }
public DateTime? ShipDate { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
public DateTime OrderDate { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
The order with the orderlines is one single document. ShipDate will be updated on each line because not all products are always in stock.
I need to be able to create a list of the last 10 products sent with the following columns:
OrderId
Customer
ProductName
ShipDate
This doesn't work because SelectMany is not supported:
var query = from helper in RavenSession.Query<Order>()
.SelectMany(l => l.OrderLines, (order, orderline) =>
new { order, orderline })
select new
{
helper.order.OrderId,
helper.order.CustomerName,
helper.orderline.ProductName,
helper.orderline.ShipDate
};
var result = query.Where(x => x.ShipDate.HasValue)
.OrderByDescending(x => x.ShipDate.Value).Take(10);
I believe the right thing to do isto create an Index that will flatten out the list but I haven't had any success. I don't believe a Map-Reduce situation will work because as I understand it will effectively does a group by which Reduces the number of documents to less rows (in the index). But in this case, I am trying to expand the number of documents to more rows (in the index).
I would rather not put each OrderLine in a separate document but I do not know what my options are.
Since you want to filter and sort by fields in the subclass, you'll need to make sure all the fields you want are indexed and stored.
public class ShippedItemsIndex
: AbstractIndexCreationTask<Order, ShippedItemsIndex.Result>
{
public class Result
{
public int OrderId { get; set; }
public string CustomerName { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public DateTime ShipDate { get; set; }
}
public ShippedItemsIndex()
{
Map = orders =>
from order in orders
from line in order.OrderLines
where line.ShipDate != null
select new
{
order.OrderId,
order.CustomerName,
line.ProductName,
line.Quantity,
line.ShipDate
};
StoreAllFields(FieldStorage.Yes);
}
}
Then you can project from the index into your results.
var query = session.Query<Order, ShippedItemsIndex>()
.ProjectFromIndexFieldsInto<ShippedItemsIndex.Result>()
.OrderByDescending(x => x.ShipDate)
.Take(10);
var results = query.ToList();
Here is a complete test demonstrating.

Linq to Entities, Master Detail into DataContracts Query

This is part of my WebAPI, and I'm having trouble getting this data out of Linq to entities into my Datacontract objects. I will be returning a custom data type to the caller, whether they want it XML or JSON, I don't care, I'm just handing this to WebAPI to take care of it.
I'm using VS2013 and EF5, ASP.NET 4.5 in C#
The structure is as follows:
ProductCategory
ID
CategoryName
(a million other things)
List<Products>
Products
ID
ProductName
(a million other things)
Category
I have set up a DataContract that looks like the following:
ProductCategoryDataContract
ProductCategoryId
ProductCategoryName
List<ProductDataContract> Products
ProductDataContract
ProductName
Basically, I want to get a Linq query to return ALL categories, and within it ALL products.
from prodcat in context.ProductCategories order by prodcat.ItemOrder
select new ProductCategoryDataContract
{
ProductCategoryId = prodcat.Id
Products = prodcat.Products // this obviously fails as ProductDataContract != Products
}
If I try
Products = new List<ProductDataContract> { //initializer stuff }
I don't have any of the intellisense things I would think I would have (ProductName, etc), because I'm guessing I'm in the list.
Basically, I have all the relationships set up and I can get everything in straight up EF, but because I'm putting these into new datacontracts, it's giving me a little grief (mostly due to my lack of linq knowledge).
My question is:
1.) how can I do this
and
2.) how can I do this with minimal database hits. Potentially I'm firing off thousands of items within tens of product groups.
Thanks much, and if I'm not clear on anything, please lmk. And, the above is pseudocodish, not the real deal so if I made stupid naming errors, that's unlikely 'it' :)
public interface IProducts
{
int ProductId { get; set; }
decimal Price { get; set; }
List<IProductCategories> Categorieses { get; set; }
}
public interface IProductCategories
{
int ProductId { get; set; }
string ProductCategoryName { get; set; }
}
internal class Products : IProducts
{
public int ProductId { get; set; }
public decimal Price { get; set; }
public List<IProductCategories> Categorieses { get; set; }
}
internal class ProductCategories : IProductCategories
{
public int ProductId { get; set; }
public string ProductCategoryName { get; set; }
public ProductCategories(int productId, string productCategoryName)
{
ProductId = productId;
ProductCategoryName = productCategoryName;
}
}
public class ProductDataContract
{
public int ProductId { get; set; }
public List<IProductCategories> Categorieses { get; set; }
}
//Here is how you get your data:
// your retun objects
var products = new List<ProductDataContract>();
using (
var db =
new DataClassesDataContext(
ConfigurationManager.ConnectionStrings["TestConnectionString"].ConnectionString))
{
foreach (var prod in db.Products.Select(p => new Products {ProductId = p.ProductId}))
{
prod.Categorieses = new List<IProductCategories>();
foreach (var category in db.ProductCategories.Where(c => c.ProductId == prod.ProductId))
{
prod.Categorieses.Add(new ProductCategories(category.ProductId, category.ProductCategoryName));
}
products.Add(new ProductDataContract {Categorieses = prod.Categorieses, ProductId = prod.ProductId});
}
}

Categories

Resources