Use Lambda expression to find all objects with matching id - c#

I'm working with Lambda expressions for the first time and trying to understand how to do this. I'm following a pattern that exists already in this test file, but my call has an extra layer of complexity in that I need to look inside each of the objects to select all that have an id that will be provided at the time of the test.
myobject.cs
public class myObject
{
public myObject()
{
this.id = Guid.Empty;
this.poolId = Guid.Empty;
this.name = string.Empty;
this.URL = string.Empty;
}
public Guid id { get; set; }
public Guid poolId { get; set; }
public string name { get; set; }
public string URL { get; set; }
}
testfile.cs
Mock<IMyObjectRepository> mock = new Mock<IMyObjectRepository>(MockBehavior.Strict);
List<myObject> objects = new List<myObject>();
mock.Setup(r => r.LoadByPoolId(It.IsAny<Guid>()))
.Returns<IEnumerable<myObject>>(objList => objects.Where(obj => objList.Contains(obj.id));
The problem is, this only searches surface level objects, it does not search the properties of the objects. What's the piece I'm missing to select all myObjects with a matching id?
The repository interface
public interface IMyObjectRepository
{
void Put(myObject object)
void Delete(Guid appId);
myObject Load(Guid appId)
IEnumerable<myObject> LoadByPoolId(Guid poolId);
}

Using provided example
var mock = new Mock<IMyObjectRepository>();
var objects = new List<myObject>();
//...populate objects
// access invocation arguments when returning a value
mock.Setup(r => r.LoadByPoolId(It.IsAny<Guid>()))
.Returns((Guid arg) => objects.Where(obj => obj.poolId == arg));
In the Returns expression the provided argument is passed to the expression used to filter the objects list.
Reference : Moq Quickstart

LINQ's Where method accepts a lambda expression that allows you to perform any check you wish. Write an expression that evaluates to true for rows you wish to include and false otherwise.
In your case the expression would be o.id == targetGUID so just insert it into the lambda body:
var filteredList = list.Where(o => o.id == targetGUID);

Related

Is there a way to map Func<T1, bool> to Func<T2, bool>?

So, I was wondering if it possible to do the next thing in c#:
I have a DB model - let's say it is Car:
public class Car {
public string Id {get;set;}
public string Name {get;set}
}
And a DbSet for this type in someDbContext:
public DbSet<Car> Cars {get;set;}
And also I have a CarDto
public class CarDto {
public string Id {get;set;}
public string Name {get;set}
}
And as result we get something like this:
var select = new Func<CarDto, bool>(car => car.Name == "BMW");
// And somehow use this expression for other type Car
someDbContext.Cars.Where(select);
Maybe there is an approach in which I could map these Funcs like this:
var newFunc = mapper.Map<Func<Car, bool>>(select);
Any thoughts?
If you just want to handle rewriting property accesses, you can use an ExpressionVisitor which looks a bit like this:
public class Program
{
public static void Main()
{
Expression<Func<Car, bool>> expr = x => x.Name == "BMW";
var replaced = ReplaceParameter<CarDto>(expr);
}
private static Expression<Func<T, bool>> ReplaceParameter<T>(LambdaExpression expr)
{
if (expr.Parameters.Count != 1)
throw new ArgumentException("Expected 1 parameter", nameof(expr));
var newParameter = Expression.Parameter(typeof(T), expr.Parameters[0].Name);
var visitor = new ParameterReplaceVisitor()
{
Target = expr.Parameters[0],
Replacement = newParameter,
};
var rewrittenBody = visitor.Visit(expr.Body);
return Expression.Lambda<Func<T, bool>>(rewrittenBody, newParameter);
}
}
public class ParameterReplaceVisitor : ExpressionVisitor
{
public ParameterExpression Target { get; set; }
public ParameterExpression Replacement { get; set; }
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression == this.Target)
{
// Try and find a property with the same name on the target type
var members = this.Replacement.Type.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Public | BindingFlags.Instance);
if (members.Length != 1)
{
throw new ArgumentException($"Unable to find a single member {node.Member.Name} of type {node.Member.MemberType} on {this.Target.Type}");
}
return Expression.MakeMemberAccess(this.Replacement, members[0]);
}
return base.VisitMember(node);
}
}
We need to deconstruct the LambdaExpression into its body and parameters. We need to create a new parameter which has the correct type, and replace all usages of the old parameter with the new one. This is where the visitor comes in: whenever it sees you access a member on the old parameter, it tries to find the corresponding member on the new parameter, and access that instead.
We then construct a new LambdaExpression, using the rewritten body and the new parameter.
You have a whole bunch of options:
Derive your Dto class from the context class. That way you can use polymorphism as normal.
Extract an interface and implement it in both your Dto and context classes. Same as above then, use polymorphism.
Use duck-typing. In C#, that's done with the dynamic keyword. You lose Intellisense and compile-time error checking, but your code will work.
Reflection. It's a lot of code, it's slow, it's practically a much worse version of #3, but you can cobble it together if you really try.
Something like Automapper will help you map your context to your Dto piece-wise, but it won't help you translate your lambda function filters.

