Reputation: 385
I have a method that I copy paste into many classes. The method hydrates a model into an IdNameViewModel. How do I create a class with this method that I can pass in the repository model and get back a list of IdNames for that model?
Note: ProjectValue is a model/class that I'm passing in for example but it really could be any model/class that that contains an id and name property.
private IdNameVM HydrateEntityToVM(ProjectValue projectValue)
{
if (projectValue == null) return null;
return new IdNameVM()
{
Id = projectValue.Id,
Name = projectValue.Name
};
}
Here is what I came up with based on the answers below:
public class GenericHydrateIdName<TModel> where TModel : INameId
{
public IdNameVM HydrateEntityToVM(TModel model)
{
if (model == null) return null;
return new IdNameVM()
{
Id = model.Id,
Name = model.Name
};
}
}
}
Just a note if anyone is following, the class above caused way too much work and violates the Clean Code Principle. I used the extension method below.
Upvotes: 0
Views: 3691
Reputation: 7019
Basically, the best type-safe way I can see is having an interface.
interface INameId
{
int Id { get; set; }
string Name { get; set; }
}
You can have this interface in every class you would like to use it in. Based on your current usage, I would suggest an extension function
static class VMExtensions
{
public static IdNameVM HydrateEntityToVM<TModel>(this TModel model)
where TModel : INameId
{
if (model == null) return null;
return new IdNameVM()
{
Id = model.Id,
Name = model.Name
};
}
// update: without generics as Marcus Höglund pointed out
public static IdNameVM HydrateEntityToVM2(this INameId model)
{
if (model == null) return null;
return new IdNameVM()
{
Id = model.Id,
Name = model.Name
};
}
static void Test()
{
var model = new ProjectValue();
var model2 = new AnotherModel();
var viewModel = model2.HydrateEntityToVM();
var viewModel2 = model2.HydrateEntityToVM();
}
}
If you don't want to add the interface to all your models, you can use reflection (but note that it would be really slow).
Update
As Marcus pointed out, In this case, there is no real point of using generics in the function. You can use the HydrateEntityToVM2
implementation that directly uses the interface. However, in a future point if you want to use something like IdNameVM<TModel>
so you can still access the original model if required, HydrateEntityToVM
function with generics will be helpful.
Upvotes: 2
Reputation: 406
I like Neville Nazerane's answer because removes the need for reflection. However, if you did want to do this using reflection then below is a workable way that constructs the result even if it cannot find matching Id or Name properties.
Please note that in this sample I have not added any checking on whether the types for the Id and Name properties match between T and TResult.
public static TResult HydrateEntityToVM<T, TResult>(T inputValue, TResult resultValue) where T : class
{
if (inputValue == null || resultValue == null)
return default(TResult);
var idValue = inputValue.GetType().GetProperty("Id").GetValue(inputValue);
var nameValue = inputValue.GetType().GetProperty("Name").GetValue(inputValue);
object instance;
if (resultValue == null)
{
var ctor = resultValue.GetType().GetConstructor(Type.EmptyTypes);
instance = ctor.Invoke(new object[] {});
}
else
{
instance = resultValue;
}
var idProp = instance.GetType().GetProperty("Id");
if (idProp != null)
{
if (idProp.CanWrite)
idProp.SetValue(instance, idValue);
}
var nameProp = instance.GetType().GetProperty("Name");
if (nameProp != null)
{
if (nameProp.CanWrite)
nameProp.SetValue(instance, nameValue);
}
return (TResult) instance;
}
Upvotes: 0
Reputation: 1603
these are the basics of C#. take a look at this link
class MyClass<T> where T : ProjectValue
{
private IdNameVM HydrateEntityToVM(T projectValue)
{
if (projectValue == null) return null;
return new IdNameVM()
{
Id = projectValue.Id,
Name = projectValue.Name
};
}
}
in case if you don't know the abstraction of model (but be careful , this method can consume any object containing id and name public properties) , then try the following:
public void SomeGenericMethod<T>(T model) where T : class
{
if (model == null) return null;
var propId = model.GetType().GetProperty("id");
var propName = model.GetType().GetProperty("name");
if (propId == null || propName == null)
return;
return new IdNameVM()
{
Id = model.Id,
Name = model.Name
};
}
Upvotes: 0
Reputation: 3991
For creating a Generic Method, you have to keep in mind the few rules
Let's take the above mentioned example: You need a generic method of name HydrateEntityToVM
. The very first step is the declaration syntax of the method along and object casting from T type of Strong Type.
Point to be noted here is you are passing a T type and want a resultant to be always IdNameVM class then your implementation should be as:
//The Method declaration of T type
private IdNameVM HydrateEntityToVM<T>(T projectValue)
{
//Checking whether the object exists or not
//if null than return the default value of IdNameVM
if(projectValue == null) return default(IdNameVM);
//Checking if we are supporting the implementation or not for that class
//In your case YourStrongTypeClass is ProjectValue Class
if(projectValue is YourStrongTypeClass)
{
//Casting the T object to strong type object
var obj = projectValue as YourStrongTypeClass;
return new IdNameVM()
{
Id = obj.Id,
Name = obj.Name
};
}
else
{
//The else statement is for if you want to handle some other type of T implementation
throw new NotImplementedException($"The type {typeof(T)} is not implemented");
}
}
Upvotes: 1