Translate Queryable<T> back to IMongoQuery - c#

Consider the following code
var q = from e in myCollection.AsQueryable<Entity>() where e.Name == "test" select e;
The actual query is very complex and I don't like building it using QueryBuilder instead of LINQ.
So I want to convert it back to IMongoQuery to use in myCollection.Group() call since there is no GroupBy support through LINQ.
Is it possible?

Edited answer:
I realized that there already is an official way to get the Mongo query from a LINQ query (I should have known!). You have to downcast the IQueryable<T> to a MongoQueryable<T> to get access to the GetMongoQuery method:
var linqQuery = from e in collection.AsQueryable<Entity>() where e.Name == "test" select e;
var mongoQuery = ((MongoQueryable<Entity>)linqQuery).GetMongoQuery();
Original answer:
At the moment there is no officially supported way to do that, but in the near future we do intend to make it easy to find out what MongoDB query the LINQ query was mapped to.
In the short term you could use the following undocumented internal methods to find out what MongoDB query the LINQ query is mapped to:
var linqQuery = from e in collection.AsQueryable<Entity>() where e.Name == "test" select e;
var translatedQuery = (SelectQuery)MongoQueryTranslator.Translate(linqQuery);
var mongoQuery = translatedQuery.BuildQuery();
But at some point you might need to switch from these undocumented methods to officially supported methods (the undocumented methods might change or be renamed in the future).

A quick extension based on Robert Stam's answer:
public static IMongoQuery ToMongoQuery<T>(this IQueryable<T> linqQuery)
{
var mongoQuery = ((MongoQueryable<T>)linqQuery).GetMongoQuery();
return mongoQuery;
}
public static WriteConcernResult Delete<T>(this MongoCollection<T> col, IQueryable<T> linqQuery)
{
return col.Remove(linqQuery.ToMongoQuery());
}
public static WriteConcernResult Delete<T>(this MongoCollection<T> col, Expression<System.Func<T, bool>> predicate)
{
return col.Remove(col.AsQueryable<T>().Where(predicate).ToMongoQuery());
}
example:
myCollection.Remove(myCollection.AsQueryable().Where(x => x.Id == id).ToMongoQuery());
myCollection.Delete(myCollection.AsQueryable().Where(x => x.Id == id));
myCollection.Delete(x => x.Id == id);

Related

How to add new where condition in IQueryable when selecting a view model?

I am fairly new to C# and .Net, so apologies if something doesn't make sense, I will try my best to explain my problem.
I have two methods which are basically going to use the similar query but with slight differences. So instead of repeating the query in both methods I created a third private method which will return the common part of the query and then the functions can add more clauses in query as they require.
Here is a generic function which returns the IQueryable object with common part of the query
private IQueryable<OfferViewModel> GetOffersQueryForSeller(int sellerId)
{
return Db.Offers
.Where(o => o.Sku.SellerId == sellerId && o.IsActive && !o.IsDiscontinued)
.Select(o => new OfferViewModel
{
Id = o.Id,
Name = o.Sku.Name,
ImageUrl = o.Sku.ImageUrl ?? o.Sku.Upcq.Upc.ImageUrl,
QuantityName = o.Sku.QuantityName
});
}
Following are the two method which are reusing the IQueryable object
public async Task<List<OfferViewModel>> GetSellerOffers(int sellerId)
{
var query = GetOffersQueryForSeller(sellerId);
return await query.ToListAsync();
}
public async Task<List<OfferViewModel>> GetDowngradableSellerOffers(int sellerId)
{
var query = GetOffersQueryForSeller(sellerId);
return await query
.Where(o => o.Sku.Id == monthlySkuId)
.ToListAsync();
}
Now GetSellerOffers works just fine but GetDowngradableSellerOffers throws a run time error with message The specified type member 'Sku' is not supported in LINQ to Entities.. I asked around and one of the guys told me that I cannot add additional where after adding a select which uses a ViewModel because then my records will be mapped to ViewModel and LINQ will attempt to look up props of ViewModel instead of database columns.
Now I have two questions,
In the docs I read Entity Framework will only run query when I try to fetch the results with methods like ToList and if I haven't done that why it wouldn't allow me to apply conditions on database fields/
How can I reuse the common query in my scenario?
How about the following code:
(The type Offer should be replaced by the type of the Elements that Db.Offers holds)
private IQueryable<OfferViewModel> GetOffersQueryForSeller(int sellerId, Func<Offer,bool> whereExtension)
{
return Db.Offers
.Where(o => ... && whereExtension.Invoke(o))
.Select(o => new OfferViewModel { ... });
}
private IQueryable<OfferViewModel> GetOffersQueryForSeller(int sellerId)
{
return GetOffersQueryForSeller(sellerId, (o) => true);
}
And then call it in GetDowngradableSellerOffers like this:
public async Task<List<OfferViewModel>> GetDowngradableSellerOffers(int sellerId)
{
var query = GetOffersQueryForSeller(sellerId, (o) => o.Sku.Id == monthlySkuId);
return await query.ToListAsync();
}

