Using Expression and Action delegate for decision making structure - c#

I have a instance and I am new to C# delegates , Func , Expression and action . I want to pass a parameter as an expression which would check for few conditions and then it would return or perform based on some conditions provided something like this
var result = Uow.GroupLicense.Set
.Join(Uow.UserGroup.Set, x => x.GroupGuid, y => y.GroupGuid, (GL, G) => new { G, GL })
.Join(Uow.Users.Set, x => x.G.UserGuid, y => y.UserGuid, (UG, U) => new { UG, U })
.SingleOrDefault(x => x.U.UserID == Username);
if (result != null && result.UG.GL.IsActive && result.U.IsEnabled && !result.U.IsLocked)
{
SessionStorage.LoginAttempts = UpdateLoginAttempts(Username, true);
SessionStorage.SessionID = HttpContext.Current.Session.SessionID;
SessionStorage.LoginAttempts = 0;
SessionStorage.UserName = Username;
SessionStorage.ABBUserName = Username;
}
else if (!result.UG.GL.IsActive)
{
result.U.IsEnabled = false;
result.U.IsLocked = true;
Uow.Users.Update(result.U);
Uow.Commit();
ErrorMessage = "User License Expired";
}
else if (result.U.IsLocked)
{
ErrorMessage = "User is locked";
}
else if (!result.U.IsEnabled)
{
ErrorMessage = "User is disabled by administrator";
}
else
{
ErrorMessage = "User doesnt exist ";
}
Now this is my original code . I want to convert this into a decision tree based on two class item conditions . Something like this
if this -> Boolean result = EnsureLicense((x, y) => x.IsEnabled && y.IsActive);
then do Action1
if This -> Boolean result = EnsureLicense((x, y) => !x.IsEnabled && y.IsActive);
then do Action2
if This - > Boolean result = EnsureLicense((x, y) => !x.IsEnabled && y.IsLocked);
then do Action3.
I dont wanna switch it into something like If else structure code with multiple if else and else if structure code . How to proceed with this kind of strategy since I cannot put into switch case .
I want to do it with Actions and Expression and Func delegates . I want to learn how to proceed with this kind of strategy .
Please guide me

Here is solution with actions and Func delegates, which do not use if...else structure. Actually this is a kind of Chain of Responsibility:
public class LicenseHandler
{
private readonly Func<GroupLicense, User, bool> predicate;
private readonly Action action;
private LicenseHandler nextHandler;
public LicenseHandler(Func<GroupLicense,User, bool> predicate, Action action)
{
this.action = action;
this.predicate = predicate;
}
public LicenseHandler SetNext(LicenseHandler handler)
{
nextHandler = handler;
return this;
}
public void Handle(GroupLicense license, User user)
{
if (predicate(license, user))
{
action();
return;
}
if (nextHandler != null)
nextHandler.Handle(license, user);
}
}
Create handlers and chain them:
var handler1 = new LicenseHandler((l, u) => l.IsEnabled && u.IsActive, Action1);
var handler2 = new LicenseHandler((l, u) => !l.IsEnabled && u.IsActive, Action2);
var handler3 = new LicenseHandler((l, u) => !l.IsEnabled && u.IsLocked, Action3);
handler1.SetNext(handler2.SetNext(handler3));
Processing looks like
handler1.Handle(licence, user);
People, who will support delegate-based code instead of simple if-else, accept my deepest sympathy.
UPDATE: If you want nice fluent API, then you can create builder class, which will create handlers chain:
public class LicenseHandlerBuilder
{
private LicenseHandler root;
private LicenseHandler current;
private LicenseHandlerBuilder() { }
public static LicenseHandlerBuilder CreateHandler(
Func<GroupLicense, User, bool> predicate, Action action)
{
var builder = new LicenseHandlerBuilder();
builder.root = new LicenseHandler(predicate, action);
builder.current = builder.root;
return builder;
}
public LicenseHandlerBuilder WithNext(
Func<GroupLicense, User, bool> predicate, Action action)
{
var next = new LicenseHandler(predicate, action);
current.SetNext(next);
current = next;
return this;
}
public LicenseHandler Build()
{
return root;
}
}
Usage:
var handler = LicenseHandlerBuilder
.CreateHandler((l, u) => l.IsEnabled && u.IsActive, Action1)
.WithNext((l, u) => !l.IsEnabled && u.IsActive, Action2)
.WithNext((l, u) => !l.IsEnabled && u.IsLocked, Action3)
.Build();

