The return type of my query is IEnumerable<XElement>. how can i convert the resultant data to XElement type? is it possible? could some body help me to understand this.
var resQ = from e in docElmnt.Descendants(xmlns + Constants.T_ROOT)
.Where(x => x.Attribute(Constants.T_ID).Value == "testid")
select e;
I have to pass resQ as a parameter to the below function. in order to do that I have to convert resQ to XElement type.
Database.usp_InsertTestNQuestions(tid, qId, qstn, ans, resQ );
As long as your query only returns a single result, you can either call Single() or First() on the result (also, there's no need for the extra query syntax up top):
// Use if there should be only one value.
// Will throw an Exception if there are no results or more than one.
var resQ = docElmnt.Descendents(xmlns + Constants.T_ROOT)
.Single(x => x.Attribute(Constants.T_ID).Value == "testid");
// Use if there could be more than one result and you want the first.
// Will throw an Exception if there are no results.
var resQ = docElmnt.Descendents(xmlns + Contants.T_ROOT)
.First(x => x.Attribute(Constants.T_ID).Value == "testid");
If you want to handle the case when no results are returned for the query without throwing an Exception, you can use SingleOrDefault (which will still throw an Exception if you get more than one result) or FirstOrDefault.
In addition to Justin's answer, you may want to allow for 0 elements returned or some other condition.
In that case, simply do a:
IEnumerable<XElement> resQ = docElmnt.Descendents(xmlns + Constants.T_ROOT)
.Where(x => x.Attribute(Constants.T_ID).Value == "testid");
if(resQ.Count() == 0) {
//handle no elements returned
} else if(resQ.Count() > 1) {
//handle more than 1 elements returned
} else {
XElement single = resQ.Single();
}
Most of the time I find it's best not to throw an error-- unless having exactly 1 returned is really important.
You can iterate over each element in the query and then call the method with your enumerator.
resQ.ToList().ForEach(e => ...func... );
Related
I am trying to write an elegant solution to filter a value against multiple Enum values in an IQueryable. Here's what I have got so far:
Extension
public static class EnumExtensions
{
public static bool IsAny<T>(this T value, params T[] choices)
where T : Enum
{
return choices.Contains(value);
}
}
Usage
query = query.Where(x => x.Status.IsAny(OrderStatus.Accepted, OrderStatus.Received, OrderStatus.Approved));
But when I execute this, I get following error:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The LINQ expression 'DbSet<SalesOrder>
.Where(s => s.DeletedAt == null)
.Where(s => False || s.SellerId == __request_OwnerId_0)
.Where(s => s.Status
.IsAny(OrderStatus[] { Accepted, Received, Approved, }))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Any pointers on how I can get this working?
You cannot use custom methods inside Linq queries and in Expression<> params for EF. You will need to do it the long-form way:
query = query
.Where( x =>
x.Status == OrderStatus.Accepted ||
x.Status == OrderStatus.Received ||
x.Status == OrderStatus.Approved
);
Depending on your RDBMS, you might be able to do it this way though:
static readonly OrderStatus[] _these = new[] { OrderStatus.Accepted, OrderStatus.Received, OrderStatus.Approved };
// ...
query = query.Where( x => _these.Contains( x.Status ) );
...which should be translated to this SQL:
WHERE
x.Status IN ( 0, 1, 2 )
(Assuming OrderStatus.Accepted == 0, Received == 1, Approved == 2).
I'm trying to manipulate properties in a GroupBy clause to be used in a dictionary:
var lifeStages = await _dbContext.Customers
.GroupBy(x => GetLifeStage(x.DoB))
.Select(x => new { LifeStage = x.Key, Count = x.Count() })
.ToDictionaryAsync(x => x.LifeStage, x => x.Count);
I'm expecting results like
adolescent: 10,
adult: 15,
senior: 12 etc
But getting error:
Either rewrite the query in a form that can be translated,
or switch to client evaluation explicitly
by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
Offcourse I can't combine ToDictionary() with any of the mentioned calls, and splitting up the query did not resolve the issues or taught my anything)
I've tried with making GetLifeStage() static and async, no difference there as well. The method gets called, performs what it needs to do, and still GroupBy can't be translated
If I leave out the Select() part and work with the Key of the GroupBy, same error:
"...could not be translated."
I saw an error too that said I couldn't combine a GroupBy() with a ToDictionary() during try-outs, but doesn't seem to pop up atm.
As I'm running out of ideas, all suggestions are welcome!
update:
private LifeStage GetLifeStage(DateTimeOffset doB)
{
var ageInMonths = Math.Abs(12 * (doB.Year - DateTimeOffset.UtcNow.Year) + doB.Month - DateTimeOffset.UtcNow.Month);
switch (ageInMonths)
{
case < 216:
return LifeStage.Adolescent;
case < 780:
return LifeStage.Adult;
case >= 780:
return LifeStage.Senior;
}
}
The problem is the usage of the custom GetLifeStage method inside the GroupBy expression. Custom methods cannot be translated to SQL because the query translator code has no way to know what is inside that method. And it cannot be called because there are no objects at all during the translation process.
In order to make it translatable, you have to replace the custom method call with its body, converted to translatable expression - basically something which can be used as expression bodied method. You can't use variables and switch, but you can use conditional operators. Instead of variable, you could use intermediate projection (Select).
Here is the equivalent translatable query:
var lifeStages = await _dbContext.Customers
.Select(c => new { Customer = c, AgeInMonths = Math.Abs(12 * (c.DoB.Year - DateTimeOffset.UtcNow.Year) + c.DoB.Month - DateTimeOffset.UtcNow.Month) })
.GroupBy(x => x.AgeInMonths < 216 ? LifeStage.Adolescent : x.AgeInMonths < 780 ? LifeStage.Adult : LifeStage.Senior)
// the rest is the same as original
.Select(x => new { LifeStage = x.Key, Count = x.Count() })
.ToDictionaryAsync(x => x.LifeStage, x => x.Count);
I need to retrieve the most recently updated object. Currently I am able to retrieve it by first doing something similar to the following:
em.createQuery("select m from MyObject m order by m.updateTime").setFirstResult(0).setMaxResults(1).getResultList()
and then getting the only object if the result list is not empty. I am curious whether there is any better way of doing this type of thing.
Thanks and regards.
Setting first result to 0 is redundant in this case and if you need just one row then you could use a getSingleResult() function.
So your code could be modified as follows:
MyObject myObject = em.createQuery("select m from MyObject m order by m.updateTime").setMaxResults(1).getSingleResult();
EDIT: as Tiny has mentioned in the comment in case when there are no matching rows the code will throw a NoResultException which is to be handled (e.g. assign a null value in this case).
EDIT2:
I've checked the Hibernate implementation of both methods and it turned out that getSingleResult() has all the code that getResultList() has and on top of that the following block:
if ( result.size() == 0 ) {
NoResultException nre = new NoResultException( "No entity found for query" );
getEntityManager().handlePersistenceException( nre );
throw nre;
}
else if ( result.size() > 1 ) {
final Set<X> uniqueResult = new HashSet<X>(result);
if ( uniqueResult.size() > 1 ) {
NonUniqueResultException nure = new NonUniqueResultException( "result returns more than one elements" );
getEntityManager().handlePersistenceException( nure );
throw nure;
}
else {
return uniqueResult.iterator().next();
}
}
else {
return result.get( 0 );
}
thus you end up with a slower implementation when using getSingleResult().
EDIT3: getSingleResult() would be beneficial in case if having zero or more than one resulting row is an invalid/exceptional data state (i.e. if such a case occurs then you know that your data is corrupted). Since both NoResultException and NonUniqueResultException are uncatched exceptions you will end up with only one line of code (no if, no catch needed in this case).
I have a enumeration of objects :
public IOrderedEnumerable<RentContract> Contracts {
get { return RentContracts.OrderByDescending(rc => rc.DateCreated); }
}
I have to compare a given RentContract instance with its previous RenContract instance on the list to highlight changes between the two objects, which is the most correct method to get the previous element ?
This is not possible directly. You can do it like this:
var input = new SomeClass[10]; //test data
var zipped = input.Zip(new SomeClass[1].Concat(input), (a, b) => { a, b });
var result = zipped.Where(x => x.b == null || x.a.DateCreated < x.b.DateCreated.AddHours(-1)); //some example
This solution is zipping the sequence with itself, but offset by one null element.
I have a query as follows:
var query = from x in context.Employees
where (x.Salary > 0 && x.DeptId == 5) || x.DeptId == 2
order by x.Surname
select x;
The above is the original query and returns let's say 1000 employee entities.
I would now like to use the first query to deconstruct it and recreate a new query that would look like this:
var query = from x in context.Employees
where ((x.Salary > 0 && x.DeptId == 5) || x.DeptId == 2) && (x,i) i % 10 == 0
order by x.Surname
select x.Surname;
This query would return 100 surnames.
The syntax is probably incorrect, but what I need to do is attach an additional where clause and modify the select to a single field.
I've been looking into the ExpressionVisitor but I'm not entirely sure how to create a new query based on an existing query.
Any guidance would be appreciated. Thanks you.
In an expression visitor you would override the method call. Check if the method is Queryable.Where, and if so, the methods second parameter is a quoted expression of type lambda expression. Fish it out and you can screw with it.
static void Main()
{
IQueryable<int> queryable = new List<int>(Enumerable.Range(0, 10)).AsQueryable();
IQueryable<string> queryable2 = queryable
.Where(integer => integer % 2 == 0)
.OrderBy(x => x)
.Select(x => x.ToString());
var expression = Rewrite(queryable2.Expression);
}
private static Expression Rewrite(Expression expression)
{
var visitor = new AddToWhere();
return visitor.Visit(expression);
}
class AddToWhere : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
ParameterExpression parameter;
LambdaExpression lambdaExpression;
if (node.Method.DeclaringType != typeof(Queryable) ||
node.Method.Name != "Where" ||
(lambdaExpression = ((UnaryExpression)node.Arguments[1]).Operand as LambdaExpression).Parameters.Count != 1 ||
(parameter = lambdaExpression.Parameters[0]).Type != typeof(int))
{
return base.VisitMethodCall(node);
}
return Expression.Call(
node.Object,
node.Method,
this.Visit(node.Arguments[0]),
Expression.Quote(
Expression.Lambda(
lambdaExpression.Type,
Expression.AndAlso(
lambdaExpression.Body,
Expression.Equal(
Expression.Modulo(
parameter,
Expression.Constant(
4
)
),
Expression.Constant(
0
)
)
),
lambdaExpression.Parameters
)
)
);
}
}
}