Reputation: 6120
I have Generic Business Service(BS) class. I get data from DB using this BS class.To get data I send to it Expression.This Expression is created with DTO objects. BS converts it into EF objects with Custome Visitor Class. When I send expression with values (as following)
Expression<Func<UserDT, bool>> t = x => x.Username == "tebo" && x.Password =="ICy5YqxZB1uWSwcVLSNLcA==" && x.Company.Id == new Guid("D88F5A20-5FBE-47FA-B3C0-B7F1241C0D45");
I get data. It works. But if I want to get data with parameters I am getting error as
var psw = "ICy5YqxZB1uWSwcVLSNLcA==";
string username = "tebo";
Expression<Func<UserDT, bool>> t = x => x.Username == username && x.Password == psw && x.Company.Id == new Guid("D88F5A20-5FBE-47FA-B3C0-B7F1241C0D45");
Error:
The operands for operator 'Equal' do not match the parameters of method 'op_Equality'.
What should I do to pass parameters as value?
Thank you.
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq.Expressions;
namespace CXFServer.Net.Test.UTest.MyTest
{
/// <summary>
/// Summary description for StackOverFlow
/// </summary>
[TestClass]
public class StackOverFlow
{
[TestMethod]
public void TestingWithoutParameters()
{
Expression<Func<UserDT, bool>> t = x => x.Username == "tebo" && x.Password == "ICy5YqxZB1uWSwcVLSNLcA==" && x.Company.Id == new Guid("D88F5A20-5FBE-47FA-B3C0-B7F1241C0D45");
var expectedUserDt = new UserBS().Login(t);
Assert.IsTrue(expectedUserDt);
}
[TestMethod]
public void TestingWithParameters()
{
var psw = "ICy5YqxZB1uWSwcVLSNLcA==";
string username = "tebo";
Expression<Func<UserDT, bool>> t = x => x.Username == username && x.Password == psw && x.Company.Id == new Guid("D88F5A20-5FBE-47FA-B3C0-B7F1241C0D45");
var expectedUserDt = new UserBS().Login(t);
Assert.IsTrue(expectedUserDt);
}
}
class User
{
public System.Guid UserId { get; set; }
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
public string UserEmail { get; set; }
public string UserUsername { get; set; }
public string UserPassword { get; set; }
public Nullable<int> UserRole { get; set; }
public Guid User_CompanyId { get; set; }
public Nullable<System.DateTime> UserModifyDate { get; set; }
public Nullable<bool> UserStatus { get; set; }
}
class UserDT
{
public Guid Id
{
get;
set;
}
public bool Status
{
get;
set;
}
public DateTime ModifyDate
{
get;
set;
}
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public string Email
{
get;
set;
}
public string Username
{
get;
set;
}
public string Password
{
get;
set;
}
public int Role
{
get;
set;
}
public CompanyDT Company
{
get;
set;
}
}
class CompanyDT : BaseDT
{
public string Name
{
get;
set;
}
}
class BaseDT
{
public Guid Id
{
get;
set;
}
public bool Status
{
get;
set;
}
public DateTime ModifyDate
{
get;
set;
}
}
class BaseBS<UserDT, User>
{
public bool FindBy(Expression<Func<UserDT, bool>> predicateDt)
{
//parameter that will be used in generated expression
var param = Expression.Parameter(typeof(User));
var visitor = new Visitor<UserDT, User>(param);
var body = visitor.Visit(predicateDt.Body);
//Expression body = new AToBConverter<T, E>(DataTransformation.DataTransformation.QueryMappings).Visit(predicateDt);
Expression<Func<User, bool>> predicate = Expression.Lambda<Func<User, bool>>(body, param);
//Expression<Func<E, bool>> predicate = (Expression<Func<E, bool>>)(body,param);
//IQueryable<User> query = ContextFactory.Retrieve().Set<User>().Where(predicate);
return true;
}
}
class Visitor<T, E> : ExpressionVisitor
{
ParameterExpression _parameter;
//there must be only one instance of parameter expression for each parameter
//there is one so one passed here
public Visitor(ParameterExpression parameter)
{
_parameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
protected override Expression VisitConstant(ConstantExpression node)
{
return base.VisitConstant(node);
}
//protected override visi
//this one is required because PersonData does not implement IPerson and it finds
//property in PersonData with the same name as the one referenced in expression
//and declared on IPerson
protected override Expression VisitMember(MemberExpression node)
{
if (node.Type.Name.IndexOf("DT") > -1)
{
return Visit(node.Expression);
}
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
{
return Visit(node.Expression);
}
//throw new NotImplementedException();
string memberName = string.Empty;
if (node.Type.Name.IndexOf("DT") > -1)
{
}
else
{
if (node.Expression.Type.Name.IndexOf("DT") > 0 && node.Expression.Type.Name.Substring(0, node.Expression.Type.Name.IndexOf("DT")) != typeof(E).Name)
{
string type = node.Expression.Type.Name.Substring(0, node.Expression.Type.Name.IndexOf("DT"));
memberName = typeof(E).Name + "_" + type + "Id";
}
else
{
memberName = typeof(E).Name + node.Member.Name;
}
}
//name of a member referenced in original expression in your
//sample Id in mine Prop
//var memberName = node.Member.Name;
//find property on type T (=PersonData) by name
var otherMember = typeof(E).GetProperty(memberName);
//visit left side of this expression p.Id this would be p
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
}
class UserBS : BaseBS<UserDT, User>
{
public UserBS()
: base()
{
}
public bool Login(Expression<Func<UserDT, bool>> predicateDt)
{
return FindBy(predicateDt);
}
}
}
Upvotes: 1
Views: 389
Reputation: 205559
It's hard to follow the logic of your VisitMember
, but the main issue is the following branch:
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
{
return Visit(node.Expression);
}
It means that if you have obj.field
, it will be converted to obj
, which is incorrect.
When you use constants, the code never reaches that condition, so that's why it is working. However, when the expression is using captured variables (closure), it does and that's why it doesn't work.
The fix is to correctly handle the case by calling the base implementation:
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
{
return base.VisitMember(node);
}
Upvotes: 2