Convert Method to Linq Expression for query

In our application we want to have standard methods for various conditions in our database. For instance, we have different types of transactions, and we want to create standard methods for retrieving them within other queries. However, this gives us the error:
Method '' has no supported translation to SQL
The method might look like this:
public static bool IsDividend(this TransactionLog tl)
{
return tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
}
To be used as such:
var dividends = ctx.TransactionLogs.Where(x => x.IsDividend());
Of course, if I copy the logic from IsDividend() into the Where clause, this works fine, but I end up duplicating this logic many places and is hard to track down if that logic changes.
I think if I would convert this to an expression like this it would work, but this is not as preferable a setup as being able to use methods:
public Expression<Func<TransactionLog, bool>> IsDividend = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
var dividends = ctx.TransactionLogs.Where(IsDividend);
Is there a way to force Linq to evaluate the method as an expression? Or to "transform" the method call into an expression within a linq query? Something like this:
var dividends = ctx.TransactionLogs.Where(tl => ToExpression(tl.IsDividend));
We are using Linq-to-SQL in our application.
Well having static property containing the expressions seems fine to me.
The only way to make it work with Methods would be to create a method which returns this expression, and then call it inside where:
public class TransactionLog
{
Expression<Func<TransactionLog, bool>> IsDividend() {
Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
return expression;
}
}
public class TransactionLogExtension
{
Expression<Func<TransactionLog, bool>> IsDividend(this TransactionLog log) {
Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
return expression;
}
}
and use it via
var dividends = ctx.TransactionLogs.Where(TransactionLog.IsDividend());
or as extension method
var dividends = ctx.TransactionLogs.Where(x.IsDividend());
But none of it is will work with var dividends = ctx.TransactionLogs.Where(x => x.IsDividend()); because x => x.IsDividend(); itself is an expression tree and your database provider can't translate "IsDividend" into an SQL statement.
But the other two options will at least allow you to pass in parameters (which doesn't work if you store the Expressions as instance or static properties).
I think that LINQ to SQL doesn't fully supports even common and build-in functions. (At least EF does not do it). And moreover - when it deals with user defined methods. I predict that your variant with expression will fall as well as the variant with method call unless you call it after enumeration (ToList or similar method). I suggest to keep the balance between 1) stored procedures at server, 2) common conditions hardcoded in Where clauses in C#, 3) expression trees generation in C#. All these points are relatively complex, for sure (and to my mind there is no easy and general solution).
Try using Extension Methods, like so:
public static class TransactionLogExtensions {
public static IQueryable<TransactionLog> OnlyDividends(this IQueryable<TransactionLog> tl)
{
return tl.Where(t=>t.SourceTypeID == (int)JobType.Dividend || t.SourceTypeID == (int)JobType.DividendAcct);
}
}
Call it like so:
var dividends=db.TransactionLogs.OnlyDividends();
or
var dividends=db.TransactionLogs.OnlyDividends().OrderBy(...);
For this scenario you can use Func. Linq works very good with those.
Here is the simple example of using Func in Linq query. Feel free to modify and use.
Func<int,bool> isDivident = x => 3==x;
int[] values = { 3, 7, 10 };
var result = values.Select (isDivident );

C# Method to utilise linq expression

I have a number of similar methods that contain linq queries - here's an example. The only difference is the .Where clause fed by the param.
public Supplier FindAny(int ID)
{
return CompareView.Select()
.Where(p => p.Supplier.ID == ID)
.Select(p => p.Supplier)
.FirstOrDefault();
}
I'm looking to see whether the code can be slimmed down through the use of expressions. To date I've found a few examples of expressions in use, but nothing that has helped me convert the code successfully.
Firstly, it would be good to know if expressions can be utilised with this style of code, and if it can, any pointers would be appreciated.
A straight forward translation would be:
public Supplier FindAny(Func<WhateverTypePIs, bool> func)
{
return CompareView.Select()
.Where(func)
.Select(p => p.Supplier)
.FirstOrDefault();
}
..which would allow this:
var supplier = FindAny(p => p.Supplier.ID == ID);
Note: You'll have to fill in the WhateverTypePIs generic type .. as that isn't shown in your original question (it's whatever p's type is in your current expression).
Does this help:
public Supplier FindAny(Func<WhateverTypePIs, bool> func)
{
return CompareView.FirstOrDefault(func).Supplier
}

Internal .NET Framework Data Provider error 1025

IQueryable<Organization> query = context.Organizations;
Func<Reservation, bool> predicate = r => !r.IsDeleted;
query.Select(o => new {
Reservations = o.Reservations.Where(predicate)
}).ToList();
this query throws "Internal .NET Framework Data Provider error 1025" exception but the query below does not.
query.Select(o => new {
Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();
I need to use the first one because I need to check a few if statements for constructing the right predicate. I know that I can not use if statements in this circumstance that is why I pass a delegate as parameter.
How can I make the first query work?
While the other answers are true, note that when trying to use it after a select statement one has to call AsQueryable() explicitly, otherwise the compiler will assume that we are trying to use IEnumerable methods, which expect a Func and not Expression<Func>.
This was probably the issue of the original poster, as otherwise the compiler will complain most of the time that it is looking for Expression<Func> and not Func.
Demo:
The following will fail:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).Any(expr))
.Load()
While the following will work:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
.Load()
After creating the bounty (rats!), I found this answer, which solved my problem. (My problem involved a .Any() call, which is a little more complicated than this question...)
In short, here's your answer:
IQueryable<Organization> query = context.Organizations;
Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;
query.Select(o => new { Reservations = o.Reservations.Where(expr) })
.ToList();
Read the referenced answer for an explanation of why you need the local variable expr, and you can't directly reference another method of return type Expression<Func<Reservation, bool>>.
Thanks for pinging me. I guess I was on the right track after all.
Anyway, to reiterate, LINQ to Entities (thanks to Jon Skeet for correcting me when I got mixed up in my own thought process in the comments) operates on Expression Trees; it allows for a projection to translate the lambda expression to SQL by the QueryProvider.
Regular Func<> works well for LINQ to Objects.
So in this case, when you're using the Entity Framework, any predicate passed to the EF's IQueryable has to be the Expression<Func<>>.
I just experienced this issue in a different scenario.
I have a static class full of Expression predicates which I can then combine or pass to an EF query. One of them was:
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(statuses.Contains);
}
This was throwing the 1025 error due to the Contains method group call. The entity framework expected an Expression and found a method group, which resulted in the error. Converting the code to use a lambda (which can be implicitly cast to an Expression) fixed the error
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(x => statuses.Contains(x));
}
Aside: I then simplified the expression to ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));
Had a similar problem. Library of ViewModels that look like this:
public class TagViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
{
Id = t.Id,
Name = t.Name,
};
This works:
var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
.ToArrayAsync();
But, this won't compile:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
})
.ToArrayAsync();
Because the second .Select is a mess - the first one is actually called off of an ICollection, which is not IQueryable, so it consumes that first Expression as a plain Func, not Expression<Func.... That returns IEnumerable<..., as discussed on this page. So .AsQueryable() to the rescue:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
But that creates a new, weirder problem: Either I get Internal Framework...Error 1025, or I get the post variable with a fully loaded .Post property, but the .Tags property has an EF proxy object that seems to be used for Lazy-Loading.
The solution is to control the return type of Tags, by ending use of the Anonymous class:
public class PostViewModel
{
public Post Post { get; set; }
public IEnumerable<TagViewModel> Tags { get; set; }
Now select into this and it all works:
var post = await db.Posts.Take(10)
.Select(p => new PostViewModel {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();

LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression

I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
The problem arises because ToString() isn't really executed, it is turned into a MethodGroup and then parsed and translated to SQL. Since there is no ToString() equivalent, the expression fails.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.

Categories