I have object CartItem and Product.
I need to make one List<> from these 2 objects.
Tried:
List<(CartItem,Product)> product = new List<(CartItem,Product)>();
Code:
public async Task<IActionResult> Index()
{
var username = User.Identity.Name;
if (username == null)
{
return Login();
}
var user = _context.Users.FirstOrDefault(x => x.Email == username);
List<CartItem> cart = _context.ShoppingCartItems.Where(s => s.IDuser.Equals(user.Id)).ToList();
List<Product> product = new List<Product>();
foreach (var item in cart)
{
product.Add(_context.Product.FirstOrDefault(x => x.id == item.ProductId));
}
return View(user);
}
The correct answer would be to not do that. What would be the expected result for 5 items and 2 products?
The code is loading both CartItem and Product objects from the database using loops. That's very slow as each object requires yet another query. This can be replaced with a single line producing a single SQL query.
If CartItem has Product and User properties (as it should) all the code can be replaced with :
var cart=_context.ShoppingCartItems
.Include(i=>i.Product)
.Where(s=>s.User.Email==userName)
.ToList();
EF Core will generate the SQL query that joins User, CartItem, Product together and returns items and their products, but no User data. The Product data will be available through the CartItem.Product property
What was asked but shouldn't be used
If a List<(CartItem,Product)> is absolutely required (why???) you could use that instead of a List<Product>, and add items inside the loop:
// PLEASE don't do it this way
var dontUseThis = new List<(CartItem,Product?)>();
foreach (var item in cart)
{
var product=_context.Product.FirstOrDefault(x => x.id == item.ProductId);
dontUseThis.Add((item,product));
}
This will result in one extra SQL query per cart item.
Slightly better
A slightly better option would be to generate a WHERE product.ID IN (....) clause, to load all relevant products in a single query. After that the two lists would have to be joined with JOIN.
var productIds=cart.Select(c=>c.ProductId);
var products=_context.Product
.Where(p=>productIds.Contains(p.Id))
.ToList();
var dontUseThis = products.Join(cart,
p => p.Id,
c => c.ProductId,
(c, p) => (c,p))
.ToList();
This will reduce the N+1 queries to 2. It's still slower than letting the database do the work though
First, see the answer of #Panagiotis Kanavos. Aside that, for combining part, you can do this:
List<CartItem> cartItems; // assuming you already have this
List<Product> products; // assuming you already have this
// The combination part
var result = from p in products
join ci in cartItems on p.Id = ci.ProductId // Find out how product and cartItem relates
select new (p,ci);
// Need List?
var resultingList = result.ToList();
You can make a struct and put both variables in there:
struct EntireProduct
{
CartItem cartItem;
Product product;
}
then you can create a list of that struct:
List<EntireProduct> product = new List<EntireProduct>();
Related
How do I select two or more values from a collection into a list using a single lambda expression? Here is what I am trying:
List<Prodcut> pds=GetProducts();
List<Product> pdl = new List<Product>();
foreach (Product item in pds)
{
pdl.Add(new Product
{
desc = item.Description,
prodId = Convert.ToInt16(item.pId)
});
}
GetProducts() returns a list of Products that have many (about 21) attributes. The above code does the job but I am trying to create a subset of the product list by extracting just two product attributes (description and productId) using a single lambda expression. How do I accomplish this?
What you want to do is called projection, you want to project each item and turn them into something else.
So you can use a Select:
var pdl = pds.Select(p => new Product
{
desc = p.Description,
prodId = Convert.ToInt16(p.pId)
}).ToList();
I have this mvc controller:
public ActionResult Index(string country)
{
var c = _hc.Countries.Where(l => l.Code.Equals(country, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
var customers = _hc.Customers.Where(h => h.Country.Code.Equals(country, StringComparison.InvariantCultureIgnoreCase));
foreach (var h in customers)
h.Country = c;
return View(customers);
}
Everything works, but I just want the Customer.Country.Code to have a value. And since this one lists for all customers with the same country...
Same for when I use a raw SqlQuery (because of the geolocation stuff)
var customers = _hc.Database.SqlQuery<Customer>(#"
SELECT customers.*, countries.code
FROM [dbo].customers
inner join countries on customers.countryid = countries.id
where geography::Point(50.436912, 5.972050, 4326).STDistance(geography::Point([Latitude], [Longitude], 4326)) <= 25000").ToList();
I have the countryid in the customer object, because that is the Foreign Key. And have seen some extension method which you could use to include other data. But according to my intellisens, it's not availble. Probably something easy. But too hard for a friday afternoon (for me).
In order to use the Include() extension method you need to add the following:
using System.Data.Entity;
See DbExtensions.Include
Example usage:
var customers = _hc.Customers
.Include(h => h.Country)
.Where(h => h.Country.Code.Equals(country, StringComparison.InvariantCultureIgnoreCase));
I am trying to link up the RestaurantId in the RestaurantReservationEventsTbl with the RestaurantID in the RestaurantTbl to display reservations that are only made for the currently logged in restaurant.
I am receiving the following error in my code operator == cannot be applied to operands of type int and iqueryable int
Here is what I am doing in my home controller
var RestaurantIDRestaurantTbl = from r in db.Restaurants select r.RestaurantID;
//var listOfRestaurantsReservations = db.RestaurantReservationEvents.ToList();
var listOfRestaurantsReservations = db.RestaurantReservationEvents.Where(x => x.RestaurantID == RestaurantIDRestaurantTbl).ToList();
//return View(restaurants.Where(x => x.RestaurantEmailAddress == UserEmail).ToList());
//create partial view called _RestaurantReservation
return PartialView("_RestaurantReservations", listOfRestaurantsReservations);
You have to change your code to materialize the restaurantIds like this:
var RestaurantIDRestaurantTbl = (from r in db.Restaurants
select r.RestaurantID).ToList();
Then you may change the code as below for the comparison to work:
var listOfRestaurantsReservations = db.RestaurantReservationEvents.Where(x => RestaurantIDRestaurantTbl.Contains(x.RestaurantID)).ToList();
Anyway this is not the best solution. I will write another example for you, just try this example if it is working or not and let me know for the result.
I would considering changing the code as below to be much more efficient:
var listOfRestaurantsReservations = (from r in db.Restaurants
join e in db.RestaurantReservationEvents
on r.RestaurantID equals e.RestaurantID
//where r.RestaurantID == something //if where condition needed
select e).ToList();
If your tables are not connected with foreignkeys please consider to read this documentation here to make a better structure of the tables since they are related to each-other.
If your tables are related as in documentation article you might have something like that:
var RestaurantIDRestaurantTbl = db.Restaurants.SingleOrDefault(x => x.RestaurantID == something);
if(RestaurantIDRestaurantTbl != null)
{
var listOfRestaurantsReservations = RestaurantIDRestaurantTbl.RestaurantReservationEvents.ToList();
}
{
// This will give you a list of IDs
var RestaurantIDRestaurantTbl = db.Restaurants
.Select(p => p.RestaurantID)
.ToList();
// Using .Any() is a better choice instead of .Contains()
// .Contains is used to check if a list contains an item while .Any will look for an item in a list with a specific ID
var listOfRestaurantsReservations = db.RestaurantReservationEvents
.Where(p => RestaurantIDRestaurantTbl.Any(r => r.pRestaurantID == p))
.ToList();
}
I have 3 tables Project, Province, ProjProvRel
In my project page when I add data I select multiple provinces for each project
means my Province is multi select dropdown list.
I insert data it is working I get the inserted Id and added to ProjProvRel with selected Ids of Province
Now In details view I want to display my data but I could not solve it.
here is my code:
// GET: Project/Details/5
public ActionResult Details(int? id)
{
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var mydata = db.Projects.Find(id);
if (mydata == null)
{
return HttpNotFound();
}
var prov_id = from o in db.ProRel where o.ProjectId.Equals(id) select o.ProvinceIds;
foreach (var p_id in prov_id)
{
int[] PIds = new int[] {p_id};
}
var Prov= from c in db.Provinces where c.ID in pIds;
ViewBag.Province = Prov;
return View(mydata);
}
one problem is how can I select data from table based on where condition
var prov_id = from o in db.ProRel where o.ProjectId.Equals(id) select o.ProvinceIds;
is the above query correct ? I am new to ASP.Net MVC
also blow query is correct ?
var Prov= from c in db.Provinces where c.ID in pIds;
how can I select data from Table Province where province.ID in PIds
Get the rows from ProRel where the ProjectId matches with id:
var prov_ids = db.ProRel.Where(r => r.ProjectId == id).Select(r => r.ProvinceId);
Get the provinces that can be found from prov_id:
var provs = db.Provinces.Where(p => prov_id.Any(i => p.ID == i));
I hope you don't mind the lambda-style LINQ.
Also, I think you should consider your variable naming. For example, prov_id gives the idea of a single id and since you declared the type implicitly it is difficult to tell otherwise.
I have 2 lists. 1 is a collection of products. And the other is a collection of products in a shop.
I need to be able to return all shopProducts if the names match any Names in the products.
I have this but it doesn't seem to work. Any ideas?
var products = shopProducts.Where(p => p.Name.Any(listOfProducts.
Select(l => l.Name).ToList())).ToList();
I need to say give me all the shopproducts where name exists in the other list.
var products = shopProducts.Where(p => listOfProducts.Any(l => p.Name == l.Name))
.ToList();
For LINQ-to-Objects, if listOfProducts contains many items then you might get better performance if you create a HashSet<T> containing all the required names and then use that in your query. HashSet<T> has O(1) lookup performance compared to O(n) for an arbitrary IEnumerable<T>.
var names = new HashSet<string>(listOfProducts.Select(p => p.Name));
var products = shopProducts.Where(p => names.Contains(p.Name))
.ToList();
For LINQ-to-SQL, I would expect (hope?) that the provider could optimise the generated SQL automatically without needing any manual tweaking of the query.
You could use a join, for example:
var q = from sp in shopProducts
join p in listOfProducts on sp.Name equals p.Name
select sp;
A fuller guide on join is here.
You could create an IEqualityComparer<T> that says products with equal names are equal.
class ProductNameEqulity : IEqualityComparer<Product>
{
public bool Equals(Product p1, Product p2)
{
return p1.Name == p2.Name
}
public int GetHashCode(Product product)
{
return product.Name.GetHashCode();
}
}
Then you can use this in the Intersect extension method.
var products = shopProducts.Intersect(listOfProducts, new ProductNameEquality());
Try this please
var products = shopProducts.Where(m=> listOfProducts.Select(l=>l.Name).ToList().Contains(m=>m.Name));
var products = shopProducts
.Where(shopProduct =>
listOfProducts.Any(p => shopProduct.Name == p.Name))
.ToList();