Type error trying to call Any() using Expression<Func<..>> from method - c#

I am creating a reusable mapping expression from my EF entity to my BLL entity:
private Expression<Func<Person, bool>> GetNameFilter(string name)
{
return person => person.Profile.UserName == name;
}
internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
return perk => new Bll.Perk
{
Id = perk.PerkId,
Name = perk.PerkName,
Description = perk.PerkDescription,
//Compilation Error
Owned = perk.Players.Any(GetNameFilter(name))
};
}
And I am getting a compilation error on the noted line. The error reads:
ICollection does not contain a definition for 'Any' and the best extension method overload 'Queryable.Any(IQueryable, Expression>)' requires a receiver of type 'IQueryable'
But this does not happen when I push this expression in directly:
internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
return perk => new Bll.Perk
{
Id = perk.PerkId,
Name = perk.PerkName,
Description = perk.PerkDescription,
//No Compilation Error
Owned = perk.Players.Any(person => person.Profile.UserName == name)
};
}
Why is this happening? The type of both expressions is the same.
Using the LinqExpression solution found below, I am now getting the following error at runtime:
An exception of type 'System.InvalidCastException' occurred in LinqKit.dll but was not handled in user code
Additional information: Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.
internal FutureQuery<Perk> GetPlayerInfoPerks(string username)
{
return Master
.Perks
.VisiblePerks
.Select(Master.Perks.MapPerkPerk(username))
.Future();
}
Is this due to the use of the EntityFramework.Future library?

Why is this happening? The type of both expressions is the same.
The type of both expressions is not the same - they just look visually the same. The following is valid:
dbContext.Persons.Any(GetNameFilter(name))
and this is not:
perk.Players.Any(GetNameFilter(name))
Why? Because the first expects Expression<Func<...>> while the second - just Func<..> (the typical difference between IQueryable<T> and IEnumerable<T> methods with the same name). Hope you see the difference. When you type it directly, the C# compiler does its magic to emit one or the another, but when you do that manually, you are supposed to use the correct one.
The problem is addressed by LinqKit package with Invoke / Expand custom extension methods (and more generally with AsExpandable).
For your concrete example, the LinqKit solution could be like this:
using LinqKit;
...
internal Expression<Func<Ef.Perk, Bll.Perk>> MapPerkPerk(string name)
{
// LinqKit requires expressions to be in variables
var nameFilter = GetNameFilter(name);
return Linq.Expr((Ef.Perk perk) => new Bll.Perk
{
Id = perk.PerkId,
Name = perk.PerkName,
Description = perk.PerkDescription,
Owned = perk.Players.Any(p => nameFilter.Invoke(p))
}).Expand();
}

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?

Explicit cast in c# generic

The non-generic version of CastRangeAndAdd works fine. If I skip the (object)
var castedList = list.Select(e => (NewType)e).ToList();
then there is a compile error. If I use the (object) then there is an exception "type TypeHintExcel can not be converted to TypeHint" though the cast operator is there. Any idea? (TypeHint and TypeHintExcel are not related so as I understand the "as keyword" can not be used.)
public static explicit operator TypeHint(TypeHintExcel p)
{
var result = new TypeHint()
{
Id = p.Id,
Hint = p.Hint,
ExternalType = p.ExternalType,
AlienType = p.AlienType,
Weight = p.Weight
};
return result;
}
public static class DbSetExtensions
{
public static void CastRangeAndAdd<OldType, NewType>
(this System.Data.Entity.DbSet<NewType> dbCollection,
List<OldType> list)
where NewType : class
{
var castedList = list.Select(e => (NewType)(object)e).ToList();
dbCollection.AddRange(castedList);
}
}
You can't write a generic method to do this. The explicit conversion operation needs to be bound at compile time, but when that method is compiled it has no idea what the source and destination types are, so it can't possibly be bound to your custom conversion operator.
c# is not as flexible as c++
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/differences-between-cpp-templates-and-csharp-generics
C++ allows code that might not be valid for all type parameters in the template, which is then checked for the specific type used as the type parameter. C# requires code in a class to be written in such a way that it will work with any type that satisfies the constraints. For example, in C++ it is possible to write a function that uses the arithmetic operators + and - on objects of the type parameter, which will produce an error at the time of instantiation of the template with a type that does not support these operators. C# disallows this; the only language constructs allowed are those that can be deduced from the constraints.

how to get data from linq database model