Filter a list of objects given a single object

I want a method that accepts a list of objects, and then the 'filter object' (which is the same type as the list of objects). I'm able to do it (inefficiently) on a small scale but also quite fixed - I'd like it to be a generic method so I can pass in the type so it can apply to anything.
Example:
public class Program {
public void Main(string[] args) {
var listOfObjects = new List<MyClass> {
new MyClass { ID = 1, Name = "Object 1" },
new MyClass { ID = 2, Name = "Object 2" },
new MyClass { ID = 3, Name = "Object 2" }
};
var filter = new MyClass { Name = "Object 2" };
// Should return only the first object in the list, since
// the filter's Name = "Object 2", all items in the list
// where the property equals "Object 2" will be filtered out
var filteredList = FilterObjects(listOfObjects, filter);
}
}
public class MyClass {
public int ID { get; set; }
public string Name { get; set; }
}
public class MyTest {
public List<MyClass> FilterObjects(List<MyClass> objects, MyClass filter) {
// first check if the filter is just an empty new instance
// if a user passes in an empty filter then they are not
// filtering anything, therefore
if (filter == new MyClass()) return objects;
var filteredList = new List<MyClass>();
foreach (var item in objects) {
// if this item passes the test for the filter
// (check if any of the properties are equal to the
// filter properties - if so, then this item is not
// is not a match, and we cannot add it to the list)
if (item.ID != filter.ID && item.Name != filter.Name)
filteredList.Add(item);
// What I want is a faster and more consolidated way of
// checking all the properties.
}
return filteredList;
}
}
EDIT: Is there any way to do this also using reflection?
EDIT2: Just wanted to clarify that my example here is just a simple template. I am working with an object that has 20+ properties and would love to not have to make a huge if statement if possible.
EDIT3: I should also mention that the filter object the user passes in can be incomplete, e.g. they can pass in a MyClass object without the ID (just the Name property) because when it reaches my Controller, that MyClass object will automagically fill in ID with a default value. I can check if it is a default value by creating a new instance of MyClass - new MyClass() and for every property, if it equals what the default value would be then ignore that property for filtering because the user didn't want that property filtered. But think of this concept on a larger scale where I have 20+ properties and a user wants to filter out all objects but ONLY wants to use 3 of those properties to filter from. The other 17+ properties will not have an equality check.
It sounds like what you want are generic statements.
It isn't super straightforward but something like this should work:
public static IEnumerable<T> Filter<T>(this IEnumerable<T> results, Filter filter)
{
var types = results.GetType().GetProperties();
foreach (var filter in filter.Filters)
{
Type type = results.GetType();
filter.ColumnName = filter.ColumnName.Replace(" ", "");
var pred = BuildPredicate<T>(filter.ColumnName, filter.FilterValue);
if (filter.ColumnName != null && filter.FilterValue != null)
{
results = results.Where(w =>
{
return w.GetType().GetProperty(filter.ColumnName).GetValue(w, null).ToString().ToLowerInvariant().Contains(filter.FilterValue.ToLowerInvariant());
});
}
}
return results;
}
The filter object looks something like:
public class Filter
{
public string ColumnName {get; set; }
public string Value { get; set; }
//Other properties for Asc / Desc and more
}
And then on any List like List or List you would essentially do:
var results = MyList.Filter(new Filter() { ColumnName = "LastName"; Value = "Smith" });
which gets translated to a Function that if typed manually would look like:
var results = MyList.Where(w => w.LastName == "Smith");
This example however is rough, no type checking for starters.
I would go with a custom IsMatch method:
static bool IsMatch (MyClass toTest, MyClass filter)
{
if (filter.Prop1 != null // or whatever value means "skip this property"
&& filter.Prop1 == toTest.Prop1)
return true;
if (filter.Prop2 != null & filter.Prop2 == toTest.Prop2)
return true;
...
return false;
}
and then let Linq do the lookup for you:
List<MyClass> filtered = listOfObjects.Where(x => !IsMatch(x, filter)).ToList();
A unit test checking (with reflection) that this method is always up to date, checking all properties now and in future can be helpful in not introducing bugs when you add properties to the class
Why not just use methods already part of the System.Generic.Collections library.
var filteredList= new List<MyClass>(listOfObjects);
filteredList.RemoveWhere(n => n.Name == "Object 2");
If you want to use another class as your filter:
MyClass filter = new MyClass() {Name = "Object 2", Id=2 };
var filteredList= new List<MyClass>(listOfObjects);
filteredList.RemoveWhere(n => (n.Name == filter.Name || n.Id == filter.Id)); // you can modify predicate based on whatever you wish to compare

