The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider' - c#

I'm trying to mock some DB access code using simple lists. It generally works except for Async. which causes the error saying the provider does not support Iqueriable. I tries a few suggested solutions but they seem to cause an expression error when executing the linq query
[Fact]
public async Task TestAsyncFromList()
{
List<int> items = new List<int>() { 1,2,3,4,5,6,7,8,9,10};
var queryable = items.AsQueryable();
var totalSync = queryable.Count();
var totalAsync = await queryable.CountAsync(); // The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'
Assert.True(totalSync==totalAsync);
}
I was expecting that there is an 'AsAsyncQueiable' solution
Tried this The provider for the source IQueryable doesn't implement IAsyncQueryProvider - but got Expression error

Related

EF Core Get DBSet by string then use it on a query

I am trying to make a tool that enables a user to select tables/columns from a ui. When the user posts the "query configuration object" to the server, I want to create a dynamic SQL Query using Entity Framework Core
Example
Given var tableName = "Table1";
I want to grab the DbSet<tableName> and then run something like
var dbSet = GetDbSet("Table1");
var stuff = await dbSet.Take(10).ToListAsync();
I'm sure this is possible in some shape/form but all the articles I'm seeing are similar in the approach. Everything compiles, but when I run it, I get Object Refeerence not set to an instance of an object fun.
Here's what I have:
var select = GetDbSet(tableName);
var results = await select.Take(10).ToListAsync();
The GetDbSet method:
This works obviously But, I need to variablize the T (which I can't do??)
private DbSet<Table1> GetDbSet(string name)
{
return _dbContext.Set<Table1>();
}
From what I've read, I created an extension method since I need Set(T) and not Set<T>()
public static IQueryable<object> Set(this DbContext _context, Type t)
{
return (IQueryable<object>) _context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null);
}
This does what I think I'm expecting it to:
private IQueryable GetDbSet(string name)
{
// forcing a type instead of fiddling with the string for simplicity
return _dbContext.Set(_dbContext.Table1.GetType());
}
But, this code won't compile:
var select = GetDbSet(tableName);
var results = await select.Take(10).ToListAsync();
'IQueryable' does not contain a definition for 'Take' and no
accessible extension method 'Take' accepting a first argument of type
'IQueryable' could be found (are you missing a using directive or an
assembly reference?)
Am I even on the right path for grabbing a DbSet and using it to query? Do I need to build extension methods for Take, and any other methods I want to use?

Dynamic Linq Predicate throws "Unsupported Filter" error with C# MongoDB Driver

I have been trying to pass in a dynamic list of Expressions to a MongoDB C# Driver query using Linq ... This method works for me with regular Linq queries against an ORM, for example, but results in an error when applied to a MongoDB query ... (FYI: I am also using LinqKit's PredicateBuilder)
//
// I create a List of Expressions which I can then add individual predicates to on an
// "as-needed" basis.
var filters = new List<Expression<Func<Session, Boolean>>>();
//
// If the Region DropDownList returns a value then add an expression to match it.
// (the WebFormsService is a home built service for extracting data from the various
// WebForms Server Controls... in case you're wondering how it fits in)
if (!String.IsNullOrEmpty(WebFormsService.GetControlValueAsString(this.ddlRegion)))
{
String region = WebFormsService.GetControlValueAsString(this.ddlRegion).ToLower();
filters.Add(e => e.Region.ToLower() == region);
}
//
// If the StartDate has been specified then add an expression to match it.
if (this.StartDate.HasValue)
{
Int64 startTicks = this.StartDate.Value.Ticks;
filters.Add(e => e.StartTimestampTicks >= startTicks);
}
//
// If the EndDate has been specified then add an expression to match it.
if (this.EndDate.HasValue)
{
Int64 endTicks = this.EndDate.Value.Ticks;
filters.Add(e => e.StartTimestampTicks <= endTicks);
}
//
// Pass the Expression list to the method that executes the query
var data = SessionMsgsDbSvc.GetSessionMsgs(filters);
The GetSessionMsgs() method is defined in a Data services class ...
public class SessionMsgsDbSvc
{
public static List<LocationOwnerSessions> GetSessionMsgs(List<Expression<Func<Session, Boolean>>> values)
{
//
// Using the LinqKit PredicateBuilder I simply add the provided expressions
// into a single "AND" expression ...
var predicate = PredicateBuilder.True<Session>();
foreach (var value in values)
{
predicate = predicate.And(value);
}
//
// ... and apply it as I would to any Linq query, in the Where clause.
// Additionally, using the Select clause I project the results into a
// pre-defined data transfer object (DTO) and only the DISTINCT DTOs are returned
var query = ApplCoreMsgDbCtx.Sessions.AsQueryable()
.Where(predicate)
.Select(e => new LocationOwnerSessions
{
AssetNumber = e.AssetNumber,
Owner = e.LocationOwner,
Region = e.Region
})
.Distinct();
var data = query.ToList();
return data;
}
}
Using the LinqKit PredicateBuilder I simply add the provided expressions into a single "AND" expression ... and apply it as I would to any Linq query, in the Where() clause. Additionally, using the Select() clause I project the results into a pre-defined data transfer object (DTO) and only the DISTINCT DTOs are returned.
This technique typically works when I an going against my Telerik ORM Context Entity collections ... but when I run this against the Mongo Document Collection I get the following error ...
Unsupported filter: Invoke(e => (e.Region.ToLower() == "central"),
{document})
There is certainly something going on beneath the covers that I am unclear on. In the C# Driver for MongoDB documentation I found the following NOTE ...
"When projecting scalars, the driver will wrap the scalar into a
document with a generated field name because MongoDB requires that
output from an aggregation pipeline be documents"
But honestly I am not sure what that neccessarily means or if it's related to this problem or not. The appearence of "{document}" in the error suggests that it might be relevant though.
Any additional thoughts or insight would be greatly appreciated though. Been stuck on this for the better part of 2 days now ...
I did find this post but so far am not sure how the accepted solution is much different than what I have done.
I'm coming back to revisit this after 4 years because while my original supposition did work it worked the wrong way which was it was pulling back all the records from Mongo and then filtering them in memory and to compound matters it was making a synchronous call into the database which is always a bad idea.
The magic happens in LinqKit's expand extension method
That flattens the invocation expression tree into something the Mongo driver can understand and thus act upon.
.Where(predicate.Expand())

