Stumped on Entity Framework & Lambda Expressions - entity-framework

I know currently the compiler is not liking this statement. Getting Error
Cannot convert lambda expression to delegate type 'System.Func<MyData.Models.SomeModels,bool>' because some of the return types in the block are not implicitly convertible to the delegate return type
My Statement I'm passing to my Repository Class
var qry = repositoryClass.Find(c => c.Categories.Where(d => d.CategoryParentID == typeID));
Repository Class Find Method
public IEnumerable<SomeModels> Find(Func<SomeModels, bool> exp)
{
return (from col in _db.SomeModels where exp select col);
}

To work with EF you need an Expression<...>, applied (as a predicate) with Where:
public IEnumerable<SomeModels> Find(Expression<Func<SomeModels, bool>> exp)
{
return _db.SomeModels.Where(exp);
}
You'd then call that as:
var qry = repositoryClass.Find(c => c.CategoryParentID == typeID);
The lambda is then translated into an Expression<...>.
If your setup is more complex, please clarify.

I just added a method in my Repository Class
public IEnumerable<Models> GetByCategory(int categoryID)
{
var qry = _db.ModelCategories.Where(p => p.CategoryID == categoryID).First();
qry.Models.Load();
return qry.Models;
}
I'm guessing because it needs to be loaded this is the best way to go.

Related

InvalidOperationException in where clause when attempting to create projection with Compile/Invoke

I've got a model and I want to build a simplified query model on top of it using projections.
Here we have 2 related 'derived' classes.
note - in the 2nd class ES_PROGRAMGUIDEDAYSCHEDULE, there are 2 implementations of of the projection, the uncommented explicit one works, the commented one out fails.
class ESP_CHANNEL
{
public string? name { get; set; }
public static Expression<Func<Channel, ESP_CHANNEL>> Projection
{
get
{
return x => new ESP_CHANNEL
{
name = x.LotIdLeafoftreepartNavigation.Leaf
};
}
}
}
class ES_PROGRAMGUIDEDAYSCHEDULE
{
public ESP_CHANNEL? ds_channel { get; set; }
public DateTime? ds_date { get; set; }
public static IQueryable<ES_PROGRAMGUIDEDAYSCHEDULE> Query(ModelContext context)
{
return context.Pgprogramguidedayschedules.Select(ES_PROGRAMGUIDEDAYSCHEDULE.Projection);
}
private static Expression<Func<Pgprogramguidedayschedule, ES_PROGRAMGUIDEDAYSCHEDULE>> Projection
{
get
{
return x => new ES_PROGRAMGUIDEDAYSCHEDULE
{
ds_date = x.PgdsIdDayscheduleNavigation.DsTxdate,
// If I explicitly instantiate an ESP_CHANNEL it works
ds_channel = new ESP_CHANNEL
{
name = x.PgdsIdDayscheduleNavigation.DsIdScheduleNavigation.SchIdChannelNavigation.LotIdLeafoftreepartNavigation.Leaf
}
// note THIS seemingly equivalent implementation via compile/invoke fails
//ds_channel = ESP_CHANNEL.Projection.Compile().Invoke(x.PgdsIdDayscheduleNavigation.DsIdScheduleNavigation.SchIdChannelNavigation)
};
}
}
}
If I then run this query like this.
var x =
(from schedule in ES_PROGRAMGUIDEDAYSCHEDULE.Query(ds)
where schedule.ds_channel.name == channel
&& schedule.ds_date == dt
select schedule).Take(1);
var ys = x.ToArray();
the explicit implementation in ES_PROGRAMGUIDEDAYSCHEDULE.Projection, works
ds_channel = new ESP_CHANNEL
{
name = x.PgdsIdDayscheduleNavigation.DsIdScheduleNavigation.SchIdChannelNavigation.LotIdLeafoftreepartNavigation.Leaf
}
whilst the seemingly equivalent implemention, where the instantiation of the ESP_CHANNEL is held in a the projection expression fails
ds_channel = ESP_CHANNEL.Projection.Compile().Invoke(x.PgdsIdDayscheduleNavigation.DsIdScheduleNavigation.SchIdChannelNavigation)
fails with
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll
The LINQ expression 'DbSet<Pgprogramguidedayschedule>()
.LeftJoin(
inner: DbSet<Wondayschedule>(),
outerKeySelector: p => EF.Property<decimal?>(p, "PgdsIdDayschedule"),
innerKeySelector: w => EF.Property<decimal?>(w, "Oid"),
resultSelector: (o, i) => new TransparentIdentifier<Pgprogramguidedayschedule, Wondayschedule>(
Outer = o,
Inner = i
))
.LeftJoin(
inner: DbSet<Psischedule>(),
outerKeySelector: p => EF.Property<decimal?>(p.Inner, "DsIdSchedule"),
innerKeySelector: p0 => EF.Property<decimal?>(p0, "Oid"),
resultSelector: (o, i) => new TransparentIdentifier<TransparentIdentifier<Pgprogramguidedayschedule, Wondayschedule>, Psischedule>(
Outer = o,
Inner = i
))
.LeftJoin(
inner: DbSet<Channel>(),
outerKeySelector: p => EF.Property<decimal?>(p.Inner, "SchIdChannel"),
innerKeySelector: c => EF.Property<decimal?>(c, "LotIdLeafoftreepart"),
resultSelector: (o, i) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<Pgprogramguidedayschedule, Wondayschedule>, Psischedule>, Channel>(
Outer = o,
Inner = i
))
.Where(p => __Compile_0.Invoke(p.Inner).name == __channel_1 && p.Outer.Outer.Inner.DsTxdate == __dt_2)' 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 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
any ideas?
(the problem appears to be that its the where clause that fails to interpret the invoke correctly).
EDIT 1
note his query DOES execute correctly.
var x =
(from schedule in ES_PROGRAMGUIDEDAYSCHEDULE.Query(ds)
//where schedule.ds_channel.name == channel
//&& schedule.ds_date == dt
select schedule).Take(1);
so it appears that it is the where clause navigating via ds_channel that is causing the issue.
You can use LINQKit to make this query working. It needs just configuring DbContextOptions:
builder
.UseSqlServer(connectionString) // or any other provider
.WithExpressionExpanding(); // enabling LINQKit extension
Then you can inject your projection using LINQKit's Invoke method (but possible your query will be corrected also)
ds_channel = ESP_CHANNEL.Projection.Invoke(x.PgdsIdDayscheduleNavigation.DsIdScheduleNavigation.SchIdChannelNavigation)
Also you may find helpful this answer. It shows how to hide expression magic from end user.