I am trying use LINQ in a C# program to get information from a database. I have found a lot of examples showing basic to advanced queries, but i get a error when i try to build. See basic LINQ example below:
The class with the LINQ
public class StdFy
{
public object GetStdFy(DrillholeEntities ddb)
{
try
{
var myList = ((from t1 in ddb.DTM_QAQC_BLK_STD from t2 in ddb.DTM_STANDARDSASSAY.Where(x=> t1.STANDARDID==x.STANDARDID && t1.ASSAYVALUE==x.STANDARDVALUE)
select new
{
CHECKID = t1.CHECKID,
STANDARDID = t1.STANDARDID,
PRIORITY = t1.ASSAY_PRIORITY,
NAME = t1.ASSAYNAME,
ASSAYVALUE = t1.ASSAYVALUE,
STANDARDVALUE = t2.STANDARDVALUE,
STANDARDDEVIATION = t2.STANDARDDEVIATION,
NORMALIZACION = (t2.STANDARDVALUE- t1.ASSAYVALUE)/ t2.STANDARDDEVIATION,
LABJOBNO = t1.LABJOBNO,
LOADDATE = t1.RETURNDATE
})).OrderBy(x => x.LOADDATE).ToList();
return myList;
}
catch (Exception)
{
throw new NotImplementedException();
}
}
}
and then
DrillholeEntities ddb;
StdFy stdFy = new StdFy();
using (ddb = new DrillholeEntities())
{
IOrderedEnumerable<DTM_QAQC_BLK_STD> datosTodos;
datosTodos = stdFy.GetStdFy(ddb);
}
and when i buuild the project i get the error
Severity Code Description Project File Line Suppression State
Error CS0266 Cannot implicitly convert type 'object' to
'System.Linq.IOrderedEnumerable'.
An explicit conversion exists (are you missing a
cast?) Inspinia_MVC5 C:\Users\chachl9\Documents\Visual Studio
2015\Projects\MVC5_Full_Version\Inspinia_MVC5\Controllers\GraphsController.cs 55 Active
You are returning a list of objects of an anonymous type by
select new { ... }
thats may the reason why you use object as return type of your method.
Than you assign the return value to a specific type. Thats the reason why the exception ist thrown.
To get this work I would implement a new class which contains all your needed properties. Use it in
select new MyNewClass { Property1 = ...}
change the return type of your method from object to IEnumerable<MyNewClass>. Aswell the variable you assign the return value to.
You should also use PascalCase properties as mentioned by Gert Arnold in the comments.
Also mixing the select from in where LINQ syntax with the Lambda methods .Where(x => ...) make it hard to read.

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.

Assign a function delegate that returns an anonymous type to a variable

The code below is valid:
IEnumerable<SomeThing> things = ...;
// map type SomeThing to a new anonymous type, resulting in a strongly typed
// sequence based on an anon type
var newList = things.Select(item =>
{
return new
{
ID = item.ID,
DateUpdatedOrCreated = ((DateTime)(item.DateUpdated ??
item.DateCreated)).ToShortDateString(),
Total = item.Part1 + item.Part2
};
});
newList now appears in Visual Studio as IEnumerable<'a> and is strongly typed with the anonymous type created in the function. That is so cool.
What I can't seem to do is figure out a way to assign just the lambda expression (and not the enumeration) to an implicitly typed variable. Even though the compiler has no problem with the anonymous type in the context above, if I try (say)
var func = (SomeThing item)=> {
return new { ... };
};
I get the error "Cannot assign lambda expression to implicitly-typed local variable". This seems a strange compiler limitation; unless I am missing something, the types are just as non-ambiguous in the 2nd example as they are in the first first: both type parameters are well defined.
Is there any way to do this? Since it's an anonymous type, of course, I don't have any way to use a type to assign it explicitly, so it seems I'd be stuck with making a class for the output type if not.
Update
Shortly after going about my merry way with Jon Skeet's answer, I found a similar dilemma instantiating classes. In case it's not obvious, the same trick can be used to create strongly typed classes using inferred anonymous types.
class Processor<T,U>
{
public Processor(Func<T,U> func) {
}
}
// func is a delegate with anon return type created using method in answer below
var instance = new Processor(func); // does not compile! Requires type arguments!
cannot be created directly, but can be created in much the same way as the trick below:
public static Processor<T,U> Create<T,U>(Func<T,U> func) {
return new Processor<T,U>(func);
}
var instance = Processor.Create(func); // all good
You can do it via type inference:
var func = BuildFunc((SomeThing item) => {
return new { ... };
});
...
static Func<TSource, TResult> BuildFunc<TSource, TResult>(
Func<TSource, TResult> function) {
return function;
}
Note that BuildFunc doesn't really do anything - it just provides the method call needed to get the compiler to do type inference for the generic type arguments for Func<,> - it adds the information that you're interested in Func<,>, basically - that's information which can't be specified as part of a variable declaration, without also specifying the type arguments.

Categories

Resources