[INTRO]
I know there are about a zillion QA about generics and reflections everywhere, but it's becoming a blackhole to me, and I'm only getting more lost the more I read!!
What i need to do is simple, and I'm amazed that it hasn't been addressed before.
[SAMPLE] Consider the following snippit:
public async Task<string> generateJsonSchema(string model)
{
try
{
string modelName = "Models." + model;
Type t = Type.GetType(modelName, false);
JsonSchema4 schema = await JsonSchema4.FromTypeAsync<t>();
return schema.ToJson();
}
catch (Exception ex)
{
Logger.WriteToLogFile(ex.ToString(), "exception");
return "";
}
}
[PROBLEM] Now the main problem is that variable t is evaluated at runtime, thus, JsonSchema4.FromTypeAsync<t>() throws the error 't' is a variable but is used like a type when trying to build compile time
Whoever used JsonSchema4 will understand what I'm trying to achieve here.
Instead of creating a generate function for each of my models, or make a switch/if-else logic,
[QUESTION]
How to make it receive the model name as a string parameter, and convert the string-model-name to model-type and pass it to jSonSchema4 method.
The problem here is that, as you say, t is evaluated as runtime.
I also ran into this Problem and solved it by creating a MethodInfo of the method I wanted to invoke, in your case JsonSchema4.FromTypeAsync<t>().
So basically this is want may fix the problem:
var methodInfo = typeof(JsonSchema4).GetMethod("FromTypeAsync", new Type[] { }); //Get the "normal method info", the overload without parameters
var methodInfoWithType = methodInfo.MakeGenericMethod(t); //now you have a method with your desired parameter t as TypeParameter
Task<JsonSchema4> task = methodInfoWithType.Invoke(null, null) as Task<JsonSchema4>; //now you can cast the result from invoke as Task to keep the Async-Await functionality
var schema = await task;
return schema.ToJson();
Related
I looked at https://stackoverflow.com/a/5174773/787958 and the method resolution is not working as expected in my instance.
I have JSON coming into a method that I want to create a typed object from and then validate it.
var type = Type.GetType($"{Namespace.Models}.{typeName.FirstCharToUpper()}");
var model = JsonConvert.DeserializeObject(json, type);
var validator = new Validator();
var isValid = await validator.ValidateAsync(model);
The Validator class looks as such:
public class Validator : IValidator
{
public Task<Boolean> ValidateAsync(Object model) { return Task.FromResult(true); }
public Task<Boolean> ValidateAsync(User user)
{
if (user.Id == null || user.Id == Guid.Empty)
return Task.FromResult(false);
if (String.IsNullOrWhiteSpace(user.Name))
return Task.FromResult(false);
return Task.FromResult(true);
}
}
Even though at runtime, type is User, it does not call the ValidateAsync(User user) method, instead it calls ValidateAsync(Object model) method.
But, if the type is not dynamic at compile time and instead I do:
var model = JsonConvert.DeserializeObject<User>(json);
It properly calls the ValidateAsync(User user) method.
Is there a proper way to have a dynamic type at compile time, but known at runtime, and have it call the expected "more specific" method?
UPDATE
Having any "future coder" add to the switch statement was not too much of an ask imo, since they would be in that class adding their new ValidateAsync(NewModel model) method anyways. Utilizing dynamic was more along the lines of how I would like the code to be as it would not bloat the code with a possibly larger and larger switch statement. But, I was worried about performance impact. So I ran a Postman runner 250 times with the switch statement (just User or default) and 250 times with dynamic. The differences in performance was negligible.
Average response time with switch: 2.668ms
Average response time with dynamic: 2.816ms
So, I'm going to go with the dynamic solution. Thanks!
Overload resolution happens at the compile time, not in the runtime. model created by var model = JsonConvert.DeserializeObject(json, type); will be of object type so compiler will expectedly select ValidateAsync(Object model) overload.
You may try using dynamic for late bound (runtime) resolution:
dynamic model = JsonConvert.DeserializeObject(json, type);
var validator = new Validator();
var isValid = await validator.ValidateAsync(model);
But note that it comes with some performance cost.
Another option is type testing and casting. For example with pattern matching can look like this:
var isValid = model switch
{
User user => await validator.ValidateAsync(user),
_ => await validator.ValidateAsync(model)
}
The type of method resolution you're thinking of happens at compile time.
If you want to resolve the method at run time you have to write the code for it yourself, e.g.
public Task<Boolean> ValidateAsync(Object model)
{
return (model is User) ? ValidateAsync((User)model) : Task.FromResult(true);
}
I would like to be able to pass a class name as parameter to a method, and then inside that method create an object of that class with certain parameters.
A concrete (simplified) example:
This is a method to compute an OperationResult
private IOperationResult<Unit> GetFailedOperationResult(IEnumerable<StrictSide> sides, IFailedOperationInfo failedOperationResult)
{
var exception = failedOperationResult.Exception.HasValue() ? failedOperationResult.Exception.Value() : null;
if (exception != null)
{
return new FailedResult<Unit>(
new InvalidBundleErrorKeyResolver(new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName, sides), exception)));
}
throw new InvalidOperationException("Failed operation result during bundle consistency check does not contain error or exception.");
}
Depending on the operation that we get the error from, we use different ErrorKeyResolvers. I would like to pass these ErrorKeyResolver as a parameter to the method, so that I don't need to make different GetFailedOperationResult methods for each error type.
Inspired by How to use class name as parameter in C#
I tried something like this:
private IOperationResult<Unit> GetFailedOperationResult(IEnumerable<StrictSide> sides,IFailedOperationInfo failedOperationResult, IErrorResourceKeyResolver resourceKeyResolver)
{
var exception = failedOperationResult.Exception.HasValue() ? failedOperationResult.Exception.Value() : null;
if (exception != null)
{
return new FailedResult<Unit>(Activator.CreateInstance(typeof(resourceKeyResolver),new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName, sides), exception)));
}
throw new InvalidOperationException("Failed operation result during bundle consistency check does not contain error or exception.");
}
But I cannot do typeof(resourceKeyResolver) because I cannot use a variable as a type.
Is there a nice way to do this? Is it even a good thing to do? I also read that dynamics should be avoided so I wonder if saving some code repetition is worth it here.
EDIT: the input parameters should be: private IOperationResult<Unit> GetFailedOperationResult(IEnumerable<StrictSide> sides,IFailedOperationInfo failedOperationResult, string resourceKeyResolver)
And from the class name as string I should be able to find the type.
If you pass the class as interface you can use following code to instantiate resolver
var resolver = Activator.CreateInstance(resourceKeyResolver.GetType(),
new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName,
sides), exception));
Otherwise, if you use the class name (assembly-qualified-name) you can, for example, convert it to type first and call the above line again
var resolverType = Type.GetType(resourceKeyResolverClassName);
var resolver = Activator.CreateInstance(resolverType ,
new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName,
sides), exception));
See here for documentation of GetType() method
I use the following function which is all well and fine but i basically do the same operation about 20 times. For various end points of an api I am hitting how would one make this routing more Generic in the ability to pass and return type OF T.
public async Task<List<StockItem>> GetStockDataFromSage()
{
StockItem stockitems = new StockItem();
string content = "";
List<StockItem> result = new List<StockItem>();
var uri = new Uri(string.Format(Constants.GetStockItems, string.Empty));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<StockItem>>(content);
}
return result;
}
Edit 1
I am trying to use the below however I am getting an error
public async Task<List<StockItem>> GetStockItemInfo()
{
return await dataTransfer.GetDataFromSageService(Constants.GetStockItems, string.Empty)) ?? new List<StockItem>();
}
Severity Code Description Project File Line Suppression State
Error CS1061 'StockTakeDT' does not contain a definition for 'GetStockDataFromSage' and no accessible extension method 'GetStockDataFromSage' accepting a first argument of type 'StockTakeDT' could be found (are you missing a using directive or an assembly reference?) StockAppDL D:\Git\Repos\StockApp\FStockApp\StockAppDal\StockDatabase.cs 76 Active
Your objective here appears to be to call an endpoint and get the results back into an object you can use. If the call is successful, you return the result and if it fails, you return an empty list.
We can abstract that logic out into a generic method that accepts a url and parameters and returns an object.
public async Task<T> GetObjectFromEndpoint<T>(string url, params string[] args)
where T : class
{
var uri = new Uri(string.Format(url, args));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(content);
}
return default(T);
}
Now your GetStockDataFromSage function passes in the information unique to this call, namely the url, parameters, and generic type for the results. If the result is null, GetStockDataFromSage returns an empty list of StockItems
public async Task<List<StockItem>> GetStockDataFromSage()
{
return (await GetObjectFromEndpoint<List<StockItem>>(Constants.GetStockItems, string.Empty)) ?? new List<StockItem>();
}
Any time you are trying to minimize repetition, you want to look at what is specific to this call and what is more general. i.e List<StockItem>, the url, and possibly the parameter are unique, but the rest of the code is very general.
Caution: This method of returning a default value when the api call fails can lead to hard-to-diagnose issues where you will be unable to differentiate between an empty list and a failed api call. I recommend adding some logging for failed api calls and perhaps looking at ways to inform the calling code that the result was in error.
I have three projects
MVC Web application
Service application which is kind of two layers business/repository
Entity framework (all EF configuration lives here)
MVC references > service
Service references > EF
I have these three methods currently that do some work.
public bool StoreUpload<T>(UploadInformation information)
where T : class, IUploadEntity { }
public bool RemoveUpload<T>(UploadInformation information)
where T : class, IUploadEntity { }
public bool CommitUpload<T>(UploadInformation information)
where T : class, IUploadEntity { }
I call these three methods from my controller using these interfaces which delegate to the work methods above:
Boolean StoreUpload(UploadInformation information);
Boolean RemoveUpload(UploadInformation information);
Boolean CommitStoredDocuments(UploadInformation information);
Based on a condition from UploadTypes enumeration in a switch I call the correct work method. I do this because I don't want my mvc project to have access to the EF database types otherwise I know someone is going to start querying data from all over the application. I use these switch statements for all interfaced methods:
public bool StoreUpload(UploadInformation information)
{
switch (information.Type)
{
case UploadTypes.AutoIncident:
return RemoveUpload<AutoIncident>(information);
case UploadTypes.Incident:
return RemoveUpload<IncidentInjury>(information);
case UploadTypes.Inspection:
return RemoveUpload<Inspection>(information);
case UploadTypes.OtherIncident:
return RemoveUpload<OtherIncident>(information);
default:
return false;
}
}
public bool RemoveUpload(UploadInformation information) { ... }
public bool CommitStoredUpload(UploadInformation information) { ... }
This method might shed a little light on what the types parameters are being used for. I am updating tables in a generic way using EF.
private bool CommitStoredDocuments<T>(UploadInformation information) where T : class, IUploadEntity
{
var uploads = GetStoredUploads(information.UniqueId);
var entity = db.Set<T>().Include(e => e.Uploads)
.Single(e => e.UniqueId == information.UniqueId);
entity.Uploads.AddRange(uploads);
...
}
It would be nice to be able to pass the work method which requires a type parameter as a delegate to the switch work method calls.
public bool DoSomeWork(delegateMethod, information) {
switch(information.Type) {
case UploadTypes.AutoInciden:
return delegateMethod<AutoIncident>(information);
...
}
}
Can this be done?
Also, I had trouble constructing a good title for this question so please comment if these is a better way to describe the challenge.
It cannot be done directly due to several reasons.
First of all, as you probably noticed, delegateMethod<FooBar>(information) simply does not compile. This is because in your example the delegateMethod is a local variable (method parameter actually, but still a variable), and you cannot apply "type arguments" <FooBar> to a variable - you can apply them only on an identifier that indicates a (generic) type or a (generic) method.
Second reason is more interesting. When you pass a method as a delegate, the delegate actually catches the whole method signature, including all parameter types.
void Blah<T>(UploadInformation information){ ... }
var one = new Action<int>(Blah); // -> Blah<int>
var two = new Action<float>(Blah); // -> Blah<float>
var thr = new Action<andsoon>(Blah); // -> Blah<andsoon>
MagicDoSomeWork(one, ...); // these all
MagicDoSomeWork(two, ...); // delegates are already bound
MagicDoSomeWork(thr, ...); // and remember their concrete T
You need to actually specify the type for the Action so a proper version of generic method will be picked from a general description called Blah. These delegates are bound to concrete versions of the method and will accept only that types. These delegates are 'closed' in terms of their type arguments. Using normal ways, the MagicDoSomeWork will simply have no way of altering the T which these delegates already have remembered.
That two things are a kind of show stoppers, since by normal code only, you cannot write things like
var nope1 = new Action(Blah); // ctor for Action NEEDS type parameter
since Action constructor simply requires a type parameter. And once you pass any, it will lock the Blah type arguments
Also you cannot use open delegates:
var nope1 = new Action<>(Blah); // can't use empty <> in this context :(
since new operator requires a full type to create an object.
However, with a bit of reflection voodoo, it is possible to analyze and build a generic type or a generic method dynamically.
// first, build the delegate in a normal way
// and pick anything as the type parameters
// we will later replace them
var delegateWithNoType = new Action<object>(Blah);
// delegate has captured the methodinfo,
// but uses a stub type parameter - it's useless to call it
// but it REMEMBERS the method!
// .... pass the delegate around
// later, elsewhere, determine the type you want to use
Type myRealArgument;
switch(..oversomething..)
{
default: throw new NotImplemented("Ooops");
case ...: myRealArgument = typeof(UploadTypes.AutoIncident); break;
...
}
// look at the delegate definition
var minfo = delegateWithNoType.Method;
var target = delegateWithNoType.Target; // probably NULL since you cross layers
var gdef = minfo.GetGenericDefinition();
var newinfo = gdef.MakeGenericMethod( myRealArgument );
// now you have a new MethodInfo object that is bound to Blah method
// using the 'real argument' type as first generic parameter
// By using the new methodinfo and original target, you could now build
// an updated delegate object and use it instead the original "untyped" one
// That would be a NEW delegate object. You can't modify the original one.
// ...but since you want to call the method, why don't use the methodinfo
UploadInformation upinfo = ... ;
newinfo.Invoke(target, new object[] { upinfo });
// -> will call Blah<UploadTypes.AutoInciden>(upinfo)
word of warning: this is a sketch to show you how the delegate.Method/Target and methodinfo and getgenericdefinition and makegenericmethod work. I wrote it from memory, never compiled, never ran. It can contain minor typos, overlooked things and invisible rainbow unicorns. I didn't noticed any. Probably because they were invisible.
You can do it like this
public bool Invoke(EntityType entityType, ActionType action, Object[] arguments)
{
var actionType = Enum.GetName(typeof(ActionType), action);
var type = GetType();
var method = type.GetMethods().Single(m => m.IsGenericMethod && m.Name == actionType);
switch (entityType)
{
case EntityType.IncidentInjury:
var genericMethod = method.MakeGenericMethod(typeof(IncidentInjury));
return (bool)genericMethod.Invoke(this, arguments);
default:
return false;
}
}
The enum will just be a list of methods that I want to invoke this way and I create a base class for my services so I don't have to pass the instance to the Invoke method.
Instead of using delegates, consider using an interface (or abstract class). This way, your methods can retain their generic nature.
For example, if you create an interface like:
interface IUploadAction
{
bool Perform<T>(UploadInformation information)
where T : class, IUploadEntity;
}
Note that the T is not exposed in the type, it's only on the method. This is the key part.
Now you can implement this for your database methods:
class CommitStoredDocuments : IUploadAction
{
public bool Perform<T>(UploadInformation information)
where T : class, IUploadEntity
{
var uploads = GetStoredUploads(information.UniqueId);
var entity = db.Set<T>().Include(e => e.Uploads)
.Single(e => e.UniqueId == information.UniqueId);
entity.Uploads.AddRange(uploads);
//...
}
}
Your switching/dispatching method can look like this:
public bool DoAction(IUploadAction action, UploadInformation information)
{
switch (information.Type)
{
case UploadTypes.AutoIncident:
return action.Perform<AutoIncident>(information);
case UploadTypes.Incident:
return action.Perform<IncidentInjury>(information);
case UploadTypes.Inspection:
return action.Perform<Inspection>(information);
case UploadTypes.OtherIncident:
return action.Perform<OtherIncident>(information);
default:
return false;
}
}
And then you can write something like:
IUploadAction storeUpload;
public bool StoreUpload(UploadInformation information) => DoAction(storeUpload, information);
I am trying to retrieve the ID of an album using the C# Facebook SDK. However I am getting the below error:
System.Threading.Tasks.Task' does not contain a definition for 'Result' and no extension method 'Result' accepting a first argument of type 'System.Threading.Tasks.Task' could be found
Please see the below code, the error occurs on the foreach line
try
{
string wallAlbumID = string.Empty;
FacebookClient client = new FacebookClient(accessToken);
client.GetTaskAsync(pageID + "/albums")
.ContinueWith(task =>
{
if (!task.IsFaulted)
{
foreach (dynamic album in task.Result.data)
{
if (album["type"] == "wall")
{
wallAlbumID = album["id"].ToString();
}
}
}
else
{
throw new DataRetrievalException("Failed to retrieve wall album ID.", task.Exception.InnerException);
}
});
return wallAlbumID;
}
For the record, the FacebookClient.GetTaskAsync method returns Task<object>
I don't know the Facebook API, but the error seems to indicate, that you're dealing with Task class (non-generic) which does not have Result property. It is the generic Task<T> derived from non-generic Task class that has the property. They both allow to run code asynchronously, but the generic class is able to run methods that return values.
If GetTaskAsync returns Task and not Task<T>, then it means you can't get the result from it, as the operation it runs in the background does not return anything.
When I compile your code, I get two errors, the first one is the one you mentioned, and the second one is:
'object' does not contain a definition for 'data' and no extension method 'data' accepting a first argument of type 'object' could be found
This second error is your actual error: task.Result is an object, but (I assume) you want to treat it as dynamic. Because of this error, the compiler also tries to use the overload of ContinueWith() that uses just Task, not Task<object>, which is why you're also getting the first error.
To fix this error, you should cast task.Result to dynamic:
dynamic result = task.Result;
foreach (dynamic album in result.data)
This will compile fine, but it won't actually work, because you set the local variable after you return from the enclosing method.
If you're using C# 5.0, you should use await here, instead of ContinueWith():
try
{
dynamic result = await client.GetTaskAsync(pageID + "/albums");
foreach (dynamic album in result.data)
{
if (album["type"] == "wall")
{
return (string)album["id"].ToString();
}
}
return string.Empty;
}
catch (Exception e) // you should use a specific exception here, but I'm not sure which
{
throw new DataRetrievalException("Failed to retrieve wall album ID.", e);
}
If you can't use C# 5.0, then your whole method should return a Task<string> that's returned by ContinueWith():
return client.GetTaskAsync(pageID + "/albums")
.ContinueWith(
task =>
{
if (!task.IsFaulted)
{
dynamic result = task.Result;
foreach (dynamic album in result.data)
{
if (album["type"] == "wall")
{
return (string)album["id"].ToString();
}
}
return string.Empty;
}
else
{
throw new DataRetrievalException(
"Failed to retrieve wall album ID.", task.Exception.InnerException);
}
});