Abstracting out queries to the Azure Table Storage using Expressions

I'm creating a class to perform CRUD functionality with Azure Table Storage.
I'm using generic types in this.
I have the following method, which i'm trying to pass in an expression to get used in the TableQuery, but am having some issues.
The line TableQuery<T> query = new TableQuery<T>().Where<T>(criteria); won't compile, and gives me the message
Cannot implicitly convert type 'System.Linq.IQueryable<T>'
to 'Microsoft.WindowsAzure.Storage.Table.TableQuery<T>'.
An explicit conversion exists (are you missing a cast?)
I understand the message and know it's telling me i'm missing a cast, though I'm unsure how to code it correctly.
My full method is:
public List<T> GetSome<T>(Expression<Func<T, bool>> criteria) where T : ITableEntity, new()
{
TableQuery<T> query = new TableQuery<T>().Where<T>(criteria); // <---- This line isn't working
List<T> results = table.ExecuteQuery<T>(query).ToList<T>();
return results;
}
Ok, so I figured it out - how i can pass in a lambda expression for Azure Table Storage to use.
I changed my method to the following:
public List<T> GetSome<T>(Expression<Func<T, bool>> criteria) where T : ITableEntity, new()
{
// table, in this case, is my `CloudTable` instance
List<T> results = table.CreateQuery<T>().Where(criteria).ToList();
return results;
}
I can now pass in an expression. For example, to search against a DynamicTableEntity I can use:
// my table storage class
TableStorage ts = new TableStorage("ContactData");
// search with expression
List<DynamicTableEntity> results = ts.GetSome<DynamicTableEntity>(t => t.Properties["FirstName"].StringValue == "Darren");
If this is something you wouldn't/shouldn't do against an Azure Table Store, please do let me know.
In the meantime, this is how I have met the requirement.
The error is because the value returned by the extension method Where is of type IQueryable<T>, but you're assigning to a variable of type TableQuery<T> and there is no implicitly valid type conversion between these types.

How to validate Linq2Object as Linq2Entity queries?

I need my implementation of IObjectSet<T> to be able to validate expressions as the "real" Linq2Entity provider would. Is it possible?
Background:
I've changed my EF model (model-first) to return IObjectSet<T> instead of ObjectSet<T>So that I can fake my model, and have it return the data I want.
Problem:
I have written an implementation of IObejctSet<T> that is bacally a wrapper for a List<T>.
This is all good - but when I query my implementation of IObjectSet<T> im using LinqToObject - so there is no guarantee that I have valid Linq2Entity expression.
Proposed solution:
use the real
In my implementaion i just use List<T>'s provider:
private class IObjectSetUnitTestImplementation<T> : IObjectSet<T> where T : EntityObject
{
private IQueryable _qry;
public IObjectSetUnitTestImplementation(List<T> lst)
{
_inner = lst;
_qry = _inner.AsQueryable();
Expression = _qry.Expression;
ElementType = _qry.ElementType;
Provider = _qry.Provider; //<--should be ObjectSet<T>'s provider
}
//Rest of implementaion omitted
}
So I tried this:
ObjectContext context = null;
var _qry = new ObjectQuery<T>(typeof(T).Name, context) as IQueryable;
Expression = _qry.Expression;
ElementType = _qry.ElementType;
Provider = _qry.Provider;
But I have no context to feed it with, and even if I had, it would just try to access the database.
Is there any other way?
so there is no guarantee that I have valid Linq2Entity expression"
Congratulations, you've just discovered a reason, why IObjectSet<T>, IRepository<T> and etc. shouldn't be implemented for IQyeryable<T>. And the reason is simple: query, which is valid for one LINQ provider, can be invalid for another one (even if you just change databases from MS SQL to something other).
And there's no generic way to verify those queries.
So, unit-testing isn't a good choice for IQyeryable<T>, but integration testing is a definitely the thing that you need.

How to define anonymous method types to build dynamic queries with LINQ?

I'm busy with a LINQ to SQL project that basically creates multiple threads for each entity type in my database, which constantly queries information from the DB in a thread.
Here's a pseudo example:
streamer.DefineDataExpression<Contacts>(x => x.FirstName == "Bob");
while(true)
{
List<Contacts> MyContactsResult = streamer.ResultList;
// do whatever with MyContactsResult
}
The above code doesn't exist, but this is what I have so far for the 'streamer' class (it obviously doesn't work, but you can see what I'm trying to achieve above):
public void DefineExpression(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
using (var db = new LINQDataContext())
{
ResultList = db.GetTable<T>().Where(expression);
}
}
How do I go about creating a method like 'DefineExpression' that will allow me to query a LINQ type dynamically?
Why not use the Dynamic LINQ provider, as mentioned by Scott Guthrie. I think that would give you everything you are looking for, because you can define the query as a string. Therefore, you can more easily build a string representation of your query, and execute on the fly.

Categories

Resources