I have the following classes:
public class Vehicle
{
public string Name { get; set; }
public bool CanFly { get; set; }
public bool CanDive { get; set; }
}
public class Hdd
{
public string Name { get; set; }
public bool CanRead { get; set; }
public bool CanWrite { get; set; }
public bool CanCopy { get; set; }
}
I want to write one function that can filter for example if a specific car exists (filter by firstOrDefault name) then check the given condition as parameter, for example CanFly or CanDive... etc
so i was thinking of:
public class TestProperties
{
public bool Check<T>(List<T> items, string name,
Expression<Func<T, bool>> expression)
{
var expr = (MemberExpression)expression.Body;
var prop = (PropertyInfo)expression.Member;
//1- Filter items with the given name
// return false if no records found
// return false if the condition fails
}
}
Then I would call the functions as follow
var myHdds= GetHdd();
var myCars= GetCars();
var CanRead = Check<Hdd>(myHdds,"samsung",x => x.CanRead);
var CanFly = Check<Vehicle>(myCars,"Audi",x => x.CanFly);
How can i implement the Check function?
You are almost there. Try this -
public bool Check<T>(List<T> items, Expression<Func<T, bool>> expression)
{
return items.Any(x => expression.Compile()(x));
}
or,
public bool Check<T>(List<T> items, Func<T, bool> compiledExp)
{
return items.Any(x => compiledExp(x));
}
and call like this -
Check<Vehicle>(myCars, x => x.Name == "Audi" && x.CanFly);
Check<Hdd>(myHdds,x => x.Name == "Samsung" && x.CanRead);
But now to think of this, you really don't need a method to do that. It is literally one line of code.
Personally I would have preferred an extension method -
public static bool Check<T>(this List<T> items, Func<T, bool> compiledExp)
{
return items.Any(x => compiledExp(x));
}
and call like -
myHdds.Check(x => x.Name == "Samsung" && x.CanRead);
Related
I have these classes:
public class AlertEvaluation
{
public string AlertId { get; set; }
public ICollection<EvaluatedTag> EvaluatedTags { get; set; }
public string TransactionId { get; set; }
public EvaluationStatus EvaluationStatus { get; set; }
public DateTime EvaluationDate { get; set; }
}
public class EvaluatedTag
{
public string Id { get; set; }
public string Name { get; set; }
}
And I would like to get a list of alert evaluations grouped by AlertId, and by EvaluatedTags, meaning that I would like to compare and group evaluations that not only have the same AlertId, but to also have the same list of EvaluatedTags. (And also get the last evaluation in time)
I tried this:
var evaluationsGroupedAndOrdered = evaluations.GroupBy(x => new { x.AlertSettingId, x.EvaluatedLabels })
.Select(x => x.OrderByDescending(z => z.EvaluationDate ).FirstOrDefault()).ToList();
But of course, the comparing of list properties like that did not work.
I read something about adding an equality comparer in GroupBy, which would mean comparing the lists inside the objects right? But I'm not sure of how to implement it in the right way.
I tried (based on GroupBy on complex object (e.g. List<T>)) :
public class AlertEvaluationComparer : IEqualityComparer<AlertEvaluation>
{
public bool Equals(AlertEvaluation x, AlertEvaluation y)
{
return x.AlertId == y.AlertId && x.EvaluatedTags.OrderBy(val => val.Name).SequenceEqual(y.EvaluatedTags.OrderBy(val => val.Name));
}
public int GetHashCode(AlertSettingEvaluation x)
{
return x.AlertId.GetHashCode() ^ x.EvaluatedTags.Aggregate(0, (a, y) => a ^ y.GetHashCode());
}
}
But did not work either.. Maybe because my list EvaluatedTags is not a list of strings but of individual objects.
Does anybody have a nice solution for this?
A typical way to compare two lists is to use the System.Linq exension method, SequenceEquals. This method returns true if both lists contain the same items, in the same order.
In order to make this work with an IEnumerable<EvaluatedTag>, we need to have a way to compare instances of the EvaluatedTag class for equality (determining if two items are the same) and for sorting (since the lists need to have their items in the same order).
To do this, we can override Equals and GetHashCode and implement IComparable<EvaluatedTag> (and might as well do IEquatable<EvaluatedTag> for completeness):
public class EvaluatedTag : IEquatable<EvaluatedTag>, IComparable<EvaluatedTag>
{
public string Id { get; set; }
public string Name { get; set; }
public int CompareTo(EvaluatedTag other)
{
if (other == null) return -1;
var result = string.CompareOrdinal(Id, other.Id);
return result == 0 ? string.CompareOrdinal(Name, other.Name) : result;
}
public bool Equals(EvaluatedTag other)
{
return other != null &&
string.Equals(other.Id, Id) &&
string.Equals(other.Name, Name);
}
public override bool Equals(object obj)
{
return Equals(obj as EvaluatedTag);
}
public override int GetHashCode()
{
return Id.GetHashCode() * 17 +
Name.GetHashCode() * 17;
}
}
Now we can use this in the custom comparer you have in your question, for sorting and comparing the EvaluatedTags:
public class AlertEvaluationComparer : IEqualityComparer<AlertEvaluation>
{
// Return true if the AlertIds are equal, and the EvaluatedTags
// contain the same items (call OrderBy to ensure they're in
// the same order before calling SequenceEqual).
public bool Equals(AlertEvaluation x, AlertEvaluation y)
{
if (x == null) return y == null;
if (y == null) return false;
if (!string.Equals(x.AlertId, y.AlertId)) return false;
if (x.EvaluatedTags == null) return y.EvaluatedTags == null;
if (y.EvaluatedTags == null) return false;
return x.EvaluatedTags.OrderBy(et => et)
.SequenceEqual(y.EvaluatedTags.OrderBy(et => et));
}
// Use the same properties in GetHashCode that were used in Equals
public int GetHashCode(AlertEvaluation obj)
{
return obj.AlertId?.GetHashCode() ?? 0 * 17 +
obj.EvaluatedTags?.Sum(et => et.GetHashCode() * 17) ?? 0;
}
}
And finally we can pass your AlertEvaluationComparer to the GroupBy method to group our items:
var evaluationsGroupedAndOrdered = evaluations
.GroupBy(ae => ae, new AlertEvaluationComparer())
.OrderBy(group => group.Key.EvaluationDate)
.ToList();
Here's a go at it, getting away from Linq a bit to make it easier to build the groups one at a time while leveraging sorting:
// Build groups by using a combination of AlertId and EvaluatedTags hashcode as group key
var groupMap = new Dictionary<string, SortedSet<AlertEvaluation>>();
foreach (var item in evals)
{
var combinedKey = item.AlertId + EvaluatedTag.GetCollectionHashCode(item.EvaluatedTags);
if (groupMap.TryGetValue(combinedKey, out SortedSet<AlertEvaluation>? groupItems))
{
// Add to existing group
groupItems.Add(item);
}
else
{
// Create new group
groupMap.Add(combinedKey, new SortedSet<AlertEvaluation> { item });
}
}
// Get a list of groupings already sorted ascending by EvaluationDate
List<SortedSet<AlertEvaluation>>? groups = groupMap.Values.ToList();
This assumes that the classes implement IComparable and Equals/GetHashCode to facilitate sorting:
public class AlertEvaluation : IComparable<AlertEvaluation>
{
public string AlertId { get; set; }
public ICollection<EvaluatedTag> EvaluatedTags { get; set; }
public string TransactionId { get; set; }
public EvaluationStatus EvaluationStatus { get; set; }
public DateTime EvaluationDate { get; set; }
// Used by SortedSet
public int CompareTo(AlertEvaluation? other)
{
if (other is null)
{
return 1;
}
return EvaluationDate.CompareTo(other.EvaluationDate);
}
}
public class EvaluatedTag : IEquatable<EvaluatedTag?>
{
public string Id { get; set; }
public string Name { get; set; }
public bool Equals(EvaluatedTag? other) => other != null && Id == other.Id && Name == other.Name;
public override int GetHashCode() => HashCode.Combine(Id, Name);
// Helper to get a hash of item collection
public static int GetCollectionHashCode(ICollection<EvaluatedTag> items)
{
var code = new HashCode();
foreach (var item in items.OrderBy(i => i.Id))
{
code.Add(item);
}
return code.ToHashCode();
}
}
By the way, I'm using the fancy new HashCode class in .NET Core to override hash codes.
I have the two expression trees : one to get a navigation property of a class and another to filter on the values from this navigation property :
I am trying to combine them .
class Program
{
public interface IDeletable
{
bool IsDeleted { get; set; }
}
public class User
{
public int Id { get; set; }
public IEnumerable<BlogPost> BlogPosts;
}
public class BlogPost : IDeletable
{
public string Text { get; set; }
public bool IsDeleted { get; set; }
}
static void Main(string[] args)
{
var user = new User()
{
Id = 1,
BlogPosts = new List<BlogPost> {
new BlogPost {IsDeleted=false,Text="hello" },
new BlogPost {IsDeleted=true,Text="this is deleted" }
}
};
Expression<Func<User, IEnumerable<BlogPost>>> notDeletedExpression = Combine<User, BlogPost>(x => x.BlogPosts, x => !x.IsDeleted);
Console.ReadLine();
}
public static Expression<Func<T, IEnumerable<TChild>>> Combine<T, TChild>
(
Expression<Func<T, IEnumerable<TChild>>> navigationProperty,
Expression<Func<TChild, bool>> filter
)
where T : class
where TChild : class, IDeletable
{
//TODO
// shourld return x=>x.Posts.Where(p=>IsDeleted==false) ;
return null;
}
}
In the sample below the two Expressions from your sample are combined using Enumerable.Where method:
public static Expression<Func<T, IEnumerable<TChild>>> Combine<T, TChild>
(
Expression<Func<T, IEnumerable<TChild>>> navigationProperty,
Expression<Func<TChild, bool>> filter
)
where T : class
where TChild : class, IDeletable
{
// Consider caching the MethodInfo object:
var whereMethodInfo = GetEnumerableWhereMethodInfo<TChild>();
// Produce an Expression tree like:
// Enumerable.Where(<navigationProperty>, <filter>)
var filterExpr = Expression
.Call(
whereMethodInfo,
navigationProperty.Body,
filter
);
// Create a Lambda Expression with the parameters
// used for `navigationProperty` expression
return Expression
.Lambda<Func<T, IEnumerable<TChild>>>(
filterExpr,
navigationProperty.Parameters
);
}
private static MethodInfo GetEnumerableWhereMethodInfo<TSource>()
{
// Get a MethodInfo definition for `Enumerable.Where<>`:
var methodInfoDefinition = typeof(Enumerable)
.GetMethods()
.Where(x => x.Name == nameof(Enumerable.Where))
.First(x =>
{
var parameters = x.GetParameters();
return
parameters.Length == 2 &&
parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>);
});
// Get a MethodInfo object for `Enumerable.Where<TSource>`:
var methodInfo = methodInfoDefinition.MakeGenericMethod(typeof(TSource));
return methodInfo;
}
I have various models in my code one is MenuEntry which is like-
public class MenuEntry : CorporationMetadata, IEntity
{
public virtual int MenuEntryId { get; set; }
public virtual MenuEntryType MenuEntryType { get; set; }
public virtual string Name { get; set; }
public virtual Category Category { get; set; }
public virtual TaxGroup TaxGroup { get; set; }
public virtual PrinterGroup PrinterGroup { get; set; }
public virtual bool OpenPrice { get; set; }
public virtual bool OpenName { get; set; }
public virtual bool Active { get; set; }
public virtual IList<Barcode> Barcodes { get; set; }
}
When I created only MenuEntry Repository then I am fetching all its details eagerly like this
public override IQueryable<MenuEntry> GetEager(Expression<Func<MenuEntry, bool>> filter)
{
var menuEntries = _unitOfWork.Session.Query<MenuEntry>().Where(filter)
.FetchMany(x => x.Barcodes).ToFuture();
_unitOfWork.Session.Query<MenuEntry>()
.Fetch(x => x.MenuEntryType).ToFuture();
_unitOfWork.Session.Query<MenuEntry>()
.Fetch(x => x.CreatedUser).ToFuture();
_unitOfWork.Session.Query<MenuEntry>()
.Fetch(x => x.LastModifiedUser).ToFuture();
_unitOfWork.Session.Query<MenuEntry>()
.Fetch(x => x.Category).ToFuture();
_unitOfWork.Session.Query<MenuEntry>()
.Fetch(x => x.TaxGroup).ToFuture();
_unitOfWork.Session.Query<MenuEntry>()
.Fetch(x => x.PrinterGroup).ToFuture();
return menuEntries.AsQueryable();
}
Now if I am using a generic repository I am fetching properties with getproperties() method like:
public virtual IQueryable<TEntity> GetEager(Expression<Func<TEntity, bool>> filter)
{
var result = _unitOfWork.Session.Query<TEntity>().Where(filter).ToFuture();
PropertyInfo[] properties = typeof(TEntity).GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType.GetInterface("IEntity") != null)
{
_unitOfWork.Session.Query<TEntity>()
.Fetch(x => x.property).ToFuture();
}
else
{
if (property.PropertyType != typeof(string) && typeof(IEnumerable<TEntity>).IsAssignableFrom(property.PropertyType))
{
_unitOfWork.Session.Query<TEntity>()
.FetchMany(x => x.propert).ToFuture();
}
}
}
return result.AsQueryable();
}
but this is not working and x => x.property is giving error
How could I eagerly fetch its all properties?
Maybe you need to create the Lambda expression dinamically with something similar to this:
Expression<Func<TEntity, object>> CreateLambda<TEntity>(PropertyInfo propertyInfo) where TEntity : class
{
var parameterExpression = System.Linq.Expressions.Expression.Parameter(typeof(TEntity), "x");
var memberExpression = System.Linq.Expressions.Expression.PropertyOrField(parameterExpression, propertyInfo.Name);
var memberExpressionConversion = System.Linq.Expressions.Expression.Convert(memberExpression, typeof(object));
return System.Linq.Expressions.Expression.Lambda<Func<TEntity, object>>(memberExpressionConversion, parameterExpression);
}
...
_unitOfWork.Session.Query<TEntity>().Fetch(CreateLambda<TEntity>(property)).ToFuture();
...
I'm setting up an automapper profile to map Revit element (from their api) to some custom element.
Their API got circular references (1 element => n parameters, 1 parameter => 1 element) I call the PreserveReference() methods. But it doesn't seem to work because I have a StackOverflowException.
So, I'm wondering how the PreserveReference works? Can I specify a property to check equality instead of using references?
public class Element
{
public int Id { get; set; }
public List<Parameter> Parameters { get; set; }
public override bool Equals(object obj)
{
return obj is Element element && Id == element.Id;
}
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return Id;
}
}
public class Parameter
{
public int Id { get; set; }
public Definition Definition { get; set; }
public Element Element { get; set; }
}
profile.CreateMap<Element, Models.Element>()
.ForMember(element => element.Id, expression => expression.MapFrom(element => element.Id.IntegerValue))
.IncludeAllDerived()
.PreserveReferences();
profile.CreateMap<Parameter, Models.Parameter>()
.ForMember(parameter => parameter.Id, expression => expression.MapFrom(parameter => parameter.Id.IntegerValue))
.IncludeAllDerived()
.PreserveReferences();
I find a way to get what I want. I had to get access to a private property like that :
public static T GetSource<T>(this ContextCacheKey contextCacheKey)
{
var source = (T)typeof(ContextCacheKey).GetField("_source", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)?.GetValue(contextCacheKey);
return source;
}
public class ElementConverter : IValueConverter<Element, Models.Element>
{
public Models.Element Convert(Element sourceMember, ResolutionContext context)
{
var a = (Models.Element)context.InstanceCache.FirstOrDefault(kvp => kvp.Key.GetSource<Element>().Id.Equals(sourceMember.Id)).Value ?? context.Mapper.Map<Models.Element>(sourceMember);
return a;
}
}
A simple solution would be so have Source and DestinationType public in the ContextCacheKey struc :
public struct ContextCacheKey : IEquatable<ContextCacheKey>
{
public static bool operator ==(ContextCacheKey left, ContextCacheKey right) => left.Equals(right);
public static bool operator !=(ContextCacheKey left, ContextCacheKey right) => !left.Equals(right);
public readonly object _source;
public readonly Type _destinationType;
public ContextCacheKey(object source, Type destinationType)
{
_source = source;
_destinationType = destinationType;
}
public override int GetHashCode() => HashCodeCombiner.Combine(_source, _destinationType);
public bool Equals(ContextCacheKey other) =>
_source == other._source && _destinationType == other._destinationType;
public override bool Equals(object other) =>
other is ContextCacheKey && Equals((ContextCacheKey)other);
}
I'm trying to create a method that wraps a LINQ Where call (on an IQueryable) for filtering on a specific field in a collection and am at a loss for how to make it work.
For example, I have a collection of Job objects similar to the following:
public class Job
{
public int Id { get; set; }
public int StatusId { get; set; }
}
public class StatusItem
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsAvailable { get; set; }
public static readonly StatusItem Canceled = new StatusItem() { Id = (int)StatusEnum.Canceled, Name = StatusEnum.Canceled.ToString(), IsAvailable = true };
public static readonly StatusItem Created = new StatusItem() { Id = (int)StatusEnum.Created, Name = StatusEnum.Created.ToString(), IsAvailable = true };
public static readonly StatusItem Open = new StatusItem() { Id = (int)StatusEnum.Open, Name = StatusEnum.Open.ToString(), IsAvailable = true };
public static readonly StatusItem Assigned = new StatusItem() { Id = (int)StatusEnum.Assigned, Name = StatusEnum.Assigned.ToString(), IsAvailable = false };
}
I'm hoping to have a service method that enforces filtering using only the system defined statuses, something like this:
IEnumerable<Job> GetAll(Expression<Func<StatusItem, bool>> statusFilter)
{
// Jobs is IQueryable<job>. How do I apply statusFilter to Job.StatusId?
return jobs.Where(/* some magic here? */);
}
With a call similar to:
return JobService.GetAll(s => s > StatusItem.Open && s < StatusItem.Assigned);
Edit: Been staring too long at this. Brain now mush. Attempted to fix previous errors
The simplest way to do it is to use Expression<Func<Job, bool>> instead of Expression<Func<StatusEnum, bool>>, which would let you write something like this:
IEnumerable<Job> GetAll(Expression<Func<Job, bool>> jobFilter)
{
return jobs.Where(jobFilter);
}
It also has the benefit of being more flexible in case you want to filter by something other than the status.
If you really want to use Expression<Func<StatusEnum, bool>>, it becomes more complex, because you need to rewrite the expression to make a Expression<Func<Job, bool>> from the Expression<Func<StatusEnum, bool>>. Here's a way to do it:
IEnumerable<Job> GetAll(Expression<Func<StatusEnum, bool>> statusFilter)
{
var job = Expression.Parameter(typeof(Job), "job");
var visitor = new ParameterReplacementVisitor(
statusFilter.Parameters[0],
Expression.Property(job, nameof(Job.StatusId)));
Expression<Func<Job, bool>> jobFilter =
Expression.Lambda<Func<Job, bool>>(
visitor.Visit(statusFilter.Body),
job);
return jobs.Where(jobFilter);
}
class ParameterReplacementVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly Expression _replacement;
public ParameterReplacementVisitor(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _parameter)
return _replacement;
return node;
}
}
I don't know if this is what you need, but check this:
jobs.Where(x => statusFilter.Compile().Invoke((StatusEnum)x.StatusId));
Also consider changing property StatusId to StatusEnum. Properties should also be public.
class Job
{
public int Id { get; set; }
public StatusEnum StatusId { get; set; }
}
With this kind of declaration casting to StatusEnum is not required:
jobs.Where(x => statusFilter.Compile().Invoke(x.StatusId));