Add a many-to-one predicate to a LINQ-to-SQL query - c#

How do I filter an IQueryable<T> LINQ-to-SQL query on a related many-to-many table? For example, given an IQueryable<Product> where each Product has many Tags that are stored in a related ProductTags table, I want to filter on the association of one or more tags.
A document store or comma-separated value on Product would make this simpler, but the data are stored in denormalised SQL tables.
For bonus points, what if the ProductTags table is just a mapping-table and I also need to retrieve data from the Tags table?
This is what the schema looks like:
Products <-> ProductTags <-> Tags
Update: Looks like I want to filter against an EntitySet<T>.

If you've got a list of tag names, say tagNames, you can do something like:
var query = from p in Products
from t in p.ProductTags.Select(pc => pc.Tag)
where tagNames.Contains(t.TagName)
select new { p.ProductName, t.TagName };
This will get you all Product that have at least one of the tags in tagNames and you can select data from Product or Tag.
Note that this is not necessarily a distinct list of products.
If the tags are stored in ProductTags directly, the query changes into
var query = from p in Products
from t in p.ProductTags
where tagNames.Contains(t.TagName)
select new { p.ProductName, t.TagName };

Related

Linq how to use a subSelect as a virtual table in FROM

There may be similar questions out here but none that I could find for doing a subSelect in the FROM clause as a virtual table.
Most of the columns I need are in one table. There are a few columns needed from different tables that I cannot join on without getting a Cartesian join.
Here is my SQL query:
SELECT meter_name, a.loc_id, a.loc_name, a.facility_name, meter_type
FROM meter_table, (SELECT loc_id, loc_name, facility_name
FROM facility_table
WHERE id = 101) a
WHERE meter_id = a.fac_id
I have no idea how to convert this into Linq and it must be done tonight for a demo in the morning.
Assume this represents your meter_table within your database
in this case each element of the list represents a record in the database table holding the appropriate attributes
i.e the table columns will become the properties of each object
List<Meter> meter_table = new List<Meter>();
Assume this represents the facility_table table you want to join with.
same goes here, each element of the list represents a record in the database table holding the appropriate attributes
i.e the table columns will become the properties of each object
List<Facility> facility_table = new List<Facility>();
then perform the inner join like so:
var query = from m in meter_table
join a in facility_table on m.meter_id equals a.fac_id
where a.id == 101
select new { meter_name = m.MeterName,
loc_id = a.LocId,
facility_name = a.FacilityName,
meter_type = m.MeterType
};
where m.MeterName, a.LocId, a.FacilityName, m.MeterType are properties of their respective types.
it's also worth noting the variable query references an IEnumerable of anonymous types. However, if you want to return an IEnumerable of strongly typed objects then feel free to define your own type with the appropriate properties then just change select new to:
select new typeName { /* assign values appropriately */}
of the above query.

Outerjoin with a table of foreign keys in EF Data-first approach

I have a table Users which contain user information. I have a table Products which contain product information. I have a table called UserProduct which acts as a junction table and whose fields are UserId and ProductId. I am using a Entity Framework database first approach.
I want to outerjoin using Linq to find the following data.
All Users in the Users table.
All Users who have bought a particular product in terms of a Boolean called isPurchased.
My thinking was to left outer join table User with UserProduct and get all users and whether they have a product something like this.
var result = from a in Users
join b in UserProduct(Not available through EF) on a.Id equals b.prodId into group1
from g1 in group1.DefaultIfEmpty()
select new
{
id = g1.Id,
isPurchased = g1.prodId != null
}.ToList();
However in EF mapping, the object UserProduct is not created and so I cannot use it directly in my Linq query? So how do I go about this? Is there a way I can use linq to join tables with the actual table name(UserProduct) instead of joining entities?
Assuming Users contains a property List<Products> products to represent the junction information, and a variable boughtProductId to represent the particular product:
var result = from u in Users
let isPurchased = u.products.Any(p => p.Id == boughtProductId)
select new {
id = isPurchased ? boughtProductId : null,
isPurchased
}.ToList();