Generic Func needed to perform sorting of Entity Framework collection

I have a grid with columns. When the grid column header is selected I post/ajax to server with header selected to return x rows.
In the following code, RefNo is integer while ProposalSectionNumber is a string.
How to make a generic function taking a string but return a Func to be used in the linq statement?
if (sort.dir == SortDirection.Asc)
{
switch (sort.field)
{
case "RefNo":
qry = qry.OrderBy(x => x.RefNo);
break;
case "ProposalSectionNumber":
qry = qry.OrderBy(x => x.ProposalSectionNumber);
break;
}
}
else
{
switch (sort.field)
{
case "RefNo":
qry = qry.OrderByDescending(x => x.RefNo);
break;
case "ProposalSectionNumber":
qry = qry.OrderByDescending(x => x.ProposalSectionNumber);
break;
}
}
I would like to do something like
string sortOrder = "RefNo"
var sortfunc = SortFunc(sortOrder)
if (sort.dir == SortDirection.Asc)
{
qry = qry.OrderBy(sortFunc)
}
else
{
qry = qry.OrderByDesc(sortFunc)
}
I have struggled creating the function SortFunc (which returns based on string or integer)
What is the best way to achieve this?
The problem with declaring a type for sortFunc is that it depends on the type of the field by which you sort. If all fields were of the same type, say, all strings, you could use the type of Expression<Func<MyEntity,string>> for your sortFunc variable.
There is another way of removing code duplication when sort fields do not share a common type. Introduce a generic helper method that takes sort order as a parameter, and call it instead of OrderBy/OrderByDescending:
private static IOrderedQueryable<T> AddOrderBy<T,TKey>(
IQueryable<T> orig
, Expression<Func<T,TKey>> selector
, bool isAscending
) {
return isAscending ? orig.OrderBy(selector) : orig.OrderByDescending(selector);
}
Now you can rewrite your code as follows:
var isAscending = (sort.dir == SortDirection.Asc);
switch (sort.field) {
case "RefNo":
qry = qry.AddOrderBy(x => x.RefNo, isAscending);
break;
case "ProposalSectionNumber":
qry = qry.AddOrderBy(x => x.ProposalSectionNumber, isAscending);
break;
}

