I have a working query as below:
var result = from sch in schemeDashboard
join exp in Expenditure on sch.schemeId equals exp.SchemeCode
into SchExpGroup
where sch.SectorDepartmentId == selectedDepartmentId &&
sch.YearCode == StateManager.CurrentYear
orderby sch.ADPId
select new
{
ProjectName = sch.schemeName,
ADPNo = sch.ADPId,
Allocation = sch.CurrentAllocation,
Expenditures = from expend in SchExpGroup
where expend.YearCode == StateManager.CurrentYear &&
expend.DepartmentId == selectedDepartmentId &&
InvStatus.Contains(expend.Status)
orderby expend.ADPId
group expend by expend.InvoiceId
};
Now I need to declare "result" as: System.Linq.IQueryable<...> result = null; so that I can write the query in various If-else blocks with minor modification. (projectName is string, ADPNo is int, Allocation is decimal).
I have tried as follow:
System.Linq.IQueryable<string, int, decimal,
System.Collections.Generic.IEnumerable<System.Linq.IGrouping<string, Expenditure>>> result = null;
But this is giving error as: The non-generic type 'IQueryable' cannot be used with typed arguments.
Can anyone point me in right direction? thanx.
The most generical class of C# is the class object. So you can declare your list with the Object type.
IQueryable <object> result = null;
Well I hope it works for you as it worked for me.
Related
I want to define a function containing a Linq query as bellow:
public IQueryable GetBasket(Guid userId)
{
DabbaghanDataContext db = new DabbaghanDataContext();
int rowNo = 0;
var query = (from c in db.Carts
join co in db.CartOrders on c.Id equals co.Cart_Id
join p in db.Products on co.Product_Id equals p.Id
where c.UserId == userId && c.Issued == false
select new
{
co.Quantity,
co.TotalPrice,
p.Code,
p.Price,
p.Thumbnail
}).AsEnumerable().Select(r => new
{
RowNumber = ++rowNo,
Quantity = r.Quantity,
TotalPrice = r.TotalPrice,
Code = r.Code,
Price = r.Price,
Thumbnail = r.Thumbnail
});
return query;
}
I get error
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Linq.IQueryable'.
on the return query line.
What is the problem? How can I solve this problem? Please help.
Your problem is the call to AsEnumerable- It converts the IQueryable to a IEnumerable; and therefore, you cannot return it as an IQueryable.
Correct me if I am wrong, but the second select seems to only add the row number to the result. You might as well want to do that together with the initial select, and skip the call to AsEnumerable().
Possible solutions: Rewrite the query to not use AsEnumerable (if you want an IQueryable returned), or you could change the return type to be IEnumerable, if that is a better fit for your problem.
In return query; change that to return query.AsQueryable();
And also try to change the method signature to use IQueryable instead of the nongeneric one
How can I get 10 records from complex LINQ query?
I tried to put .Skip(X).Take(10), but it doesn't work, depending on where I'm trying to take 10 it returns the full set of objects or nothing.
Setting .Skip(X).Take(10) at the end of the query doesn't what I'm looking for because of slow performance.
This is my query:
List<ReportReturn> report =
from var1 in context.Table1
join var2 in context.Table2
on var1.AccountID equals var2.AccountID
join var3 in context.Table3
on var1.AccountID equals var3.AccountID into all
where
var1.SubAccountID == intSubAccountID &&
// ...... and more conditions
let actual = var1.Total.GetValueOrDefault(0)
let Unique = var2.CountUnique
let Total = var2.Count
// ........ and more helper values
orderby var1.Date descending
from final in all.DefaultIfEmpty()
select new ReportReturn {
// ........................some property assigments
};
just writing
.Skip(X).Take(10)
will give you output in IEnumerable<T> type but yours is List<T> type.
So you should use
.Skip(X).Take(10).ToList()
in your case.
Linq and compiled queries
This is my code:
I need to associated the value of a properties a.Value to a Literal.Text property.
Any idea how to do it?
Thanks for your valuable help!
using (var context = new CmsConnectionStringEntityDataModel())
{
context.CmsOptions.MergeOption = MergeOption.NoTracking;
var query = CompiledQuery.Compile<CmsConnectionStringEntityDataModel, IQueryable<CmsOption>>
(ctx => from a in ctx.CmsOptions where a.OptionId == 7 select a);
uxHeaderIncluder.Text = // What I do here?;
}
Have you looked at these examples:
http://www.blog.ingenuitynow.net/15+Minutes+On+LINQ+Compiled+Queries.aspx
http://msdn.microsoft.com/en-us/library/bb896297.aspx
The core of the approach is to spesify the input type(s) in the Compile method call.
Your compile call should be something like this:
CompiledQuery.Compile<CmsConnectionStringEntityDataModel,string,string, IQueryable<CmsOption>>
((ctx,str1,str2)=>from a in ctx.CmsOptions where a.OptionId == 7 && /* use str1 and str2 params here */ select a);
I'm trying to get the following SQL query to work in LINQ:
Select id from table1 where id in (1,2) or canceledId in (1,2)
I'm using BuildContainsExpression to achieve the "IN" condition, but I can't figure out how to implement the "or" condition.
My shot in the dark is as follows:
var identifiers = new List<int> {1,2};
var query = (from t in Context.Table1
select t);
var query =
query.Where(BuildContainsExpression<Table1, int>(t => t.Id, identifiers));
if (showCanceled)
{
var expression = query.Where(BuildContainsExpression<Table1, int>(t => t.CanceledId.Value, identifiers)).Expression;
Expression.Or(expression, transactionsQuery.Expression);
}
But I get the following exception:
The binary operator Or is not defined for the types 'System.Linq.IQueryable1[Table1]' and 'System.Linq.IQueryable1[Table1]'..
Any ideas? -Am I in the right direction?
Thanks,
Nir.
You are appending your OR in the wrong place. What you are doing now is effectively something like this:
(from t in Context.Table1
where identifiers.Contains(t.Id)
select t)
OR
(where identifiers.Contains(t.CanceledId))
The second problem is that the BuildContainsExpression method you use, returns a lambda expression, something that looks like this:
t => t.Id == 1 || t.Id == 2 || ...
You can't change this expression once it's generated. However, that's what you want because you'd like to have something like this:
t => t.Id == 1 || t.Id == 2 || ... || t.CanceledId == 1 || t.CanceledId == 2 || ...
You can't simply take the body of this lambda expression and or it together with another expression because it depends on the parameter t.
So what you can do is the following:
// Overload of BuildContainsExpression.
private static Expression<Func<T, bool>> BuildOtherContainsExpression<T>(
ParameterExpression p, Expression field1, Expression field2, int[] values)
{
var eq1 = values.Select(v => Expression.Equal(field1, Expression.Constant(v)));
var eq2 = values.Select(v => Expression.Equal(field2, Expression.Constant(v)));
var body = eq1.Aggregate((acc, equal) => Expression.Or(acc, equal));
body = eq2.Aggregate(body, (acc, equal) => Expression.Or(acc, equal));
return Expression.Lambda<Func<T, bool>>(body, p);
}
// Create a parameter expression that represents something of type Table1.
var parameter = Expression.Parameter(typeof(Table1), "t");
// Create two field expressions that refer to a field of the parameter.
var idField = Expression.Property(parameter, "Id");
var canceledIdField = Expression.Property(parameter, "CanceledId");
// And finally the call to this method.
query.Where(BuildContainsExpression<Table1>(
parameter, idField, canceledIdField, identifiers));
Your if statement would now look like this:
if (!showCanceled)
{
// Use original version of BuildContainsExpression.
}
else
{
// Create some expressions and use overloaded version of BuildContainsExpression.
}
I know I'm a bit late to the party here - but I think the original code in the original poster's question was 99% right.
The only wrong was that instead of
Expression.Or
it should have been
Expression.OrElse
I just asked this question. Which lead me to a new question :)
Up until this point, I have used the following pattern of selecting stuff with Linq to SQL, with the purpose of being able to handle 0 "rows" returned by the query:
var person = (from p in [DataContextObject].Persons
where p.PersonsID == 1
select new p).FirstOrDefault();
if (person == null)
{
// handle 0 "rows" returned.
}
But I can't use FirstOrDefault() when I do:
var person = from p in [DataContextObject].Persons
where p.PersonsID == 1
select new { p.PersonsID, p.PersonsAdress, p.PersonsZipcode };
// Under the hood, this pattern generates a query which selects specific
// columns which will be faster than selecting all columns as the above
// snippet of code does. This results in a performance-boost on large tables.
How do I check for 0 "rows" returned by the query, using the second pattern?
UPDATE:
I think my build fails because I am trying to assign the result of the query to a variable (this._user) declared with the type of [DataContext].User.
this._user = (from u in [DataContextObject].Users
where u.UsersID == [Int32]
select new { u.UsersID }).FirstOrDefault();
Compilation error: Cannot implicitly convert type "AnonymousType#1" to "[DataContext].User".
Any thoughts on how I can get around this? Would I have to make my own object?
Why can you keep doing the samething? Is it giving you an error?
var person = (from p in [DataContextObject].Persons
where p.PersonsID == 1
select new { p.PersonsID, p.PersonsAdress, p.PersonsZipcode }).FirstOrDefault();
if (person == null) {
// handle 0 "rows" returned.
}
It is still a reference object just like you actual object, it is just anonymous so you don't know the actual type before the code is compiled.
Update:
I see now what you were actually asking! Sorry, my answer no longer applies. I thought you were not getting a null value when it was empty. The accepted response is correct, if you want to use the object out of scope, you need to create a new type and just use New MyType(...). I know DevEx's RefactorPro has a refactoring for this, and I think resharper does as well.
Call .FirstOrDefault(null) like this:
string[] names = { "jim", "jane", "joe", "john", "jeremy", "jebus" };
var person = (
from p in names where p.StartsWith("notpresent") select
new { Name=p, FirstLetter=p.Substring(0,1) }
)
.DefaultIfEmpty(null)
.FirstOrDefault();
MessageBox.Show(person==null?"person was null":person.Name + "/" + person.FirstLetter);
That does the trick for me.
Regarding your UPDATE: you have to either create your own type, change this._user to be int, or select the whole object, not only specific columns.
if (person.Any()) /* ... */;
OR
if (person.Count() == 0) /* ... */;
You can still use FirstOrDefault. Just have
var PersonFields = (...).FirstOrDefault()
PersonFields will be be null or an object with those properties you created.