Related

Passing an array of differently typed Action<T> to a method

I have the following code, which stores a list of actions to perform based on the type of value passed as an argument...
Action<int> intAction = x => Console.WriteLine($"You sent an int: {x}");
Action<bool> boolAction = x => Console.WriteLine($"You sent a bool: {x}");
var funcsByInputType = new Dictionary<Type, Action<object>>();
funcsByInputType[typeof(int)] = (object x) => intAction((int)x);
funcsByInputType[typeof(bool)] = (object x) => boolAction((bool)x);
PerformAction(true);
PerformAction(42);
void PerformAction<TValue>(TValue value)
{
if (funcsByInputType!.TryGetValue(typeof(TValue), out Action<object>? action))
{
action(value!);
}
}
This works as expected.
What I now want to do is create the dictionary from a single method that I only call once, so I can rewrite my code like this instead.
Action<int> intAction = x => Console.WriteLine($"You sent an int: {x}");
Action<bool> boolAction = x => Console.WriteLine($"You sent a bool: {x}");
funcsByInputType = MakeFuncsByInputType(intAction, boolAction);
PerformAction(true);
PerformAction(42);
void PerformAction<TValue>(TValue value)
{
if (funcsByInputType!.TryGetValue(typeof(TValue), out Action<object>? action))
{
action(value!);
}
}
How could I write the MakeFuncsByInputType method?
Note that I don't want to make multiple calls to the method, and I don't want to have my parameters defined as params object[]
This is not possible, because via an Action<object> as an alias of Action<string> I would be able to pass 42 instead of a string.
So I had to go with a builder pattern instead.
public static partial class Reducer
{
public static Builder New() => new Builder();
public class Builder<TState>
{
private bool Built;
private ImmutableArray<KeyValuePair<Type, Func<TState, object, Result<TState>>>> TypesAndReducers;
internal Builder() => TypesAndReducers = ImmutableArray.Create<KeyValuePair<Type, Func<TState, object, Result<TState>>>>();
public Builder<TState> Add<TDelta>(Func<TState, TDelta, Result<TState>> reducer)
{
if (reducer is null)
throw new ArgumentNullException(nameof(reducer));
EnsureNotBuilt();
TypesAndReducers = TypesAndReducers.Add(new(typeof(TDelta), (state, delta) => reducer(state, (TDelta)delta)));
return this;
}
public Func<TState, object, Result<TState>> Build()
{
EnsureNotBuilt();
if (TypesAndReducers.Length == 0)
throw new InvalidOperationException("Must add at least one reducer to build.");
Built = true;
var dictionary = TypesAndReducers
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(x => x.Value));
return (TState state, object delta) =>
{
if (delta is null)
throw new ArgumentNullException(nameof(delta));
if (!dictionary.TryGetValue(delta.GetType(), out var reducers))
return (false, state);
bool anyChanged = false;
TState newState = state;
foreach(var reducer in reducers)
{
(bool changed, newState) = reducer(newState, delta);
anyChanged |= changed;
}
return anyChanged
? (true, newState)
: (false, state);
};
}
private void EnsureNotBuilt()
{
if (Built)
throw new InvalidOperationException("Reducer has already been built.");
}
}
}

Modifying Expression<Func<T, bool>>

