How would I count these records using Linq to NHibernate (3.1) - linq

I have an entity defined like so:
public class TestEntity : EntityBase
{
public string Source {get;set;}
public bool Suppressed {get;set;}
/* other stuff */
}
I want to show an HTML table that looks like:
Source Suppressed Not Suppressed
-------------------------------------------------
Source1 30 1225
Soure 7 573
My first attempt to query this was:
from e in _session.Query<TestEntity>()
group e by e.Source into g1
select new
{
Source = g1.Key,
Suppressed = g1.Sum(x=>x.Suppressed ? 1 : 0),
NotSuppressed = g1.Sum(x=>x.Suppressed ? 0 : 1),
}
But of course, Linq choked on the ternary expression when converting it to SQL. Any alternative ways to do this?
Edit: I tried Dmitry's suggestion, and it returns the same counts for both. The SQL generated by his suggestion is:
select
customer0_.SourceA as col_0_0_,
cast(count(*) as INT) as col_1_0_,
cast(count(*) as INT) as col_2_0_
from
dbo.Customers customer0_
group by
customer0_.SourceA
Which obviously isn't what I want...

What about doing something like g1.Count(x => x.Suppressed == true)?

Related

Why is this LINQ to EF query not working?

I have a problem with the following method:
public IQueryable<Customer> ListCustomers(int? parentId)
{
Debug.Assert(parentId == null);
//var list = _customerRepository.List().Where(c => c.ParentId == null);
var list = _customerRepository.List().Where(c => c.ParentId == parentId);
return list;
}
I have one Customer in my DB with a ParentId of null. When I call the method with ListCustomers(null), list is empty for the return statement. If I swap the commented out line in and query with a hard-coded null, then list contains my one customer.
What could cause this difference between these two queries? Why is the one with c.ParentId == parentId not returning anything?
Becouse the Nullable type the linq provider will not generate the proper IS NULL check. See this answer for further information: https://stackoverflow.com/a/785501/1195510
EF translates your query with int? to something like this:
DECLARE #parentId Int = null
SELECT ... WHERE ParentId = #parentId
When this is executed on the database, it doesn't do what you expect because in SQL [column] = NULL is always false.
I agree EF could handle this better, but as a workaround, you can write something like this:
.Where( c => !parentId.HasValue
? !c.ParentId.HasValue
: c.ParentId.Value == parentId.Value
)
EF will then generate a ( somewhat verbose ) SQL statement with the correct IS NULL predicates.
with nullable types you have to use it like this:
.Where(c=> object.Equals(c.ParentId, parentId))

C# linq generate query dynamically [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
build linq queries dynamically
linq update query dynamically
I have a method passing code id, e.g. if code - 1 then i select data from tbl A and if code - 2 I want to select from tbl B
e.g. I tried to do: but the code doesnt work
public static void Update(int code, int userid)
{
var query =
(from t1 in code == 1? dataContext.tb1 : dataContext.tb2
where t1.Id == userid
select t1).SingleOrDefault();
}
how can I generate query dynamically?
Thanks
It is not obvious from your question but is sounds like you are using some linq wrapper on database access. If this is the case then it is most likely that you cannot crate query that has condition on source of data. The easiest way to crate such if is to crate two queries as simple as that:
public static void Update(int code, int userid)
{
SomeType result;
if(code == 1)
{
result =
(from t1 in dataContext.tb1
where t1.Id == userid
select t1).SingleOrDefault();
}
else
{
result =
(from t1 in dataContext.tb2
where t1.Id == userid
select t1).SingleOrDefault();
}
// and do something with result
}
Edit
On now it obvious that you are using linq to sql so the above stands. But your error is easier then that you have ternary operator that returns two different types on true and false side and that is the error.

prevent unnecessary cross joins in count query of generated sql code

I am using this query:
return from oi in NHibernateSession.Current.Query<BlaInteraction>()
select new BlaViewModel
{
...
NoPublications = oi.Publications.Count(),
...
};
BlaInteraction contains an IList of publications (i.e. entities). To determine the number of publications one does not really need to do all the joins for a publication. Can I prevent nhibernate from using joins in the generated sql (e.g. using projection???) somehow?
Thanks.
Christian
PS:
This is what NH produces (slightly adapted):
select cast(count(*) as INT) from RelationshipStatementPublications publicatio21_, Publication publicatio22_ inner join Statements publicatio22_1_ on publicatio22_.StatementId=publicatio22_1_.DBId where publicatio21_.StatementId = 22762181 and publicatio21_.PublicationId=publicatio22_.StatementId
This is what would be sufficient:
select cast(count(*) as INT) from RelationshipStatementPublications publicatio21_ where publicatio21_.StatementId = 22762181
Why can't you just create another query ?
Session.QueryOver<Publication>().Where(x => x.BlaInteractionId == idSentAsParameter).Select(Projections.RowCount()).SingleOrDefault<int>();
I think that's will work
return from oi in NHibernateSession.Current.Query<BlaInteraction>()
select new BlaViewModel
{
...
NoPublications = Session.QueryOver<Publication>().Where(x => x.BlaInteractionId == oi.Id).Select(Projections.RowCount()).SingleOrDefault<int>();
...
};
Another edit, have you tried lazy="extra" ?
Ok the best solution I have found so far is to use a FNH Formula:
mapping.Map(x => x.NOPublications).Formula("(select count(distinct RelationshipStatementPublications.PublicationId) from RelationshipStatementPublications where RelationshipStatementPublications.StatementId = DBId)");
public virtual int NOPublications {get; private set;}
when I map from the domain to the view model I use:
NoPublications = oi.NOPublications,
Christian

