I have:
public class Basket
{
public int BasketId { get; set;}
public string Name { get; set;}
public virtual ICollection<BasketItem> BasketItems {get; set;}
}
public class BasketItem
{
public int BasketItemId { get; set;}
public string Name { get; set;}
public int BasketId { get; set;}
public int ProductId { get; set; }
public virtual Product product { get; set;}
}
public class Product
{
public int ProductId { get; set;}
public string ProductName { get; set; }
}
Can anyone know how to query in linq to retrieve BasketList which includes basketItems list and prodcut ?
Sorry: The question had confused. I'm trying retrieve basketList with this:
(from b in Baskets
join bi in BasketItems on b.Id equals bi.BasketId
join pp in Products on bi.ProductId equals pp.Id
where b.Id == 10001
select b).ToList()
Product is not included in basketItems with above linq query. I've updated the question.
You've set up your objects like a relational database with ProductId and BasketId as foreign keys onto a BasketItem "table". It is better to embed your "sub" classes into your container class. That makes using Linq to query much easier.
Try:
public class BasketItem
{
public int BasketItemId { get; set; }
public string Name { get; set; }
public Basket Basket { get; set; }
public Product Product { get; set; }
}
public class Basket
{
public int BasketId { get; set; }
public string Name { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
private void SetupAndQuery()
{
#region setup data objects
Product product01 = new Product {ProductName = "Whizzo", ProductId = 12345};
Basket basket01 = new Basket {Name = "Johns Basket", BasketId = 34535987};
Product product02 = new Product { ProductName = "Blammo", ProductId = 54321 };
Basket basket02 = new Basket { Name = "Susans Basket", BasketId = 23546758 };
List<BasketItem> basketItems = new List<BasketItem>();
BasketItem basketItem01 = new BasketItem();
basketItem01.BasketItemId = 12365;
basketItem01.Name = "Erm";
basketItem01.Basket = basket01;
basketItem01.Product = product01;
basketItems.Add(basketItem01);
BasketItem basketItem02 = new BasketItem();
basketItem02.BasketItemId = 15478;
basketItem02.Name = "What";
basketItem02.Basket = basket02;
basketItem02.Product = product02;
basketItems.Add(basketItem02);
#endregion
#region Query
//The 2 code blocks below do the same thing
//Foreach/If version of the Query
foreach (BasketItem item in basketItems)
{
if (item.Product.ProductId == 54321)
{
//Do stuff here
}
}
//LINQ version of the Query
foreach (BasketItem item in basketItems.Where(item => item.Product.ProductId == 54321))
{
//Do stuff here
}
#endregion
}
You may want to change what types go inside other types. I suggest Basket at the top and a Product list inside that. BasketItem looks unnecessary.
Related
What would be the best way to get all the products in all the child categories of a selected main category?
This is my Class File Structure:
public partial class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int ParentCategoryId { get; set; } //reference to Id
public ICollection<Category> _subcategories;
}
public partial class ProductCategory
{
public int Id { get; set; }
public int ProductId { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual Product Product { get; set; }
}
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ProductViewMap> _productViewmap;
}
public class ProductViewMap
{
public int ProductId { get; set; }
public int ProductViewCount { get; set; }//indicated how many times product has been viewed means most popular product.
public virtual Product Product { get; set; }
}
This is what i have tried:
//List to hold all Category Ids of Parent Category Id say for eg:1
List<int> categoryChildList = new List<int>();
var data = (from temp in context.Category
where temp.ParentCategoryId == parentCategoryId
select new { CategoryId = temp.Id });
if(data.Count() > 0)
{
foreach (var cat in data)
{
int _cat = Convert.ToInt32(cat.CategoryId);
categoryChildList.Add(_cat);
}
var tmpList = (from p in Context.ProductCategory
join m in context.Product on p.ProductId equals m.Id
join n in context.ProductViewMap on m.Id equals n.ProductId
where categoryChildList.Contains(p.CategoryId)
select m).ToList();
Here error is coming:
Object reference not set to instance of object.**
When i am removing this line then everything works fine:
join n in context.ProductViewMap on m.Id equals n.ProductId
any help would be greatly appreciated.
Sql fiddle which contain sample records:http://www.sqlfiddle.com/#!3/bde6b
If Input is :Computer(parentCategoryId:1) then output is as below
Final output:
ProductId ProductName
1 hp
2 compaq
3 lenovo
If all you're trying to do is to grab the Product records, then using the Extension Method syntax you could do it this way:
var products = context.ProductCategory.Where(pc => pc.Category.ParentCategoryID != null && pc.Category.ParentCategoryID == parentCategoryID)
.Select(pc => pc.Product)
.Distinct()
.ToList()
.OrderBy(p => p.ProductViewMap.Max(pvm => pvm.ProductViewCount);
All of the joins will be taken care of by the SQL query generated by LINQ to Entities.
I have a parent entity Widget with core members and multiple WidgetTranslation children that have language translated members i.e. Description text available in English, French, German etc.
e.g.
public class Widget
{
public int Id { get; set; }
public string Code { get; set; }
public virtual ICollection<WidgetTranslation> WidgetTranslations { get; set; }
}
public class WidgetTranslation
{
public int WidgetId { get; set; }
public virtual Widget Widget { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
What is the most efficient method of querying the widget collection, flattening for a given LanguageId & projecting to a TranslatedWidget DTO
public class TranslatedWidget
{
public int Id { get; set; }
public string Code { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
Given languageId I've started with
DbSet.Select(w => new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = w.LanguageId,
Name = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Name,
Description = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Description,
Summary = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Summary
});
But I've a feeling this is inefficient and won't scale for more properties on WidgetTranslation.
Thanks
Use SelectMany to flatten structures via a single join:
var widgetQuery = from w in dbSet.Widgets
from wt in w.WidgetTranslations
where wt.Language == languageId
select new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = w.LanguageId,
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
});
I'm assuming here that you only have a single translation for each widget in a given language.
I would move Name, Description and Summary into a nested class of your DTO...
public class TranslatedWidgetTranslation
{
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
public class TranslatedWidget
{
public int Id { get; set; }
public string Code { get; set; }
public int LanguageId { get; set; }
public TranslatedWidgetTranslation Translation { get; set; }
}
Then you can project into that class and need First only once which would result in only one TOP(1) subquery in SQL instead of three:
DbSet.Select(w => new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = languageId,
Translation = w.WidgetTranslations
.Where(wt => wt.LanguageId == languageId)
.Select(wt => new TranslatedWidgetTranslation
{
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
})
.FirstOrDefault()
});
You must use FirstOrDefault here, First is not supported in a LINQ-to-Entities projection.
If you don't want that nested type you can project into anonymous types first and then convert into your final class, but the code will be a bit longer:
DbSet.Select(w => new
{
Id = w.Id,
Code = w.Code,
LanguageId = languageId,
Translation = w.WidgetTranslations
.Where(wt => wt.LanguageId == languageId)
.Select(wt => new
{
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
})
.FirstOrDefault()
})
.AsEnumerable()
.Select(x => new TranslatedWidget
{
Id = x.Id,
Code = x.Code,
LanguageId = x.LanguageId,
Name = x.Translation != null ? x.Translation.Name : null,
Description = x.Translation != null ? x.Translation.Description : null,
Summary = x.Translation != null ? x.Translation.Summary : null
});
I have the following simple "textbook" classes defined:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
The Category field in Product is an integer referencing the Id field in the Category class. In the database, it would be a foreign key relationship between the Product and Category tables.
But my application would need to show the friendly name of the Category of a product.
Q1: Is it correct practice to define a new class as follows:
public class ProductJ : Product
{
public string CategoryName { get; set; }
}
And then, if I now have a method getProductsJ as follows:
public class Test
{
public List<Category> Categories = new List<Category>() { ... };
public List<Product> Products = new List<Product>() { ... };
public List<ProductJ> getProductsJ()
{
var products = from p in Products
join c in Categories on p.Category equals c.Id
select new ProductJ { Id = p.Id, , Name = p.Name, CategoryName = c.Name }; //!
return products.ToList();
}
}
Q2: Is the above the best way to retrieve a list of Products with Category names?
Q3: In the select statement (//!) is there a faster way to populate the fields of the base class Product linqwithout having to enter them one by one?
Thanks.
You could project the result of the join into an object that contains a reference to both the product and category.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductCategory
{
public Product Product { get; set; }
public Category Category { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Category> Categories = new List<Category>();
List<Product> Products = new List<Product>();
var products = from p in Products
join c in Categories on p.Category equals c.Id
select new ProductCategory { Product = p, Category = c };
var list = products.ToList();
}
}
Does each book belongs to one category, or can it belong to more than one category in your case? because if a book belongs to one category then you can simply move the category name in the Book Table. also i will suggest to have a look on
MVC LINQ to SQL Table Join Record Display
I am following MVC music store similarly. http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-9
When I doing create orderDetails, I have got error with inner exception.
Could you help me what does it mean?
"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_OrderDetails_Product\". The conflict occurred in database \"rentalDB\", table \"dbo.Product\", column 'productId'.\r\nThe statement has been terminated."
Do I need to check in SQL Server? I don't know why it errors happen..
Can you give me some advice? I am giving you some my code.
Please help me. Thanks.
public int CreateOrder(Order order)
{
decimal orderTotal = 0;
var cartItems = GetCartItems();
// Iterate over the items in the cart, adding the order details for each
foreach (var item in cartItems)
{
var orderDetail = new OrderDetails
{
productId = item.Product.productId,
orderId = order.orderId,
unitPrice = item.priceValue,
rentalPeriod = item.rentalPeriod,
startDate = item.dateCreated.AddDays(2),
endDate = item.dateCreated.AddDays(2 + item.rentalPeriod),
quantity = item.count
};
// Set the order total of the shopping cart
orderTotal += (item.count * item.priceValue);
db.OrderDetails.Add(orderDetail);
}
// Set the order's total to the orderTotal count
order.total = orderTotal;
// Save the order
db.SaveChanges(); //I have error in here!!!
// Empty the shopping cart
EmptyCart();
// Return the OrderId as the confirmation number
return order.orderId;
}
Here is viewModel
public class ShoppingCartViewModel
{
public List<Cart> CartItems { get; set; }
public decimal CartTotal { get; set; }
}
Here is Cart
public class Cart
{
[Key]
public int recordId { get; set; }
public string cartId { get; set; }
public int productId { get; set; }
public decimal priceValue { get; set; }
public int count { get; set; }
public int rentalPeriod { get; set; }
public DateTime dateCreated { get; set; }
public virtual Product Product { get; set; }
}
Here is Product
public class Product
{
[Key] public int productId { get; set; }
[Required(ErrorMessage = "Please select category")]
public int categoryId { get; set; }
[Required(ErrorMessage = "Please fill in model name")]
[DisplayName("Model name")]
public String model { get; set; }
[DisplayName("Description")]
public String description { get; set; }
[DisplayName("Original price")]
public decimal price { get; set; }
[Required(ErrorMessage = "Please fill in stock of product")]
[DisplayName("Stock")]
public int stock { get; set; }
public virtual Category Category { get; set; }
}
I did not set FK in OrderDetails..
That's what I have got error before.
When I create FK between OrderDetails and Order, it will work.
I have this POCO and I want to return a list of the users in a particular company.
public class Company
{
public AccreditedCompany()
{
this.Branches = new HashSet<Branch>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int CompanyId { get; set; }
public bool Active { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
}
public class Branch
{
public Branch()
{
this.Users = new HashSet<User>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int BranchId { get; set; }
public int CompanyId { get; set; }
public string Name { get; set; }
public string ContactName { get; set; }
public virtual Company Company { get; set;}
public virtual ICollection<User> Users { get; set; }
}
public class User
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int UserId { get; set; }
public int BranchId { get; set; }
public string ComputerSN { get; set; }
public string CameraSN { get; set; }
public virtual Branch Branch { get; set; }
}
This is my LINQ query:
var company = (from u in objDataContext.Companies.Include(c=>c.Branches.Select(v=>v.Users))
where u.CompanyId == 8 select u).FirstOrDefault();
IQueryable<User> users = (from j in company.Branches select j.Users);
I have this compilation error on the second query:
Error 2 Cannot implicitly convert type
'System.Collections.Generic.IEnumerable>'
to 'System.Linq.IQueryable'. An explicit conversion exists (are
you missing a cast?)
I want to get a list of the users, similar to a plain SQL statement like
SELECT dbo.Users.* FROM Branches
INNER JOIN dbo.Users ON dbo.Branches.BranchId = dbo.Users.BranchId
INNER JOIN dbo.Companies ON dbo.Branches.CompanyId = dbo.Companies.CompanyId
WHERE (dbo.Companies.CompanyId = 8)
Thanks in advance.
Your user query could be:
IEnumerable<User> users = company.Branches.SelectMany(branch => branch.Users);
This will return all users in any branch of the company.
It looks to me like you could just use:
IQueryable<User> users = objDataContext.Users
.Where(u => u.Branch.CompanyId == 8);
I notice you have both Company and CompanyId on your Branch entity, though. That seems redundant, even though it simplifies this query slightly. You should be able to get rid of Branch.CompanyId and User.BranchId and just use the entity associations.