I haven't worked with expressions that much so I can't say if my intention makes any sense at all. I have looked around the interwebs and SO to no avail.
Say I have a method like so
public async Task<T> GetFirstWhereAsync(Expression<Func<T, bool>> expression)
{
// this would be a call to 3rd party dependency
return await SomeDataProvider
.Connection
.Table<T>()
.Where(expression)
.FirstOrDefaultAsync();
}
And from my-other-code I could be calling this e.g.
private async Task<User> GetUser(Credentials credentials)
{
return await SomeDataSource
.GetFirstWhereAsync(u => u.UserName.Equals(credentials.UserName));
}
So I'd be receiving first User from my SomeDataProvider that matches the given expression.
My actual question is how would I go about modifying GetFirstWhereAsync so that it would apply some SecretSauce to any expression passed to it? I could do this in caller(s) but that would be ugly and not much fun.
So if I pass in expressions like
u => u.UserName.Equals(credentials.UserName);
p => p.productId == 1;
I'd like these to be modified to
u => u.UserName.Equals(SecretSauce.Apply(credentials.UserName));
p => p.productId == SecrectSauce.Apply(1);
You can modify the expression inside method, but it's a bit complicated and you will need to do it case-by-case basis.
Example below will deal with modification from x => x.Id == 1 to x => x.Id == SecretSauce.Apply(1)
Class
class User
{
public int Id { get; set; }
public string Name { get; set;}
public override string ToString()
{
return $"{Id}: {Name}";
}
}
Sauce
class SquareSauce
{
public static int Apply(int input)
{
// square the number
return input * input;
}
}
Data
User[] user = new[]
{
new User{Id = 1, Name = "One"},
new User{Id = 4, Name = "Four"},
new User{Id = 9, Name = "Nine"}
};
Method
User GetFirstWhere(Expression<Func<User, bool>> predicate)
{
//get expression body
var body = predicate.Body;
//get if body is logical binary (a == b)
if (body.NodeType == ExpressionType.Equal)
{
var b2 = ((BinaryExpression)body);
var rightOp = b2.Right;
// Sauce you want to apply
var methInfo = typeof(SquareSauce).GetMethod("Apply");
// Apply sauce to the right operand
var sauceExpr = Expression.Call(methInfo, rightOp);
// reconstruct equals expression with right operand replaced
// with "sauced" one
body = Expression.Equal(b2.Left, sauceExpr);
// reconstruct lambda expression with new body
predicate = Expression.Lambda<Func<User, bool>>(body, predicate.Parameters);
}
/*
deals with more expression type here using else if
*/
else
{
throw new ArgumentException("predicate invalid");
}
return user
.AsQueryable()
.Where(predicate)
.FirstOrDefault();
}
Usage
Console.WriteLine(GetFirstWhere(x => x.Id == 2).ToString());
The method will turn x => x.Id == 2 to x => x.Id == SquareSauce.Apply(2) and will produce:
4: Four

OrderBy based on list of fields and Asc / Desc rules

