I have an EF 4.0 model that has a parent child relationship (say order and orderdetails)
order
{
[primative]
orderid,
ordername,
ordershipping
[navigation]
orderdetails
}
orderdetail
{
orderdetailid,
orderpartid,
orderquantity,
orderpartname
}
My question is how do i load orders with orderdetails where the quantity is greater than 1 in LINQ?
for example
var orders = (from o in context.Orders.Include("OrderDetails")
where....
any ideas?
Okay simple answer actually...
http://msmvps.com/blogs/matthieu/archive/2009/10/07/ef-include-with-where-clause.aspx
Related
I'm new with Linq and hoping for some clarity on a particular query.
I have two tables (simplified for demonstration):
Table: Customer
CustomerId | Name
1 | John Smith
2 | Peter James
Table: Order
id | CustomerId | Total
1 | 1 | $100
2 | 1 | $200
Sample CustomerDto:
public class CustomerDto
{
public long CustomerId { get; set; }
public string Name{ get; set; }
public CustomerOrder[] CustomerOrderList{ get;set;}
}
Linq example for left outer join in the select here, they return string.empty if the join fails, I'm not sure the equivalent when I'm returning an object rather than a string.
My Linq query looks as follows. I've used the DefaultIfEmpty() to assist in a left outer join, however given I'm dealing with my object, I'm not sure how to return null if there isn't anything.
IQueryable<CustomerDto> search =
from customer in _database.Customer
join customerOrder in _database.CustomerOrder on customer.CustomerId equals customerOrder.CustomerId into CS
from subCustomerSale in CS.DefaultIfEmpty()
select new CustomerDto
{
CustomerId = customer.CustomerId,
Name = customer.Name,
CustomerOrderList = subCustomerSale
};
As I mentioned, I want to return a list of orders rather than one row per order. So there should be two records returned (the two customers), one with a list of orders and the other without any.
How do I achieve this?
The first step to make the entities easier to work with is to ensure that navigation properties are set up. If the table is called "Order" then the entity can be Order, or renamed to CustomerOrder if you like. A Customer entity can have an ICollection<Order> collection which EF can automatically map provided a consistent naming convention and normalization is used. (Otherwise explicit mapping can be provided)
For example:
public class Customer
{
[Key]
public int CustomerId { get; set; }
// other customer fields.
public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
}
From here you are projecting Customers to a CustomerDTO, so we should also project the Orders to an OrderDTO. Note that when using navigation properties we don't have to explicitly join entities. We don't even have to eager load related data via Include(). The later would apply if/when we want to work with the entities rather than projections.
The resulting query would end up looking like:
IQueryable<CustomerDto> search = _database.Customer
.Select(c => new CustomerDto
{
CustomerId = c.CustomerId,
Name = c.Name,
Orders = c.Orders.Select(o => new OrderDto
{
OrderId = o.OrderId,
Total = o.Total
}).ToList()
});
The benefit is no need to explicitly write Join expressions. EF can help simplify accessing related data considerably rather than just facilitating using Linq as an alternative to SQL. This would return an empty list rather than #null if there are no Orders for that customer. It may be possible to substitute a #null if there aren't any orders, though worst case it could be post-processed after the results are materialized Ie:
var customers = await search.ToListAsync();
var noOrderCustomers = customers.Where(c => !c.Orders.Any()).ToList();
foreach(var customer in noOrderCustomers)
customer.Orders = null;
It really just boils down to whether the consumer is Ok knowing there is always an Orders collection that will be empty if there are no orders, or in the Orders collection is only present if there are orders. (via JSON etc. serialization)
The important details to consider: When filtering, such as filling in search criteria, do this before the Select as the IQueryable is working with entities so you have full access to the table fields. Adding Where clauses after the Select will limit the available fields to the ones you have selected for the DTO. (They will still bubble down into the SQL) There is a ToList inside the Select to build the Orders collection. This may look "bad" that it might be materializing data synchronously, but it will be executed only when the main query is. (Such as an awaited async operation on the IQueryable)
When projecting to DTOs be sure not to mix DTOs and entities such as:
IQueryable<CustomerDto> search = _database.Customer
.Select(c => new CustomerDto
{
CustomerId = c.CustomerId,
Name = c.Name,
Orders = c.Orders
});
... which can be tempting. The issue here is that "c.Orders" would return a collection of Order entities. Those Orders may have references to other entities or information you don't need to/want to expose to the consumer. Accessing references could result in lazy load costs, null references, or exceptions (i.e. disposed DbContext) depending on when/where they occur.
just put the ternary condition to achieve it like follows:
IQueryable<CustomerDto> search =
from customer in _database.Customer
join customerOrder in _database.CustomerOrder on customer.CustomerId equals customerOrder.CustomerId into CS
from subCustomerSale in CS.DefaultIfEmpty()
select new CustomerDto
{
CustomerId = customer.CustomerId,
Name = customer.Name,
CustomerOrderList = subCustomerSale == null ? null : subCustomerSale // add this line and you will get the null as well if there is no record
};
I have a problem with laravel relationships.
I have 3 models:
City —(hasMany) —>Clinic—(hasMany) —>Stock
Stock —(belongsTo) —>Clinic —(belongsTo) —>City
I need get all cities which has stocks, looks like «Stock->cities». I can write sql-query:
SELECT ct.id, ct.name
FROM CfgCity as ct
RIGHT JOIN lr_clinics AS cl ON(ct.id=cl.city_id)
RIGHT JOIN lr_clinic_stocks AS st ON(cl.id=st.clinic_id)
WHERE st.deleted_at IS NULL
GROUP BY ct.id
But I want decision in laravel-orm, because it’s more readable and I don’t need write names of rows and tables. Is it possible?
Thanks.
From your question it is clear that city has relation with clinic and clinic has relation with stock that means city has relation with stock through clinic. That you can define it using laravel relationship.
City Model
class City extends Model {
public function clinics(){
return $this->hasMany(Clinic::class);
}
public function stocks(){
return $this->hasManyThrough(Stock::class, Clinic::class);
}
}
Fetch data
$cities = City::whereHas('stocks')->get();
For details you can check https://laravel.com/docs/5.6/eloquent-relationships#has-many-through
I'm coming from TSQL + C# land and have been trying to adapt to linq and EF. Many-to-many relationships have been tripping me up. I have models with many-to-many relationships that I want to query from a database. Such as:
class Product{
public int ID {get;set;}
public string ProductName {get;set;}
public virtual ICollection<Tag> Tags {get;set;}
}
class Tag {
public int ID {get;set;}
public string TagName {get;set;}
public virtual ICollection<Product> Products {get;set;}
}
I'm able to get a product itself out of the DbContext, and then later fetch it's associated Tags like this:
// product exists in memory as a Product with an empty product.Tags
var query = from p in db.Product
from t in db.Tags
where p.ID == product.ID
select p.Tags;
Then I can assign the product.Tags with the fetched Tags. Obviously, this is very inefficient when dealing with multiple products if I have to query for every product.
With linq and EF, I want to be able to get a Product with all of its associated Tags in one round trip to the database. Also, I want to be able to get all Products and their associated Tags (or a filtered list of Products). How do would the linq look?
Edit:
Ok, after some more fiddling around, I've got this:
var query = db.Product.Include("Tags")
.Where(p => p.Tags.Any(t => t.Products.Select(m => m.ID).Contains(p.ID)));
This is almost what I need. The results are all products with tags. Missing are the products that don't have tags. I think of this as the equivalent of a SQL inner join. I want to left outer join the tags to the product, and return all products with tags optional. How to get all products with their associated tags without excluding products that have no tags?
Edit:
This was easier than I thought.
var query2 = db.Product.Include("Tags").DefaultIfEmpty();
This gets all the products and their respective tags, including products without tags. Hopefully it works for the right reasons...
The purpose of using an object-relational mapper like EF is that it maps relationships for you. If you are manually joining objects that have foreign keys in the database, you are doing it wrong.
See my question Why use LINQ Join on a simple one-many relationship?
The correct answer is simply context.Products.Include("Tags"), which will auto-magically join Products and Tags for you. This is literally the biggest (only?) benefit of using an ORM.
My first MVC3 EF 4.2 site and I'm confused on some things, currently on ViewModels when querying and saving. Please correct me if I explain this poorly, i'm not sure how to term this. The .edmx automatically created the table classes but I read it was better to create a ViewModel, considering I need to join tables to display/edit my Product completely. The controller code below is where I join tables to output a Product to edit, and then save. My question - what is the right way to save the Product, to the Product.cs model generated by DbContext or my own ProductViewModel.cs?
Is there an easier method to query a product and join the tables and then map to the viewmodels parameters, or do I keep doing all this in the controller like below?
I also want to save/update the product each time someone views/clicks on the product, so I wasn't sure if I create a separate ViewModel for updating just that parameter or again, use the Product model.
Hope that makes sense! I can explain further if needed.
private SiteForgeEntities db = new SiteForgeEntities();
public ActionResult Edit(int id)
{
var viewModel = (
from a in db.Products
join b in db.Sites
on a.SiteId equals b.SiteId
join c in db.Sections
on a.SectionId equals c.SectionId
join d in db.Affiliates
on a.AffiliateId equals d.AffiliateId
select new ProductViewModel()
{
ProductId = a.ProductId,
Product = a.Product,
Description = a.Description,
Image = a.Image,
Price = a.Price,
Clicks = a.Clicks,
Link = a.Link,
Site = b.Site,
Section = c.Section,
Affiliate = d.Affiliate
}).Single(x => x.ProductId == id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(Product product)
{
...update database...do I pass in and save back to Product or my ProductViewModel
}
You use ViewModel to pass multiple models to the view, but when you save data, you need to save it to the appropriate model. If you are adding or modifying products, you will add items to products (using your DbContext). If you have one-to-many relationship defined between two models (in your Product.cs model you might have a property declared as:
public virtual ICollection<SomeOtherModel> SomeOtherData { get; set; }
you can use this to build a table instead of passing everything in a ViewModel. There is a nice tutorial here regarding the CRUD operations using EF4. Have a look at these short tutorials that can give you an idea about your strategy http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc.
How to retrieve the records from more than one table which has one to many relationship.
Categories[table]
CategoryId
CategoryName
Products[table]
ProductId
CategoryId
ProductName
Description
Entites
Category[Entity]
CategoryId
CategoryName
List<Product>
Product[Entity]
ProductId
ProductName
Description
So if i give categoryId, i should get the category details with list of products associated with the category.
How to do this in linq to sql?
In linq to sql you get a reference property generated in each of your entities. This said if you do this:
Category cat = context.Categories.FirstOrDefault(x=>x.CategoryId == 1); //Where one is the //id of a random category
foreach(Product prd in cat.Products)
{
//do some logic here
}
you will get all the products.
See Include for LINQ to SQL