Select data from 3 tables with Linq to SQL

I have 3 tables. Orders, OrderItems and OrderItemServices. Each order can contain multiple OrderItems and each OrderItem can contain multiple OrderItemServices.
I want to get a list of data of all orders from these tables in Linq. I could write a join but how do I make an anonymous data type to in select clasue which can give me Order list in this hierarchy?
If I use navigation properties and then select OrderItemServices in side select clause shown below it would fire individual select query for each OrderItemService which I want to avoid.
from order in Orders
select new
{
ActiveOrders = order,
ActiveOrderItems =order.OrderItems,
ActiveServices = order.OrderItems.Select(o => o.OrderItemServices)
}
Is it possible to group each order with a structure of multiple items inside it and multiple services inside items?
Refer msdn to start on LINQ To SQL
To get data from three tables you can get idea from the following simple example
var data = (from product in context.Products
from department in context.Departments
from category in context.Categories
where product.DeptId == department.DeptId
&& product.CatId == category.CatId
select new
{
product.Code,
product.Name,
department.Name,
category.Name
}).Distinct.ToList();
You have to set up your context to use eager loading:
var context = new MyDataContext();
var options = new DataLoadOptions();
options.LoadWith<Orders>(x => x.OrderItems);
options.LoadWith<OrderItems>(x => x.OrderItemServices);
context.LoadOptions = options;
var query = from order in context.Orders // ... etc
Then sub items will be included in initial query result and won't cause additional requests to the database. This will use JOIN internally to retrieve all the data in one go. You can check generated SQL using SQL Server Profiler.
http://blog.stevensanderson.com/2007/12/02/linq-to-sql-lazy-and-eager-loading-hiccups/

How to set the count property of a related entity without having to perform an extra query or include unnecessary data

I have a class "Campaign" that has a navigation property "Students". The campaign class has an attribute "StudentsCount" to store the amount of students. I do not want to include all the students in the query results. How can I query for campaigns while attaching their respective student counts? I ideally do not want to iterate through all my campaigns after the initial query to grab the counts.
IQueryable<TEntity> query = this._objectSet.AsQueryable(); //this is my campaigns object set
query = query.Where(c => c.UserId == id);
query = query.Include("");
return query.ToArray();
Update: --
Please note that my initial query is grabbing more than one campaign
I'm thinking maybe I could do something with a select but I am not exactly sure how to accomplish this
Querying for counts without loading the collection of child items is called an extra lazy query if you need the term to allow you to Google around this.
In this case, you would do something like this:
var campaign = query.Single();
var studentsQuery = context.Entry(campaign).Collection(c => c.Students).Query();
var count = studentsQuery.Count();
This will materialise the count of entities without bringing them all back.
I ended up adding a computed column with sql onto the Campaign Table.
CREATE FUNCTION dbo.getStudentCount(#studentCount int)
RETURNS int
AS
BEGIN
DECLARE #r int
select #r = COUNT(*) from Student where CampaignId = #studentCount
RETURN #r
END
GO
ALTER TABLE Campaign ADD StudentCount AS dbo.getStudentCount(Id)
this automatically sets the column to be a generated attribute in the EDMX.

Including a second query in Entity Framework LINQ Query

I've got a table "Houses" and "Cats", which contains the columns "Id" and "HouseName" and "Id" and "CatName".
Now I got a table "HouseCatAssignments", where I store the relations between the Cats and the Houses (the Cat can live in more than one house and one house can store more than one cat).
This table looks like:
Id, CatId, HouseId
"CatId" is bound to Cats.Id and HouseId is bound to Houses.Id.
Now I want to display the Table "House" in a datagrid that also contains a field for "CatCount" - a counter for the value of how many cats are living in this house.
How should I now query my tables so I have all the values of "Houses" and an additional Column that contains the Cat-Count for the specific house?
For Entity Framework it should have automatically added navigation properties that allow you to do the following query:
var housesWithCount = context.Houses
.Select( h=> new
{
Id = h.Id,
HouseName = h.HouseName,
CatCount = h.Cats.Count()
});

Categories