Converting sql statement that contains 'with' cte to linq

I have this piece of code here, been battling with it for hours. basically what this sql statement does is gets ALL subfolders of a specified folder (#compositeId).
WITH auto_table (id, Name, ParentID) AS
(
SELECT
C.ID, C.Name, C.ParentID
FROM Composite_Table AS C
WHERE C.ID = #compositeId
UNION ALL
SELECT
C.ID, C.Name, C.ParentID
FROM Composite_Table AS C
INNER JOIN auto_table AS a_t ON C.ParentID = a_t.ID
)
SELECT * FROM auto_table
This query would return something like this:
Id | Name | ParentId
1 | StartFolder| NULL
2 | Folder2 | 1
4 | Folder3 | 1
5 | Folder4 | 4
Now I want to convert the code to linq. I know it involves some form of recursion but still stuck thanks to the with statement.
There is no Linq to SQL equivalent that can do that (in an efficient manner). Best solution would be to call a SP/View/UDF from Linq containing that statement.
You could write code (recursive or not) that repeatedly queries the database, until it has all the results.
But I think there is no way to write a single LINQ to SQL query that would get all the results you need in one go, so it's probably best to keep the query in SQL.
There is known plugin 'LinqToDb', which provides methods to get CTE equivalent in Linq
public static List<Composite> GetSubCascading(int compositeId)
{
List<Composite> compositeList = new List<Composite>();
List<Composite> matches = (from uf in ctx.Composite_Table
where uf.Id == compositeId
select new Composite(uf.Id, uf.Name, uf.ParentID)).ToList();
if (matches.Any())
{
compositeList.AddRange(TraverseSubs(matches));
}
return compositeList;
}
private static List<Composite> TraverseSubs(List<Composite> resultSet)
{
List<Composite> compList = new List<Composite>();
compList.AddRange(resultSet);
for (int i = 0; i < resultSet.Count; i++)
{
//Get all subcompList of each folder
List<Composite> children = (from uf in ctx.Composite_Table
where uf.ParentID == resultSet[i].Id
select new Composite(uf.Id, uf.Name, uf.ParentID)).ToList();
if (children.Any())
{
compList.AddRange(TraverseSubs(children));
}
}
return compList;
}
//All where ctx is your DataContext

Linq to entities - searching in EntityCollection navigation properties

We have classes
public Invoice: EntityObject
{
public EntityCollection<InvoicePosition> Positions { get {...}; set{...}; }
...
}
public InvoicePosition: EntityObject
{
public string GroupName { get {...}; set{...}; }
}
We are given IQueryable<Invoice>, we are not given IQueryable<InvoicePosition>. How should I find invoices that have positions, where GroupName is 'Fuel'?
IQueryable<Invoice> invoices = InvoiceRepository.List();
IQueryable<Invoice> invoicesThatHaveFuelPositions =
from i in invoices
where ?
select i
EntityFramework should be able to translate it to proper sql query.
EDIT
As Mark Seemann wrote, I can use:
IQueryable<Invoice> invoices = InvoiceRepository.List().Include("Positions").Include("OtherInclude");
IQueryable<Invoice> invoicesThatHaveFuelPositions =
from i in invoices
from p in i.Positions
where p.GroupName = 'Fuel'
select i;
There is a problem. When I use this filtering, I lose "OtherInclude". I think that this is not proper way of filtering when using EF. I'll have to change it to:
IQueryable<Invoice> invoices = InvoiceRepository.List().Include("Positions").Include("OtherInclude");
IQueryable<Invoice> invoicesThatHaveFuelPositions = invoices.Where(???);
But what should I write in Where?
EDIT
Changed Include("Position") to Include("Positions").
EDIT
Alex James gave link to the tip (http://blogs.msdn.com/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx), which suggests:
IQueryable<Invoice> invoicesThatHaveFuelPositions =
from i in invoices
where i.Positions.Any(p => p.GroupName == 'Fuel')
select i;
It seems to work and doesn't influence EF includes.
Building on Marks answer. If you do this:
var q = from i in invoices.Include("something")
from p in i.Positions
where p.GroupName == "Fuel"
select i;
The include is lost (see this tip) because the EF loses all includes if the shape of the query changes, for example if you do implicit joins like in a SelectMany query, aka from from.
The workaround is to write your query, and then at the end apply the Include.
Something like this:
var q = ((from i in invoices
from p in i.Positions
where p.GroupName == "Fuel"
select i) as ObjectQuery<Invoice>).Include("something");
If you do this the Entity Framework actually does the include.
Hope this helps
Alex
Something like this ought to work:
var q = from i in invoices
from p in i.Positions
where p.GroupName == "Fuel"
select i;
However, that uses the navigation property Positions, which by default isn't loaded (the Entity Framework uses explicit loading). It will, however, work, if the invoices variable was created like this:
var invoices = from i in myObjectContext.Invoices.Include("Positions")
select i;

Resources