How to declare instantiation in Haxe macro function

I want to create a macro that generates this code for me:
if (myEntity.get(Attack) == null) myEntity.add(new Attack());
if (myEntity.get(Confused) == null) myEntity.add(new Confused());
if (myEntity.get(Defend) == null) myEntity.add(new Defend());
if (myEntity.get(Offense) == null) myEntity.add(new Offense());
In code I'd like to declare/use it like this:
EntityMacroUtils.addComponents(myEntity, Attack, Confused, Defend, Offense);
The current macro function looks like this:
macro public static function addComponents(entity:ExprOf<Entity>, components:Array<ExprOf<Class<Component>>>):Expr
{
var exprs:Array<Expr> = [];
for (componentClass in components)
{
var instance = macro $e { new $componentClass() }; // problem is here
var expr = macro if ($entity.get($componentClass) == null) $entity.add(instance);
exprs.push(expr);
}
return macro $b{ exprs };
}
This macro function is incorrect, I get the error:
EntityMacroUtils.hx:17: characters 22-43 : Type not found : $componentClass
The problem is I don't know how to define new $componentClass(). How would I solve this?
I also want to avoid to have Type.createInstance in the output code.
One way to programmatically generate instantiation code is by using "old school" enums AST building (compatible Haxe 3.0.1+):
// new pack.age.TheClass()
return {
expr:ENew({name:"TheClass", pack:["pack", "age"], params:[]}, []),
pos:Context.currentPos()
};
An improved syntax using reification is possible:
// new pack.age.TheClass()
var typePath = { name:"TheClass", pack:["pack", "age"], params:[] };
return macro new $typePath();
Now, for a convenient "instantiation helper" function syntax, we need to do some contorsions to extract a type path from the expression we receive in the macro function:
// new Foo(), new pack.Bar(), new pack.age.Baz()
instantiate(Foo, pack.Bar, pack.age.Baz);
macro static function instantiate(list:Array<Expr>)
{
var news = [for (what in list) {
var tp = makeTypePath(what);
macro new $tp();
}];
return macro $b{news};
}
#if macro
static function makeTypePath(of:Expr, ?path:Array<String>):TypePath
{
switch (of.expr)
{
case EConst(CIdent(name)):
if (path != null) {
path.unshift(name);
name = path.pop();
}
else path = [];
return { name:name, pack:path, params:[] };
case EField(e, field):
if (path == null) path = [field];
else path.unshift(field);
return makeTypePath(e, path);
default:
throw "nope";
}
}
#end
In case anyone is in need for answers, I got this Thanks to ousado on the Haxe IRC chat:
If you do it in macro alone you can do this:
var ct = macro : pack.age.SomeTypename;
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; }
var expr = macro new $tp();
..or, if you explicitly construct tp:
var tp = {sub:'SomeTypeName',params:[],pack:['pack','age'],name:"SomeModuleName"}
As you can see, the complex type path is explicitly given here.
Unfortunately, Haxe don't really have a concise syntax for types in expression positions. You can pass ( _ : TypeName ) to provide an expression that contains a ComplexType.
But if you want to pass a type as argument, you could do it like this:
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TMacroNew {
macro static function instances( arr:Array<Expr> ) {
var news = [for (e in arr) {
var ct = switch e.expr { case EParenthesis({expr:ECheckType(_,ct)}):ct; case _: throw "nope"; };
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; };
macro new $tp();
}];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:Thing), (_:Thing), (_:OtherThing) );
}
}
..if you want a list of types, you might want to go for a parameter list like ( _ : L< One,Two,Three> ).
The accepted answer is problematic because it breaks when type parameters are involved, or when support for non-nominal types should be included.
I updated the example using two alternatives for a more concise notation for the list of types, while still allowing syntax for actual types.
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TPThing<T>{
public function new(){}
}
class TMacroNew {
macro static function instances( e:Expr ) {
var tps = switch e.expr {
case EParenthesis({expr:ECheckType(_,TPath({params:tps}))}):tps;
case ENew({params:tps},_):tps;
case _: throw "not supported";
}
var type_paths = [ for (tp in tps) switch tp {
case TPType(TPath(tp)):tp;
case _: throw "not supported";
}];
var news = [for (tp in type_paths) macro new $tp()];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:L<Thing,Thing,OtherThing,TPThing<Int>> ) );
instances( new L<Thing,Thing,OtherThing,TPThing<Int>>() );
}
}
Edit:
The L in L< ... > could be any valid type name. Its only purpose is allowing to write a comma-separated list of types in valid syntax. Since macro functions take expressions as arguments, we have to use an expression that allows/requires a type, like: ( _ :T ), new T(), var v:T, function(_:T):T {}.