Linq To Entities Where Condition to Expressions comparison

I have 3 classes mapped with Entity Framework
public class A
{
public string Name { get; set; }
}
public class B
{
public string Name { get; set; }
public A A { get; set; }
}
public class C
{
public string Name { get; set; }
}
I have this Linq To Entities Where Condition
return queryableOfB.Where(b => b.A.Name = instanceOfC.Name);
Because this is a repetitive method in my logic, I want to create a method as:
protected void GetFilter<B, TBProperty, TCProperty>(
IQueryable<B> queryofB, C cModel,
Expression<Func<B, TBProperty>> bExpression,
Expression<Func<C, TCProperty>> cExpression)
{
var bExpValue = cExpression.Compile()(cModel);
queryofB.Where(b => b.Property.EndsWith(bExpValue)); // How can I compare two expressions? but adding "for example" an .EndsWith to expression 1?
}
It's important not to pass the .EndsWith in the expression because the decision of using EndsWith, StartsWith, Contains or exact comparison must be done in the method.
Thank you in advance Gurus.
actually where method expect function as predicate. you can try with below one.
its doing nothing just calling the where Extension method. its better to use there where method inline. But telling u the way how can u do it.
static IEnumerable<T1> M<T1>(IEnumerable<T1> objEnumerable, Func<T1, bool> wherePredicate)
{
return objEnumerable.Where(predicate);
}
// calling like
var listOfBObjects = GetBObjectIEnumerable();
C instanceOfC = GetCClassObject();
M<B>(listOfBObjects, b => b.A.Name = instanceOfC.Name);
update
you can use the
M<B>(listOfBObjects, b => b.A.Name.Equals(instanceOfC.Name));
M<B>(listOfBObjects, b => b.A.Name.StartsWith(instanceOfC.Name));
M<B>(listOfBObjects, b => b.A.Name.Contains(instanceOfC.Name));
to improve the performance and all. yes you can use the IQueryable rather than IEnumerable

Limit queried columns based on list set in runtime

