I have a collection of NHibernate objects ConcurrentBag<Event> with properties project_id and name. I want to a set of unrelated (schema wise) objects from another table which also match these properties.
The SQL I would expect would look something like:
SELECT * FROM table WHERE (project_id = 1 AND name = 'foo')
OR (project_id = 2 AND name = 'foo')
OR (project_id = 1 AND name = 'bar')
... etc
The pairs of values in the WHERE clause are based on the values from each Event in the ConcurrentBag<Event>.
I am not sure how to query this with NHibernate (ideally with LINQ). Is this even possible?
This would be difficult with LINQ I think, but if you use QueryOver it's easy to build a WHERE clause dynamically using Restrictions.Disjunction:
var disjunction = Restrictions.Disjunction();
foreach (Event evt in events)
{
disjunction.Add(Restrictions.Where<Table>(
t => t.project_id == evt.project_id && t.name == evt.name);
}
var rows = session.QueryOver<Table>()
.Where(disjunction)
.List<Table>();
Related
foreach (ReportType t in reportsCollection)
{
List<Report> reps = (from r in t.reports where r.isChecked == true select r).ToList<Report>();
foreach (Report r in reps)
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(r.path));
}
This statement works just fine. I'm just looking for a single LINQ statement that could maybe do this.
Basically, I have a report object with a property called isChecked. reportsCollection contains several reportTypes that contains lists of reports of that type. So the collection looks like this:
type1
report
report 2
report 3
type 2
report 4
report 5
report 6
and so on.
I want a LINQ statement that will grab all reports where isChecked == true within those types. I suspect a loop is necessary, but I was curious to see if the community had a solution. Thanks!
You want to use SelectMany
var query = reportsCollection.SelectMany(t => t.reports)
.Where(r => r.isChecked == true)
.ToList();
In query expression syntax form, you might write it as
var query = (from type in reportsCollection
from report in type.reports
where report.isChecked == true
select report).ToList();
In query syntax it will look like this
var query = from t in reportCollection
from r in t.reports
where r.isChecked == true
select r;
Let's say I have a table in a database that has three columns: Agency ID, Name, and Value.
I want to get a collection of <Name, Value> pairs grouped by Agency ID.
How can I do this? I tried something like below, which works, but makes a DB call for each agency!
from div in db.AgencyDivisionsENT
group div by div.AgencyId into NamePairCollection
select new KeyValuePair<int, IEnumerable<DivisionResults>>(NamePairCollection.Key,
NamePairCollection.Select(k => new DivisionResults
{
Name = k.Name,
Value = k.Value
));
I want to end up with something like this: IEnumerable<KeyValuePair<int, IEnumerable<NameValuePair>>>
Using chain syntax it would be:
db.AgencyDivisionsENT
.GroupBy(x=>x.AgencyId)
.ToDictionary(x=>x.Key, g=>g.Select(x=>new { k.Name, k.Value }).ToArray());
The easiest way to avoid round-tripping with this type of query is to group on the client side - by calling .AsEnumerable():
db.AgencyDivisionsENT
.Select (x => new { x.AgencyId, x.Name, x.Value } )
.AsEnumerable()
.GroupBy(...) // AsEnumerable() forces grouping to happen on the client
.ToDictionary(...)
This is in no way inefficient - as long as:
you select only the data you need from the server with the initial .Select statement
if you need .Where statement to filter the data, it is placed before the .AsEnumerable
you're selecting detail rows (as in this case) rather than just aggregates.
I'm trying to use LINQ to query the following Entity Data Model
based on this db model
I'd like to be able to pull a list of products based on ProductFacets.FacetTypeId.
Normally, I'd use joins and this wouldn't be a problem but I don't quite understand how to query many-to-many tables under the Entity DataModel.
This is an example sql query:
select p.Name, pf.FacetTypeId from Products p
inner join ProductFacets pf on p.ProductId = pf.ProductId
where pf.FacetTypeId in(8, 12)
Presuming EF 4:
var facetIds = new [] { 8, 12 };
var q = from p in Context.Products
where p.FacetTypes.Any(f => facetIds.Contains(f.FacetTypeId))
select p;
In EF (assuming the mapping is done correctly), joins are hardly ever used; navigation properties are used instead.
Your original SQL returns a tuple with repeated Name entries. With LINQ, it's often easier
to "shape" the queries into non-tuple results.
The following should be the same as the SQL, only instead of returning (Name, FacetTypeId) pairs with repeated Names, it will return a type that has a Name and a sequence of FacetTypeIds:
var facetIds = new [] { 8, 12 };
var result = from p in db.Products
select new
{
p.Name,
FacetTypeIds = from pf in p.FacetTypes
where pf.FacetTypeId == 8 || pf.FacetTypeId == 12
select pf.FacetTypeId,
};
Suppose I have three tables:
Person(pid, ...)
PersonAddress(pid, aid,...)
Address(aid, ...)
Then I want to get the person address like sql:
select a.* from address a join PersonAddress pa on a.addressID=pa.addressID
where pa.personID = myPersonID
Use Entity Framework to create Entity model, then want to write a linq equivalent as above sql.
I tried it in following way:
var addresses = this.GetAddress();
var personaddresses = this.GetPersonAddress();
var query = from ad in addresses
from pa in personaddresses
where ((ad.AddressID == pa.AddressID)&&(pa.PersonID==person.personID))
select ad;
but I got error. Or I try to start from:
var result = this.Context.Address;
var result = result.Join .... //how to write linq in this way?
How to write the linq?
This is untested but if you have all of your relationships setup and you create the model (I have used Model as the name for this) from this you should be able to use the following:
var values = this.Model.Address.Select(a => a.PersonAddress.Where(pa => pa.Id == myPersonID));
You almost never use join in LINQ to Entities.
Try:
var q = from p in Context.People
where p.PersonId == personId
from a in p.Addresses // presumes p.Addresses is 1..*
select a;
Assuming you have three entities: Person, PersonAddress and Address, here is a query that should meet your needs (this example assumes an Entity Framework context named context):
var values = context.PersonAddress.Where(pa => pa.Person.PersonId == myPersonId).Select(pa => pa.Address);
However, if the PersonAddress table exists as a pure many-to-many relationship table (i.e. contains only keys), you'd be better off setting up your Entity Framework model in such a way that the intermediate table isn't necessary, which would leave you with the much simpler:
var values = context.Person.Where(p => p.PersonId == myPersonId).Addresses;
Based on the additional feedback
Because you need to include the country table, you should originate your query from the Address table. In that case:
var values = context.Address.Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)
To include the Country table in the result:
var values = context.Address.Include("Country").Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)
I am looking to optimize my LINQ query because although it works right, the SQL it generates is convoluted and inefficient...
Basically, I am looking to select customers (as CustomerDisplay objects) who ordered the required product (reqdProdId), and are registered with a credit card number (stored as a row in RegisteredCustomer table with a foreign key CustId)
var q = from cust in db.Customers
join regCust in db.RegisteredCustomers on cust.ID equals regCust.CustId
where cust.CustomerProducts.Any(co => co.ProductID == reqdProdId)
where regCust.CreditCardNumber != null && regCust.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.DisplayName,
RegNumber = cust.RegNumber
};
As an overview, a Customer has a corresponding Person which has the Name; PersonID is a foreign key in Customer table.
If I look at the SQL generated, I see all columns being selected from the Person table. Fyi, DisplayName is an extension method which uses Customer.FirstName and LastName. Any ideas how I can limit the columns from Person?
Secondly, I want to get rid of the Any clause (and use a sub-query) to select all other CustomerIds who have the required ProductID, because it (understandably) generates an Exists clause.
As you may know, LINQ has a known issue with junction tables, so I cannot just do a cust.CustomerProducts.Products.
How can I select all Customers in the junction table with the required ProductID?
Any help/advice is appreciated.
The first step is to start your query from CustomerProducts (as Alex Said):
IQueryable<CustomerDisplay> myCustDisplay =
from custProd in db.CustomerProducts
join regCust in db.RegisteredCustomers
on custProd.Customer.ID equals regCust.CustId
where
custProd.ProductID == reqProdId
&& regCust.CreditCardNumber != null
&& regCust.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
This will simplify your syntax and hopefully result in a better execution plan.
Next, you should consider creating a foreign key relationship between Customers and RegisteredCustomers. This would result in a query that looked like this:
IQueryable<CustomerDisplay> myCustDisplay =
from custProd in db.CustomerProducts
where
custProd.ProductID == reqProdId
&& custProd.Customer.RegisteredCustomer.CreditCardNumber != null
&& custProd.Customer.RegisteredCustomer.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
Finally, for optimum speed, have LINQ compile your query at compile time, rather than run time by using a compiled query:
Func<MyDataContext, SearchParameters, IQueryable<CustomerDisplay>>
GetCustWithProd =
System.Data.Linq.CompiledQuery.Compile(
(MyDataContext db, SearchParameters myParams) =>
from custProd in db.CustomerProducts
where
custProd.ProductID == myParams.reqProdId
&& custProd.Customer.RegisteredCustomer.CreditCardNumber != null
&& custProd.Customer.RegisteredCustomer.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
);
You can call the compiled query like this:
IQueryable<CustomerDisplay> myCustDisplay = GetCustWithProd(db, myParams);
I'd suggest starting your query from the product in question, e.g. something like:
from cp in db.CustomerProducts
join .....
where cp.ProductID == reqdProdID
As you have found, using a property defined as an extension function or in a partial class will require that the entire object is hydrated first and then the select projection is done on the client side because the server has no knowledge of these additional properties. Be glad that your code ran at all. If you were to use the non-mapped value elsewhere in your query (other than in the projection), you would likely see a run-time exception. You can see this if you try to use the Customer.Person.DisplayName property in a Where clause. As you have found, the fix is to do the string concatenation in the projection clause directly.
Lame Duck, I think there is a bug in your code as the cust variable used in your select clause isn't declared elsewhere as a source local variable (in the from clauses).