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});
}
}
Related
I've seen the scenarios where a ViewModel is populated with one LINQ query as shown below. Question: How can I populate the TotalSale attribute (column) - that is a grand total of Sale column - of the following ViewModel? Note: I'm using latest version of ASP.NET Core with VS2015.
ViewModel:
public class CategProdViewModel
{
public string Category { get; set; }
public string ProductName { get; set; }
public float Sale { get; set; }
public float TotalSale { get; set; }
}
Controller:
var innerJoinQuery = from category in categories
join prod in products on category.ID equals prod.CategoryID
select new CategProdViewModel { Category = category.Name, ProductName = prod.Name, Sale = prod.Sale };
You will have many of these in your view:
public class CategProdViewModel
{
public string Category { get; set; }
public string ProductName { get; set; }
public float Sale { get; set; }
public real TotalSale { get; set; }
}
But you will only have the last property TotalSale once. Therefore, change your view model to this:
public class CategProdViewModel
{
public string Category { get; set; }
public string ProductName { get; set; }
public float Sale { get; set; }
}
Create this model for your view and pass it to your view.
public class UseThisOnYourView // Give it another name
{
public List<CategProdViewModel> Items { get; set; }
public real TotalSale { get { return this.Items.Sum(x => x.Sale); } }
}
Then your query will be like this:
var innerJoinQuery = from category in categories
join prod in products on category.ID equals prod.CategoryID
select new CategProdViewModel { Category = category.Name, ProductName = prod.Name, Sale = prod.Sale };
var model = new UseThisOnYourView
{
Items = innerJoinQuery.ToList()
};
NOTE: Please keep in mind you will need to adjust your view to use the new type you are passing to it and adjust the code accordingly.
You don't clarify the structure of Product, but you can Sum multiple values
var innerJoinQuery = from category in categories
join prod in products on category.ID equals prod.CategoryID
select new CategProdViewModel {
Category = category.Name,
ProductName = prod.Name,
Sale = prod.Sale,
TotalSales = products.Sum(t => t.Sale)
};
Without more about the code, it's hard to say, and also, the prod.sale is probably only going to be the amount of the last product. You can use Sum(this IEnumerable<TSource> source, Func<TSource, double> selector) to sum the total of multiple items.
This is a quick answer. It will probably need to be altered depending on what the rest of your code looks like. You can also Group the query results to sum.
I'm using the SQL-NET Extensions in my Xamarin project. I am trying to return children emelments of my model using a where clause. Using the example models on the website:
public class Stock
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[MaxLength(8)]
public string Symbol { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)] // One to many relationship with Valuation
public List<Valuation> Valuations { get; set; }
}
public class Valuation
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[ForeignKey(typeof(Stock))] // Specify the foreign key
public int StockId { get; set; }
public DateTime Time { get; set; }
public decimal Price { get; set; }
[ManyToOne] // Many to one relationship with Stock
public Stock Stock { get; set; }
}
I can succesfully return a specific item with the children populated using:
var storedValuation = db.GetWithChildren<Valuation>(valuation.Id);
However I can't work out how to do it using a Where clause instead of Get. I have tried:
var results = db.Table<Valuation>().Where(x=>x.Price > 5.0m).ToList();
This returns with all the Stock parameters being null. I could then loop through each result and set them, but I assume there must be a better way to do it in the original query?
You can obtain the relationships for any object calling GetChildren method:
var results = db.Table<Valuation>().Where(x=>x.Price > 5.0m).ToList();
foreach (var element in results) {
conn.GetChildren(element);
}
There's also a convenience method for querying the database called GetAllWithChildren that performs the same operation in a less verbose way:
var results = conn.GetAllWithChildren<Valuation>(x => x.Price > 5.0m).ToList();
Please take into account that you cannot access relationships in this query as they would require a JOIN that is not being performed. For simple queries like this it should work as expected.
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();
public class Price
{
public int Id { get; set; }
public decimal Price { get; set; }
}
public class Product
{
public Product()
{
// Set default value here
}
public int Id { get; set; }
public string Sku { get; set; }
public int PriceId { get; private set; }
public virtual Price Price { get; set; }
}
I've searched in the internet the best way to set a default value in a field. They say put in the constructor or make a backing field. Now what if I want to set a default value in an entity from another entity's field value? Say, the default value of Product's Price is the latest Price(in the Price class)
How do you achieve that?
Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id)?
Any help would be much appreciated. Thanks
What i would do is when i query the DB to return the Product / Products, i would let it fill the price for me.
This is a just for demonstration purposes
I would do something like:
public class MyDataAccessLayer
{
public IEnumerable<Product> GetProducts()
{
return DbContext.Products.Select(x => new Product
{
Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id)
};
}
public Product GetProduct(string id)
{
var product = DbContext.Products.FirstOrDefault(x => x.Id == id);
if (product != null)
{
product.Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id);
}
return product;
}
}
I would definitely let me Data Access Layer do the fetching for me and have it fill the latest price from the DB, and not have the POCO access my database.
Keep last inserted Producte in Cache. while you are creating new Product get last Product from Cache and set default values inside constructor and when you are inserting new Product update Cache.
While you are inserting new Product it's same as getting last Product from database because new inserted product is last product in database. So instead of:
var lastProduct = Context.Products.FirstOrDefault().OrderByDescending(c => c.Id);
do this:
var lastProduct = Context.Products.Add(newProduct);
Cache.Set("LastProduct", lastProduct, new CacheItemPolicy { SlidingExpiration = new TimeSpan(1, 0, 0, 0)});
There are some advantages here. first you dot query database every time for getting last product. second your are not going access EF Context inside your Poco class.
public class Product
{
public Product()
{
// var lastProduct = (Product)Cache["LastProduct"];
// Set default value here
}
public int Id { get; set; }
public string Sku { get; set; }
public int PriceId { get; private set; }
public virtual Price Price { get; set; }
}
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.