I have the following query:
// Type T is constrained to a class that contains "ID" property
// propertiesToQuery is a list constructed based on type T
var set = AppContext.Set<T>();
var result = set.SelectMany(x => propertiesToQuery.Select(p => new { x.ID, Value = x.GetType().GetProperty(p.Name).GetValue(x) })
.Where(p => p.Value != null)
.Select(p => new SearchIndexItem
{
Key = p.Value.ToString(),
Url = Url.Action("Edit", type.Name, new { p.ID }),
Type = type
}));
Now because linq to entities does not allow to use PropertyInfo in queries, I need to run a ToList() on the set in order to execute the query on the db first and then perform the desired SelectMany().
This queries more than it needs to from the database which will be a problem when there's a lot of data (queried columns are of type string, others can be blobs and these are the ones I don't want to pull the data from)
So the question is how can I limit the columns queried from the db based on a list constructed at runtime?
I was trying to create an expression tree, and pass it to the Select() method on the set, but the problem was with creating the anonymous type, which can be different depenging on type T.
Your observation here:
the problem was with creating the anonymous type, which can be different depenging on type T
is accurate; it is hugely problematic to construct the result columns at runtime. The only simply way to do that is to make it look like you are populating a projection of a type that has all the members, for example the equivalent of:
// where our list is "Foo", "Bar":
x => new SomeType {
Foo = x.Foo,
Bar = x.Bar
// but other SomeType properties exist, but are't mapped
}
The obvious contender would be the entity type, so you are partially mapping a set of Customer rows to Customer objects - but most ORMs won't let you do that: if the projection is an entity type they want the entire type (i.e. x => x). You might be able to create a second version of the entity type that is a regular POCO/DTO but isn't part of the entity model, i.e.
Customer x => new CustomerDto {
Foo = x.Foo,
Bar = x.Bar
}
which you can do as part of Expression.MemberInit at runtime. Example:
class Foo
{
public string A { get; set; }
public int B { get; set; }
public DateTime C { get; set; }
}
class FooDto
{
public string A { get; set; }
public int B { get; set; }
public DateTime C { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data = new[] { new Foo { A = "a", B = 1, C = DateTime.Now}}
.AsQueryable();
var mapped = PartialMap<Foo, FooDto>(data, "A", "C").ToList();
}
static IQueryable<TTo> PartialMap<TFrom, TTo>(
IQueryable<TFrom> source, params string[] members)
{
var p = Expression.Parameter(typeof(TFrom));
var body = Expression.MemberInit(Expression.New(typeof(TTo)),
from member in members
select (MemberBinding)Expression.Bind(
typeof(TTo).GetMember(member).Single(),
Expression.PropertyOrField(p, member))
);
return source.Select(Expression.Lambda<Func<TFrom, TTo>>(body, p));
}
}
in the output, A and C have values, but B does not.

How to create filter expression dynamically in EF IQueryable?

Lets say we have interface:
public interface ISystemCompany
{
Guid SystemCompanyId { get; set; }
}
We also have IQueryable<> of type, that implements ISystemCompany.
We have specific SystemCompanyId value: 432486bc-95f2-e111-be6e-74de2b99e249
We want to write function, that filters IQueryable by value of this field. This is example of function like that:
public IQueryable FilterByValue(IQueryable listToFilter, Guid valueToFilter)
{
var parameter = Expression.Parameter(typeof(object));
// item
var converted = Expression.Convert(parameter, typeof(ISystemCompany));
// (ISystemCompany)item
var member = Expression.Property(converted, "SystemCompanyId");
// ((ISystemCompany)item).SystemCompanyId
var comparison = Expression.Equal(member, Expression.Constant(valueToFilter));
// ((ISystemCompany)item).SystemCompanyId == valueToFilter
var expression = Expression.Lambda<Func<object, bool>>(comparison, new[] { parameter } );
//item => ((ISystemCompany)item).SystemCompanyId == valueToFilter
return ((IQueryable<object>)listToFilter).Where(expression);
// listToFilter.Where(((ISystemCompany)item).SystemCompanyId == valueToFilter)
}
This function doesn't work with Entity Framework, because it doesn't support deferred execution. How can I improve it to introduce this support?

Categories

Resources