EF6 Convert IQueryable to DbQuery

In Entity framework 6, when running the overload of the Include method that uses lambda expression to the context:
Context.SomeEntity.Include(x => x.MyOtherEntity))
it returns an IQueryable, whereas when we use the one that uses string:
Context.SomeEntity.Include("MyOtherEntity")
it returns a DbQuery.
I need to return a DbQuery and don't want to use the string overload so that I can get inclusion errors at compile time.
How can I return a DbQuery after using the include with the lambda?
I believe you can't convert an IQueryable to a DbQuery. However, you can use this code to pass in an expression and get the required string.
Be sure to write some unit tests for this and adapt the method for your needs. I have not tested it properly yet.
public static string GetMemberName<T>(this Expression<Func<T>> expression)
{
MemberExpression memberExp;
if (!TryFindMemberExpression(expression.Body, out memberExp))
return string.Empty;
var memberNames = new Stack<string>();
do
{
memberNames.Push(memberExp.Member.Name);
}
while (TryFindMemberExpression(memberExp.Expression, out memberExp));
return string.Join(".", memberNames.ToArray());
}
private static bool TryFindMemberExpression(Expression exp, out MemberExpression memberExp)
{
memberExp = exp as MemberExpression;
if (memberExp != null)
{
return true;
}
if (IsConversion(exp) && exp is UnaryExpression)
{
memberExp = ((UnaryExpression)exp).Operand as MemberExpression;
if (memberExp != null)
{
return true;
}
}
return false;
}
private static bool IsConversion(Expression exp)
{
return (exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked);
}

How to create and return an Expression<Func

I Use entity Framework 4.
I would like to be able to create a function that return an Expression func that will be use in a lambda expression.
var ViewModel = _db.Suppliers.Select(model => new {
model,SupType = model.SupplierType.SupplierTypeTexts.Where( st => st.LangID == 1)
});
I would like to make this call like that
var ViewModel = _db.Suppliers.Select(model => new {
model,SupType = model.SupplierType.GetText()
});
My Partial class is:
public partial class SupplierType
{
public Expression<Func<SupplierTypeText, bool>> GetText()
{
return p => p.LangID == 1;
}
How can i perform this.
Easy. For example, Let's assume you have a Product table that is mapped to Products EntitySet in your context, now you want to pass a predicate and select a Product:
Expression<Func<Product, bool>> GetPredicate(int id) {
return (p => p.ProductID == id);
}
You can call GetPredicate() with a Product ID to filter based on that:
var query = ctx.Products.Where(GetPredicate(1)).First();
The point really is that you can always pass a Lambda Expression to where an Expression<T> is needed.
EDIT:
You should change your code like this:
var ViewModel = _db.Suppliers.Select(model => new {
model,
SupType = model.SupplierType.SupplierTypeTexts.Where(GetText())
});
public Expression<Func<SupplierTypeText, bool>> GetText() {
return (stt => stt.LangID == 1);
}
If you want to dynamically create compiled Expression at runtime (as opposed to ones hardcoded against a particular data model at compile time) you need to use the static methods on the Expression class.