I have the following List with OrderBy parameters:
List<String> fields = new List<String> { "+created", "-approved", "+author" }
This would result in the following Linq query:
IQueryable<Post> posts = _context.posts.AsQueryable();
posts = posts
.OrderBy(x => x.Created)
.ThenByDescending(x => x.Approved);
.ThenBy(x => x.Author.Name);
So basically the rules are:
Use the first item in the OrderBy and the others in ThenBy.
Use descending when the field starts with - and ascending when with +.
My idea would be to have something like:
OrderExpression expression = posts
.Add(x => x.Created, "created")
.Add(x => x.Approved, "approved")
.Add(x => x.Author.Name, "author");
So the expression associates the post properties / child properties to each key in fields. Then it would be applied as follows:
posts = posts.OrderBy(expression, fields);
So the OrderBy extension would go through each item in the OrderExpression and apply the rules (1) and (2) to build the query:
posts = posts
.OrderBy(x => x.Created)
.ThenByDescending(x => x.Approved);
.ThenBy(x => x.Author.Name);
How can this be done?
This following class will help you do it. You can find the explanation of code inline.
public static class MyClass
{
public static IQueryable<T> Order<T>(
IQueryable<T> queryable,
List<string> fields,
//We pass LambdaExpression because the selector property type can be anything
Dictionary<string, LambdaExpression> expressions)
{
//Start with input queryable
IQueryable<T> result = queryable;
//Loop through fields
for (int i = 0; i < fields.Count; i++)
{
bool ascending = fields[i][0] == '+';
string field = fields[i].Substring(1);
LambdaExpression expression = expressions[field];
MethodInfo method = null;
//Based on sort order and field index, determine which method to invoke
if (ascending && i == 0)
method = OrderbyMethod;
else if (ascending && i > 0)
method = ThenByMethod;
else if (!ascending && i == 0)
method = OrderbyDescendingMethod;
else
method = ThenByDescendingMethod;
//Invoke appropriate method
result = InvokeQueryableMethod( method, result, expression);
}
return result;
}
//This method can invoke OrderBy or the other methods without
//getting as input the expression return value type
private static IQueryable<T> InvokeQueryableMethod<T>(
MethodInfo methodinfo,
IQueryable<T> queryable,
LambdaExpression expression)
{
var generic_order_by =
methodinfo.MakeGenericMethod(
typeof(T),
expression.ReturnType);
return (IQueryable<T>)generic_order_by.Invoke(
null,
new object[] { queryable, expression });
}
private static readonly MethodInfo OrderbyMethod;
private static readonly MethodInfo OrderbyDescendingMethod;
private static readonly MethodInfo ThenByMethod;
private static readonly MethodInfo ThenByDescendingMethod;
//Here we use reflection to get references to the open generic methods for
//the 4 Queryable methods that we need
static MyClass()
{
OrderbyMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "OrderBy" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
OrderbyDescendingMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "OrderByDescending" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
ThenByMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "ThenBy" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
ThenByDescendingMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "ThenByDescending" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
}
}
Here is an example usage:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name + ", " + Age;
}
}
class Program
{
static void Main(string[] args)
{
List<Person> persons = new List<Person>
{
new Person {Name = "yacoub", Age = 30},
new Person {Name = "yacoub", Age = 32},
new Person {Name = "adam", Age = 30},
new Person {Name = "adam", Age = 33},
};
var query = MyClass.Order(
persons.AsQueryable(),
new List<string> { "+Name", "-Age" },
new Dictionary<string, LambdaExpression>
{
{"Name", (Expression<Func<Person, string>>) (x => x.Name)},
{"Age", (Expression<Func<Person, int>>) (x => x.Age)}
});
var result = query.ToList();
}
}
This answer is the joint effort of #YacoubMassad and me. Take a look at the separate answers for details. The following code works perfectly and even translates to SQL without issues (I checked the query with the answer to this question on a 2008 R2), so all the sorting is done on the server (or wherever you data is, it works with simple lists too of course).
Example usage:
OrderExpression<Post> expression = new OrderExpression<Post>()
.Add(x => x.Created, "created")
.Add(x => x.Approved, "approved")
.Add(x => x.Author.Name, "author");
IQueryable<Post> posts = _context.posts.AsQueryable();
posts = posts.OrderBy(expression, "+created", "-approved", "+author");
// OR
posts = posts.OrderBy(expression, new string[]{"+created", "-approved", "+author"});
// OR
posts = posts.OrderBy(expression, fields.ToArray[]);
And of course a live demo on dotNetFiddle
Code:
public class OrderExpressions<T>
{
private readonly Dictionary<string, LambdaExpression> _mappings =
new Dictionary<string, LambdaExpression>();
public OrderExpressions<T> Add<TKey>(Expression<Func<T, TKey>> expression, string keyword)
{
_mappings.Add(keyword, expression);
return this;
}
public LambdaExpression this[string keyword]
{
get { return _mappings[keyword]; }
}
}
public static class KeywordSearchExtender
{
private static readonly MethodInfo OrderbyMethod;
private static readonly MethodInfo OrderbyDescendingMethod;
private static readonly MethodInfo ThenByMethod;
private static readonly MethodInfo ThenByDescendingMethod;
//Here we use reflection to get references to the open generic methods for
//the 4 Queryable methods that we need
static KeywordSearchExtender()
{
OrderbyMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "OrderBy" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
OrderbyDescendingMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "OrderByDescending" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IQueryable<>), typeof(Expression<>) }));
ThenByMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "ThenBy" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
ThenByDescendingMethod = typeof(Queryable)
.GetMethods()
.First(x => x.Name == "ThenByDescending" &&
x.GetParameters()
.Select(y => y.ParameterType.GetGenericTypeDefinition())
.SequenceEqual(new[] { typeof(IOrderedQueryable<>), typeof(Expression<>) }));
}
//This method can invoke OrderBy or the other methods without
//getting as input the expression return value type
private static IQueryable<T> InvokeQueryableMethod<T>(
MethodInfo methodinfo,
IQueryable<T> queryable,
LambdaExpression expression)
{
var generic_order_by =
methodinfo.MakeGenericMethod(
typeof(T),
expression.ReturnType);
return (IQueryable<T>)generic_order_by.Invoke(
null,
new object[] { queryable, expression });
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> data,
OrderExpressions<T> mapper, params string[] arguments)
{
if (arguments.Length == 0)
throw new ArgumentException(#"You need at least one argument!", "arguments");
List<SortArgument> sorting = arguments.Select(a => new SortArgument(a)).ToList();
IQueryable<T> result = null;
for (int i = 0; i < sorting.Count; i++)
{
SortArgument sort = sorting[i];
LambdaExpression lambda = mapper[sort.Keyword];
if (i == 0)
result = InvokeQueryableMethod(sort.Ascending ?
OrderbyMethod : OrderbyDescendingMethod, data, lambda);
else
result = InvokeQueryableMethod(sort.Ascending ?
ThenByMethod : ThenByDescendingMethod, result, lambda);
}
return result;
}
}
public class SortArgument
{
public SortArgument()
{ }
public SortArgument(string term)
{
if (term.StartsWith("-"))
{
Ascending = false;
Keyword = term.Substring(1);
}
else if (term.StartsWith("+"))
{
Ascending = true;
Keyword = term.Substring(1);
}
else
{
Ascending = true;
Keyword = term;
}
}
public string Keyword { get; set; }
public bool Ascending { get; set; }
}
Edit: Changed Code to closely match your syntax
This code sorts on the client, but works with all IEnumerables. If you absolutely need to sort on the database, take a look at Yacoub's static MyClass() to see how he solved this problem.
The example below is based on the information you provided, you might need to adjust it a bit.
public class DemoClass
{
public DateTime Created { get; set; }
public bool Approved { get; set; }
public Person Author { get; set; }
}
public class Person
{
public string Name { get; set; }
}
Since your example contains author which actually resolves to Author.Name, you need to create some sort of mapping for your keywords (Like you did with your OrderExpression class).
public class OrderExpressions<T>
{
private readonly Dictionary<string,Func<T,object>> _mappings =
new Dictionary<string,Func<T, object>>();
public OrderExpressions<T> Add(Func<T, object> expression, string keyword)
{
_mappings.Add(keyword, expression);
return this;
}
public Func<T, object> this[string keyword]
{
get { return _mappings[keyword]; }
}
}
Which could be used like this:
OrderExpressions<DemoClass> expressions = new OrderExpressions<DemoClass>()
.Add(x => x.Created, "created")
.Add(x => x.Approved, "approved")
.Add(x => x.Author.Name, "author");
You can pass those functions / lambda expressions, directly to Linq and add the next comparison one after another. Start with OrderBy or OrderByDescrending, that will give you your first IOrderedEnumerable and then add all remaining arguments with ThenBy or ThenByDescending.
public static class KeywordSearchExtender
{
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> data,
OrderExpressions<T> mapper, params string[] arguments)
{
if (arguments.Length == 0)
throw new ArgumentException(#"You need at least one argument!", "arguments");
List<SortArgument> sorting = arguments.Select(a => new SortArgument(a)).ToList();
IOrderedEnumerable<T> result = null;
for (int i = 0; i < sorting.Count; i++)
{
SortArgument sort = sorting[i];
Func<T, object> lambda = mapper[sort.Keyword];
if (i == 0)
result = sorting[i].Ascending ?
data.OrderBy(lambda) :
data.OrderByDescending(lambda);
else
result = sorting[i].Ascending ?
result.ThenBy(lambda) :
result.ThenByDescending(lambda);
}
return result;
}
}
public class SortArgument
{
public SortArgument()
{ }
public SortArgument(string term)
{
if (term.StartsWith("-"))
{
Ascending = false;
Keyword = term.Substring(1);
}
else if (term.StartsWith("+"))
{
Ascending = true;
Keyword = term.Substring(1);
}
else
{
Ascending = true;
Keyword = term;
}
}
public string Keyword { get; set; }
public bool Ascending { get; set; }
}
All together it be used like this:
var data = WhateverYouDoToGetYourData();
var expressions = new OrderExpressions<DemoClass>()
.Add(x => x.Created, "created")
.Add(x => x.Approved, "approved")
.Add(x =>x.Author.Name, "author");
var result = data.OrderBy(expressions, "+created", "-approved", "+author");
// OR
var result = data.OrderBy(expressions, fields);
You can find my proof-of-concept on dotNetFiddle.

