I am using the ElasticSearch NEST package and i want to have an extension method that can be used with multiple NEST descriptors.
public static Nest.MatchQueryDescriptor<Models.Data.Test> FuzzySearch(
this Nest.MatchQueryDescriptor<Models.Data.Test> q,
bool isFuzzy = false)
{
if (isFuzzy) return q.Fuzziness(Fuzziness.Auto);
return q;
}
public static Nest.MultiMatchQueryDescriptor<Models.Data.Test> FuzzySearch(
this Nest.MultiMatchQueryDescriptor<Models.Data.Test> q,
bool isFuzzy = false)
{
if (isFuzzy) return q.Fuzziness(Fuzziness.Auto);
return q;
}
I have to write this extension method for both Match and MultiMatch, how can i extract the duplicate code inside both methods so both extension methods use the same q.Fuzziness logic?
Related
I am currently creating a custom way of deep-copying my objects. I use a static class for this functionality.
public static class CopyServer
{
public static int CopyDeep(int original)
{
return original;
}
//not shown: same for all other value types I use (long, float,...)
public static T CopyDeep<T>(T original) where T: ICopyAble
{
if (original == null)
return default;
if (original is ICopyAutofields)
return CopyAutofields(original);
return (T)original.CopyDeep();
}
private static T CopyAutofields<T>(T original)
{
Delegate del;
if (!_copyFunctions.TryGetValue(typeof(T), out del))
{
//not shown: Building expression for parameter etc.
foreach (var fieldInfo in typeof(T).GetFields())
{
//not shown: checking options set by custom attributes
MethodInfo methodInfo = typeof(CopyServer).GetMethod("CopyDeep", new[] { fieldInfo.FieldType });
//I can't remove the second param without getting an AmbiguousMatchException
if (methodInfo == null)
{
throw new Exception($"CopyDeep not defined for type {fieldInfo.FieldType}");
}
if (methodInfo.IsGenericMethod)
methodInfo = methodInfo.MakeGenericMethod(fieldInfo.FieldType);
Expression call = Expression.Call(methodInfo, readValue);
//not shown: Assign Expression
}
//not shown: return Expression and compiling
}
return ((Func<T, T>)del)(original);
}
}
I use T CopyAutofields<T> to build functions (by building and compiling Expression Trees) so I don't have to create copy-functions for each and every class I want to copy by hand. I control the copy-behaviour with Custom Attributes (I left this part in the code above since it is not relevant for my problem).
The code works fine as long as long as only fields with types for which a non-generic function exists are used. But it is not able to retrieve my generic function T CopyDeep<T>.
Example:
//This works:
public class Manager : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
}
//This doesn't
//Meaning: typeof(CopyServer).GetMethod("copyDeep", new[] { fieldInfo.FieldType });
//in T copyAutofields<T> returns null for the Manager-field and my exception gets thrown
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
}
//This is what I was using before I started using the ICopyAutofields.
//This approach works, but its' too much too write since my classes usually
//have way more than three fields and I occasionally forget to update
//copyDeep()-function if I add new ones.
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
public IModable CopyDeep()
{
var result = new Employee();
result.FirstName = CopyServer.copyDeep(FirstName);
result.LastName= CopyServer.copyDeep(LastName);
result.Manager= CopyServer.copyDeep(Manager);
return result;
}
}
Long story short: I need a way of getting a matching function for a type T if both generic and non-generic functions with the right name exist.
In .NET 4.7.1 you need to use method GetMethods and filter the results:
class MyClass
{
public T M<T>(T t) { return default(T); }
public int M(int t) { return 0; }
}
var m = typeof(MyClass).GetMethod("M", new[] { typeof(string) }); // null
var m1 = typeof(MyClass).GetMethods()
.Where(mi => mi.Name == "M" && mi.GetGenericArguments().Any())
.First(); // returns generic method
In .NET Standard 2.1 (and .NET Core since 2.1) there is another way to resolve generic type arguments - Type.MakeGenericMethodParameter, like you can see it in this answer.
Also as workaround you can move your copyAutofields<T> method to generic class like CopyAutoFieldServer<T>:
public static class CopyAutoFieldServer<T>
{
public static T copyAutofields(T original) { ... }
}
I’m building a class named CommandHandler that have ExecuteCommand method that have Command as input parameter.
The Idea is that ExcecuteCommand will check command name and execute proper class method by the name pattern so when command name is Test class should have corresponding TestHandler method.
On initialization I’m using reflection to find all methods and create a mapping between command name and created Func<Command, Task<object>>. For now I have achieve this by making all methods return Task<object> and use Delegate.CreateDelegate to create Func<Command, Task<object>> from reflected method but I would like to clean code and allow methods to return simple types like int or Task<custom class>.
I would like to build some simple Expression that for simple types will execute method and do Task.FromResult dynamically so class method remains clean. For Task with specific type result I would like to create expression that will result Task<object> so all could be cached as Dictionary<string, Func<Command, Task<object>>.
public class CommandHandler
{
public Dictionary<string, Func<Command, Task<object>>> methodCache = new Dictionary<string, Func<Command, Task<object>>>();
public int IntCommandHandler(Command c)
{
return 5;
}
public string StringCommandHandler(Command c)
{
return "5";
}
public Task<int> AsyncIntCommandHandler(Command c)
{
return Task.Run(() => 5);
}
public async Task<object> OldWayCommandHandler(Command c)
{
return "5";
}
private void RegisterAsyncQueryHandlers(Dictionary<string, MethodInfo> handlers)
{
var filtered = handlers.Where(h => h.Value.ReturnType == typeof(Task<object>)).ToList();
foreach (var handler in filtered)
{
methodCache.Add(handler.Key, (Func<Command, Task<object>>)Delegate.CreateDelegate(typeof(Func<Command, Task<object>>), this, handler.Value, false));
}
}
public void FillCache()
{
// Get all methods with proper pattern and pass it to RegisterAsyncQueryHandlers in dictionary of command name and MethodInfo
//RegisterAsyncQueryHandlers
}
public Task<object> ExecuteCommand(Command c)
{
return methodCache[c.Name].Invoke(c);
}
}
public class Command
{
public string Name { get; set; }
}
I have no experience with using Expressions and most of the samples I found are using basic operators and static methods. Maybe someone can help me ho w to build such expression?
If I understand correctly, the question is how to convert Task<TResult> to Task<object>.
It can be done for instance using the Task<TResult>.ContinueWith method as follows:
static Task<object> Convert<TResult>(Task<TResult> source)
{
return source.ContinueWith(t => (object)t.Result);
}
It's possible to dynamically build such expression, but the easier would be to put the above method in your class and "call" it via the following Expression.Call overload.
The method that builds expression and compiles a delegate from it could be like this:
Func<Command, Task<object>> MakeFunc(MethodInfo handler)
{
var c = Expression.Parameter(typeof(Command), "c");
var task = Expression.Call(Expression.Constant(this), handler, c);
if (task.Type != typeof(Task<object>))
task = Expression.Call(GetType(), "Convert", new[] { task.Type.GetGenericArguments().Single() }, task);
var expr = Expression.Lambda<Func<Command, Task<object>>>(task, c);
return expr.Compile();
}
My code is as follows:
public partial class WhereHelper<T1> { }
public static partial class WhereHelperExtension
{
public static T Where<T,T1>(this T t, Expression<Func<T1,bool>> where) where T : WhereHelper<T1>
{
//do something....
return t;
}
}
public class Test
{
public void Main()
{
WhereHelper<DateTime> dt = new WhereHelper<DateTime>();
dt.Where(t => t.Year == 2016);//this is error
dt.Where<WhereHelper<DateTime>, DateTime>(t => t.Year == 2016);//this is success
}
}
Extension method I want to be like this:
WhereHelper<DateTime> dt = new WhereHelper<DateTime>();
dt.Where(t => t.Year == 2016);//this is error
how to create generic extension with Expression method.
Visual Studio does not recognize the "Where" extension methods.
In C#, if you need to provide any generic argument, you must provide them all. where constraints do not provide hints to the type resolver, and so it's impossible to decide what T1 is.
Change your signature to the following:
public static WhereHelper<T> Where<T>(this WhereHelper<T> t, Expression<Func<T,bool>> where)
{
return t;
}
Here, we know exactly what T, purely from the first argument, and so we do not have to explicitly specific the arguments.
I have a library of model-to-viewmodel mapping extension methods. Supporting them is a base class with a few common methods, including Transform, below:
internal abstract class TransformBase<TOriginal, TConverted>
{
protected abstract Expression<Func<TOriginal, TConverted>> Expression { get; }
public IQueryable<TConverted> Transform(IEnumerable<TOriginal> value)
{
var queryable = value as IQueryable<TOriginal> ?? value.AsQueryable();
return queryable.Select(Expression);
}
My question: is there any significant reason, besides a negligible performance hit, that I should avoid the as IQueryable cast above? For example, I could instead do the following:
internal abstract class TransformBase<TOriginal, TConverted>
{
protected abstract Expression<Func<TOriginal, TConverted>> Expression { get; }
public IQueryable<TConverted> Transform(IQueryable<TOriginal> value)
{
return value.Select(Expression);
}
public IQueryable<TConverted> Transform(IEnumerable<TOriginal> value)
{
return value.AsQueryable().Select(Expression);
}
... but I would prefer not to have to write the overloads in every one of my dependent classes. EDIT: To clarify, here is an example of what I'm seeking to avoid:
public static class TransformCompany
{
private static readonly TransformBase<Organization, CompanyHeader> header = new TransformPrecompiled<Organization, CompanyHeader>(
company => new CompanyHeader
{
Name = company.Name,
});
public static IQueryable<CompanyHeader> AsHeaders(this IQueryable<Organization> companies)
{
return header.Transform(companies);
}
// Note I have to include this capability in each of my dependent classes
// Worse is the possibility that someone may accidentally implement
// only IEnumerable for a future model transformation,
// causing a hidden data performance problem
public static IQueryable<CompanyHeader> AsHeaders(this IEnumerable<Organization> companies)
{
return header.Transform(companies);
}
I would say you do not need separate extensions for IEnumerable<T> and IQueryable<T> as IQueryable<T> inherits from IEnumerable<T>, and you also do not need to the cast.
Looking at referencesource, AsQueryable() actually does this check for you:
public static IQueryable<TElement> AsQueryable<TElement>(this IEnumerable<TElement> source)
{
if (source == null)
throw Error.ArgumentNull("source");
if (source is IQueryable<TElement>)
return (IQueryable<TElement>)source;
return new EnumerableQuery<TElement>(source);
}
Therefore the following should work for you with no performance hit:
internal abstract class TransformBase<TOriginal, TConverted>
{
protected abstract Expression<Func<TOriginal, TConverted>> Expression { get; }
public IQueryable<TConverted> Transform(IEnumerable<TOriginal> value)
{
return value.AsQueryable().Select(Expression);
}
}
public static class TransformCompany
{
public static IQueryable<CompanyHeader> AsHeaders(this IEnumerable<Organization> companies)
{
return header.Transform(companies);
}
}
Queryable.AsQueryable Method (IEnumerable)
If the type of source implements IQueryable,
AsQueryable(IEnumerable) returns it directly. Otherwise, it returns an
IQueryable that executes queries by calling the equivalent query
operator methods in Enumerable instead of those in Queryable.
Instead of casting you simplify your Transform method to
return value.AsQueryable().Select(Expression);
I want to combine two array's, excluding duplicates. I am using a custom class:
public class ArcContact : IEquatable<ArcContact>
{
public String Text;
public Boolean Equals(ArcContact other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return Text.Equals(other.Text);
}
public override Int32 GetHashCode()
{
return Text == null ? 0 : Text.GetHashCode();
}
}
I implemented and the needed IEquatable interface as mentioned in this msdn section. I only want to check the Text property of the ArcContact class and make sure an Array of ArcContact have an unique Text.
Here I pasted the code that I use, as you can see I have method with two parameters, array's to combine and below that the code I got from the previous mentioned msdn section.
internal static class ArcBizz
{
internal static ArcContact[] MergeDuplicateContacts(ArcContact[] contacts1, ArcContact[] contacts2)
{
return (ArcContact[])contacts1.Union(contacts2);
}
internal static IEnumerable<T> Union<T>(this IEnumerable<T> a, IEnumerable<T> b);
}
What am I doing wrong?
I would assume you get an InvalidCastException due to:
return (ArcContact[])contacts1.Union(contacts2);
It should be
return contacts1.Union(contacts2).ToArray();
Also, I am not sure what the following is doing in your code:
internal static IEnumerable<T> Union<T>(this IEnumerable<T> a, IEnumerable<T> b);
The result of the Union is not an array, it's an IEnumerable.
You have to use ToArray extension method:
return contacts1.Union(contacts2).ToArray();