How to add a non-property rule in FluentValidation?

I have this validator class:
internal class CustomerTypeValidator : AbstractValidator<CustomerType>
{
public CustomerTypeValidator()
{
RuleFor(x => x.Number).Must(BeANumber).WithState(x => CustomerTypeError.NoNumber);
}
private bool BeANumber(string number)
{
int temp;
bool ok = int.TryParse(number, out temp);
return ok && temp > 0;
}
}
And I have the service class:
public class CustomerTypeService
{
public CustomerType Save(CustomerType customerType)
{
ValidationResult results = Validate(customerType);
if (results != null && !results.IsValid)
{
throw new ValidationException<CustomerTypeError>(results.Errors);
}
//Save to DB here....
return customerType;
}
public bool IsNumberUnique(CustomerType customerType)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}
}
However I get the following exception:
Property name could not be automatically determined for expression x => x. Please specify either a custom property name by calling 'WithName'.
Is the above not the correct way to add an extra rule?
With the current version of FluentValidation, it is possible to solve the above problem by doing the following:
public bool IsNumberUnique(CustomerType customerType, int id)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Id).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}

Moq function call with expression

I have the following Interface that is used to DI and IOC webservice clients
public interface IWcfServiceClientProvider <TContract>: IDisposable where TContract: class
{
TResult Execute<TResult>(Expression<Func<TContract, TResult>> expression);
TResult Execute<TResult>(Expression<Func<TContract, TResult>> expression, bool closeConnection = true);
void Execute(Expression<Action<TContract>> expression);
void Execute(Expression<Action<TContract>> expression, bool closeConnection = true);
}
In my test class I have the following:
List<BaseLookup> myList = new List<BaseLookup> {
new BaseLookup { Id =1, Code = "1001"},
new BaseLookup { Id =2, Code = "1002"},
new BaseLookup { Id =3, Code = "1003"}};
In my test method
Mock<IWcfServiceClientProvider<ILookupService>> lookupServiceClinetProvider = new Mock<IWcfServiceClientProvider<ILookupService>>();
var controller = new ElectorSearchController(lookupServiceClinetProvider.Object);
lookupServiceClinetProvider.Setup(mock => mock.Execute(lookup => lookup.GetList(10))).Returns(myList).Verifiable();
var list = controller.testMethod();
lookupServiceClinetProvider.VerifyAll();
list will only have value when the parameter for GetList is set to 10 i.e GetList(10) Not GetList(i) where i=10
The following works
lookupServiceClinetProvider.Setup(mock => mock.Execute(It.IsAny<Expression<Func<ILookupService, List<BaseLookup>>>>(), true )).Returns((List<BaseLookup>)myList).Verifiable();
But I want to mock the call for GetList and not any call to Execute. If that works, then I can filter the values in the Return method
As an interim solution I wrote a simple brute-force solution to compare expressions following:
public static bool ExpressionMatcher<TIn, TResult>(Expression<Func<TIn, TResult>> expr1, Expression<Func<TIn, TResult>> expr2)
{
if (expr1 == null || expr2 == null)
{
return false;
}
if (expr1 == expr2)
{
return true;
}
if (expr1.Type != expr2.Type)
{
return false;
}
if (expr1.Body == expr2.Body)
{
return true;
}
if (expr1.Body.NodeType != expr2.Body.NodeType)
{
return false;
}
if (expr1.Body.NodeType == ExpressionType.Call)
{
dynamic expr1Body = expr1.Body;
dynamic expr2Body = expr2.Body;
if (expr1Body.Method.Name == expr2Body.Method.Name &&
expr1Body.Method.ReturnType == expr2Body.Method.ReturnType)
{
if (expr1Body.Arguments == null && expr2Body.Arguments == null)
{
return true;
}
if (expr1Body.Arguments.Count != expr2Body.Arguments.Count)
{
return false;
}
return true;
}
}
return false;
}
I used the following to call it
Expression<Func<ILookupService, List<BaseLookup>>> expr = lookup => lookup.GetMyList(It.IsAny<long>());
.Setup(mock => mock.Execute(It.Is<Expression<Func<ILookupService, List<BaseLookup>>>>(method => ExpressionMatcher(method, expr))))
.Returns(myList)
.Verifiable();
I don't need to check the arguments' type at this point. If you have a better answer, please let me know

Categories