First, a quick overview on what I'm trying to do: I want to take a C# expression, serialize it, send it to another process, de-serialize it, and use it to filter a list. Here's the caveat - when the expression is created it is done so going against a generic parameter type T but when it is de-serialized it needs to go against a dynamic instead. The reason for this is that when it is eventually used on a server in a different process it will be done so against a list of dynamics as I will not have type information in that context.
I feel like I'm close as I've used the Newtonsoft.Json and Serialize.Linq libraries to put together a proof of concept but I can only get it to work without using dynamics (e.g. I have the type T available on both the serializing side (client) and the de-serializing side (server). On to some code to show you what I have...
Given this:
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
}
...as the type we are working with. I have a client that has interface:
public interface IClient
{
T Get<T>(Expression<Func<T, bool>> query);
}
...so that I can do this:
var client = new Client();
var john = client.Get<Person>(p => p.Name == "John");
...all is well and good so far. In the client.Get() method I am taking the passed expression and serializing it down and sending to the server. The server looks as such:
public dynamic Server(string serializedExpression)
{
var people = new List<dynamic>
{
new { Name = "John", Email = "john#stackoverflow.com" },
new { Name = "Jane", Email = "jane#stackoverflow.com" }
};
var serializer = new ExpressionSerializer(new JsonSerializer());
var deserializedExpression = (Expression<Func<dynamic, bool>>)serializer.DeserializeText(serializedExpression);
return people.FirstOrDefault(deserializedExpression.Compile());
}
...and here is where the problems happen because I'm trying to deserialize it into a type of
Expression<Func<dynamic, bool>>
...instead of...
Expression<Func<Person, bool>>.
So my questions are:
1) Is what I'm trying to do even possible? It seems like using an ExpressionVisitor you can change the generic parameter types and I've tried to do so to change from Person to dynamic before I serialize and send but have had no luck.
2) Is there a better way to do what I want to accomplish? I know the first question will be why don't I just get access to the type T specified in the expression Func<> on the server but that won't be possible due to the nature of the server. I basically want the luxury of using Linq on the client to specify query predicates while executing those predicates against dynamic lists.
Thanks in advance for any answers or ideas you can provide.
Regards,
Craig
LINQ doesn't like dynamics in Expressions much. (DLINQ perhaps?)
Alternatively, you could pass along a hint to the server about which object type you're using. I realize this is probably not what you're looking for, but it is working:
(borrowed from this CodeProject article)
public static class Extensions
{
public static object ToType<T>(this object obj, T type)
{
//create instance of T type object:
var tmp = Activator.CreateInstance(Type.GetType(type.ToString()));
//loop through the properties of the object you want to covert:
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
try
{
//get the value of property and try
//to assign it to the property of T type object:
tmp.GetType().GetProperty(pi.Name).SetValue(tmp,
pi.GetValue(obj, null), null);
}
catch { }
}
//return the T type object:
return tmp;
}
public static object ToNonAnonymousList<T>(this List<T> list, Type t)
{
//define system Type representing List of objects of T type:
var genericType = typeof(List<>).MakeGenericType(t);
//create an object instance of defined type:
var l = Activator.CreateInstance(genericType);
//get method Add from from the list:
MethodInfo addMethod = l.GetType().GetMethod("Add");
//loop through the calling list:
foreach (T item in list)
{
//convert each object of the list into T object
//by calling extension ToType<T>()
//Add this object to newly created list:
addMethod.Invoke(l, new object[] { item.ToType(t) });
}
//return List of T objects:
return l;
}
}
... with some not-so-nice branching on the server:
public interface IClient
{
T Get<T>(Expression<Func<T, bool>> query);
}
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
}
public class Client
{
public T Get<T>(Expression<Func<dynamic, bool>> query)
{
var serializer = new ExpressionSerializer(new JsonSerializer());
var serializedExpression = serializer.SerializeText(query);
return (T)Server.Retrieve(serializedExpression, typeof(T).FullName);
}
}
public static class Server
{
public static dynamic Retrieve(string serializedExpression, string targetType)
{
var people = new List<dynamic>
{
new { Name = "John", Email = "john#stackoverflow.com" },
new { Name = "Jane", Email = "jane#stackoverflow.com" }
};
// Try creating an object of the type hint passed to the server
var typeInstance = Activator.CreateInstance(Type.GetType(targetType));
if (typeInstance.GetType() == typeof(Person))
{
var serializer = new ExpressionSerializer(new JsonSerializer());
var deserializedExpression = (Expression<Func<Person, bool>>)serializer.DeserializeText(serializedExpression);
var peopleCasted = (IEnumerable<Person>)people.ToNonAnonymousList(typeof(Person));
return peopleCasted.Where(deserializedExpression.Compile()).SingleOrDefault();
}
else
{
throw new ArgumentException("Type is unknown");
}
}
}
and a working test:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void MyTestMethod()
{
var client = new Client();
var john = client.Get<Person>(p => p.Name == "John");
Assert.IsNotNull(john);
}
}
Related
I need to return one row of List from my function Selectus.
So I pass to the function Selectus object that reflects database table fields and I need to return one row which match the parameter looking_for:
public static List<T> Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.ToList();//here one record should be returned
}
I do not know how to select one row from the list assuming that T parameter is vary. In SelectusT> method I want to pass as T different objects which reflect fields in database table rather than creatinig separate methods for each select. e.g. call Selectus, where object passed is public class ProductCodes { public int ID { get; set; } public string SapIndex { get; set; } public string SapName { get; set; } }. Then I want to call another Selectus<ProductTypes> for another table etc... So I want to write generic/overall method and use it universally for all types of my objects which reflects the fields of few database tables. The SapIndex property is always in the same place of all objects...
Using prop[1] is incredibly fragile. Who says that the property you're currently interested in is always going to be in second place? What if someone adds another property tomorrow? What if not every T that you use have the same property in the second place on its list of properties? It is quite unclear what your actual goal is here and why you've taken the reflection route.
You would be better off using inheritance or interface implementation here. I'm going to use an interface in this answer, but either would work.
For the sake of clarity, let's assume there is a Code field in all your possible lists, and this is the property you're trying to match with.
Define a reusable interface:
public interface ICodeEntity
{
string Code { get; }
}
Apply your interface to all of the classes that you intend to use for your Selectus method.
public class Person : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
public class Document : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
Add a generic type constraint that limits the use of T only to types that implement your interface.
public static List<T> Selectus<T>(string code)
where T : ICodeEntity
You can now write your code in a way that it relies on the type in question having a Code property, and the compiler will help enforce it.
var db = OrmLiteBaza().Open();
var list = db.Select<T>().ToList();
db.Dispose();
return list.Where(item => item.Code == code).ToList();
Usage examples:
List<Person> peopleWithCodeABC = Selectus<Person>("ABC");
List<Person> documentsWithCodeXYZ = Selectus<Document>("XYZ");
// This will fail if Animal does not implement ICodeEntity
var compilerError = Selectus<Animal>("ABC");
I might not understand fully what you want, but instead of string looking_for you could pass in a Func<,> delegate which acts as a selector.
Something like:
public static List<TField> Selectus<T, TField>(Func<T, TField> selector)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Select(selector); // 'using System.Linq;'
return list_selected_record.ToList();
}
Then I believe it could be called like this:
var list_one = Selectus((ProductCodes x) => x.SapIndex);
var list_two = Selectus((ProductTypes x) => x.SapIndex);
var list_three = Selectus((ProductCodes x) => x.SapName);
With this syntax I leave out the <ProductCodes, string> generic arguments to the method since they can be inferred.
Hmm, maybe you want it in the opposite dimension. You could do:
public static List<T> Selectus<T>(Func<T, bool> predicate)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Where(predicate); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus((ProductCodes x) => x.SapIndex == "ABC");
var list_two = Selectus((ProductTypes x) => x.SapIndex == "ABC");
var list_three = Selectus((ProductCodes x) => x.SapName == "DaName");
or:
var list_one = Selectus<ProductCodes>(x => x.SapIndex == "ABC");
var list_two = Selectus<ProductTypes>(x => x.SapIndex == "ABC");
var list_three = Selectus<ProductCodes>(x => x.SapName == "DaName");
But if it is going to always be the "same" property, like always x.SapIndex (but for different types of x), then Flater's answer looks good.
Otherwise, if you insist, your reflection approach should be possible. Use propety's name, not its index! Let me try:
public static List<T> Selectus<T>(string looking_for)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
const string prop_name = "SapIndex";
var prop = typeof(T).GetProperty(prop_name); // can blow up for bad T
var list_selected_record = select_all_list
.Where(x => (string)(prop.GetValue(x)) == looking_for); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus<ProductCodes>("ABC");
var list_two = Selectus<ProductTypes>("ABC");
you can change code to return just one element
public static T Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.FirstOrDefault();//here one record should be returned
}
I have following assemble which contains following data
demo.dll
Class 1
namespace demo
public class Data
{
public string FirstName {get; set;}
}
Class 2
namespace demo
public class Utility
{
private List<Data> items;
public Utility()
{
items = new List<Data>();
items.add(new Data(){ FirstName = "Abc" });
}
public List<Data> GetItems()
{
return items;
}
}
Now I want to load above demo.dll assemble using runtime and call GetItem() method.
For that I can write following code
Assembly assembly = Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ,"demo.dll"));
Type Utility= assembly.GetType("demo.Utility");
Type Data= assembly.GetType("demo.Data");
MethodInfo GetItems = Utility.GetMethod("GetItems");
object utility = Activator.CreateInstance(Utility);
object returnValue = GetItems.Invoke(utility, null);
Now I want to type cast above returnValue in List of type Data and access the FirstName property.
How can I achieve this using reflection?
Assuming that everything you wrote works then
object returnValue = GetItems.Invoke(utility, null);
returnValue is actually a List<Data>, but you can't cast it, because at compile time the compiler doesn't have the type information of the Data class. But since a List<T> implements IList you can cast to that and iterate over its items (IEnumerable<object> should work too).
Then you can use reflection again to access the FirstName property:
var returnValue = (IList)GetItems.Invoke(utility, null);
foreach (var item in returnValue)
{
var type = item.GetType();
var property = type.GetProperty("FirstName");
var firstName = property.GetValue(item);
}
I'm currently having some issues with a method I made. I use reflection to run through my class and get all it's properties. I use this to cast my models to DTO and vice-versa.
The problem I am encountering is that, whenever my class has another class as an attribute, I get an error.
Object of type 'UserTypeProxy' cannot be converted to type 'MyNamespace.DTO.UserTypeDto'.
This is my code:
public static T Cast<T>(object myobj)
{
Type _objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in _objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
var propy = myobj.GetType().GetProperty(memberInfo.Name);
value = propy.GetValue(myobj, null);
propertyInfo.SetValue(x, value, null); //<-- this is the line that gives the error
}
return (T)x;
}
As a previous commenter states, this is not the kind of code you should be writing/maintaining yourself. Frameworks like AutoMapper were built specifically to solve the problem you are attacking - converting model objects to DTOs. The right long-term choice would be to start leveraging such a framework instead of reinventing the wheel.
In the meanwhile the following code is a short-term solution for your issue. Keep in mind that while this may solve the specific case you mention in your question, object mapping has many corner cases and eventually you will run into another. I would recommend only using this as a temporary fix until you migrate to using AutoMapper or a similar framework.
Based on your description and your code, here is an example which models your failure:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This fails saying that UserModel cannot be converted to UserDto
var userDto = Cast<UserDto>(user);
}
class UserModel
{
public String Name { get; set; }
public int Age { get; set; }
public UserModel Buddy { get; set; }
}
class UserDto
{
public String Name { get; set; }
public int Age { get; set; }
public UserDto Buddy { get; set; }
}
The problem is that the Buddy property, unlike all the others, has a different type in the model and DTO classes. A UserModel is simply not assignable to a UserDto. The only exception to this is if the value is null.
For properties which are class types, instead of setting the target equal to the source you need to map the source type to the target type: UserModel -> UserDto. This can be done with a recursive call.
Before I show you the code which solves this issue, let's talk about naming for a minute. Calling your function Cast() is very misleading. The operation we are really doing here is taking some source object and mapping its property values onto some target object of a specific type (with possible recursive mappings for properties which are class types).
Given this terminology, here is some updated code which solves this specific issue:
public static T MapProperties<T>(object source)
{
return (T)MapProperties(source, typeof(T));
}
public static object MapProperties(object source, Type targetType)
{
object target = Activator.CreateInstance(targetType, nonPublic: false);
Type sourceType = source.GetType();
var sourcePropertyLookup = sourceType.GetProperties().ToDictionary(p => p.Name);
var targetPropertyLookup = targetType.GetProperties().ToDictionary(p => p.Name);
var commonProperties = targetPropertyLookup.Keys.Intersect(sourcePropertyLookup.Keys);
foreach (var commonProp in commonProperties)
{
PropertyInfo sourceProp = sourcePropertyLookup[commonProp];
PropertyInfo targetProp = targetPropertyLookup[commonProp];
object sourcePropValue = sourceProp.GetValue(source);
if(sourcePropValue == null || targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
targetProp.SetValue(target, sourceProp.GetValue(source));
}
else
{
object mappedValue = MapProperties(sourceProp.GetValue(source), targetProp.PropertyType);
targetProp.SetValue(target, mappedValue);
}
}
return target;
}
You can use this in the same way you've used your previous code:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This works!
var userDto = MapProperties<UserDto>(user);
}
Aside from some optimizations the key differences from your code is in the if-else block. There we check if we can assign the source value to the target directly, in which case we do what your code was doing so far. Otherwise it assumes we need to recursively map the value over. This new section is what solves the issue of converting a source property of a model class type to a target property of a DTO class type.
Consider the following scenario:
public class DestinationType1 { }
public class DestinationType2 { }
public class SourceType { }
public class SourceTypeA : SourceType { }
public class SourceTypeB : SourceType { }
I set up these mappings:
Mapper.CreateMap<SourceType, DestinationType2>();
Mapper.CreateMap<SourceTypeB, DestinationType2>();
Mapper.CreateMap<SourceType, DestinationType1>();
Mapper.CreateMap<SourceTypeA, DestinationType1>();
Then try to map the following:
var sourceTypes = new List<SourceType>{new SourceTypeA(), new SourceTypeB()};
var destinationType1s = Mapper.Map<List<DestinationType2>>(sourceTypes);
var destinationType2s = Mapper.Map<List<DestinationType1>>(sourceTypes);
What i want to achieve is for destinationType1s to only have one member, mapped from the SourceTypeA in the source list, and destinationType2s to only have one mapped from SourceTypeB. However what i get is two elements in both lists mapped from both the source types.
Is this achievable somehow out of the box or do i need to write my own value resolver or similar?
You could use the OfType LINQ extension method to filter the sourceTypes list.
var sourceTypes = new List<SourceType>{new SourceTypeA(), new SourceTypeB()};
var destinationType1s = Mapper.Map<List<DestinationType1>>(sourceTypes.OfType<SourceTypeA>());
var destinationType2s = Mapper.Map<List<DestinationType2>>(sourceTypes.OfType<SourceTypeB>());
OfType<type> will produce an IEnumerable<type> so you could also remove the 2 maps for the base SourceType if you're not going to use them.
If you want to filter for more than one type, then you could create your own extension method similar to OfType that takes a list of types or takes the DestinationType and looks up which types are mapped to it. Here's something that works using Mapper.FindTypeMapFor to filter only compatible types:
//Mapper.CreateMap<SourceType, DestinationType2>(); -- don't want this!
Mapper.CreateMap<SourceTypeB, DestinationType2>();
//Mapper.CreateMap<SourceType, DestinationType1>(); -- don't want this!
Mapper.CreateMap<SourceTypeA, DestinationType1>();
var sourceTypes = new List<SourceType> { new SourceTypeA(), new SourceTypeB() };
var destinationType1s = Mapper.Map<List<DestinationType1>>(sourceTypes.CompatibleMappedTypes<DestinationType1>());
var destinationType2s = Mapper.Map<List<DestinationType2>>(sourceTypes.CompatibleMappedTypes<DestinationType2>());
...
static class Extensions
{
public static IEnumerable CompatibleMappedTypes<TDestination>(this IEnumerable source)
{
foreach (var s in source)
{
if (Mapper.FindTypeMapFor(s.GetType(), typeof(TDestination)) != null) yield return s;
}
}
}
I'm currently struggling with finding a better way to populate my ViewModel objects with my Entitiy objects. I have the following Web Api controller method:
[HttpGet]
public IEnumerable<ClientSearchViewModel> FindClients(string query)
{
var clients = _clientService.SearchClient(query).ToList();
var returnClients = new List<ClientSearchViewModel>();
foreach (var client in clients)
{
returnClients.Add(new ClientSearchViewModel(client));
}
return returnClients;
}
And I'm doing this in my ClientSearchViewModel constructor:
public ClientSearchViewModel(Client client)
{
this.Id = client.Id;
this.FirstName = client.PersonName.FirstName;
this.LastName = client.PersonName.LastName;
}
Is there another way other than going through the list of returned objects and creating a new ViewModel list?
I strongly suggest use of a mapping plugin for this, such as:
AutoMapper
or
ValueInjector
Plugins like this will allow you to map between the objects being used internally or in your data layer, with your external objects (DTOs/ViewModels). They handle a number of things out of the box such as automatic mapping of any like named properties with the same type, but also allow for a lot of control in the specific mapping of properties or types, for those times when you need something more custom.
For a brief comparison of the two, there isn't much better than hearing the authors themselves respond: AutoMapper vs ValueInjecter
Personally, I find ValueInjector to be quicker to use, while having more control overall, but I also find it to be much less readable/inuitive than AutoMapper, which can require a bit more code to accomplish similar goals. As such, I'd pick the one that you find you and/or your team will prefer the syntax of and how easily you can grasp the concepts vs how much power you really need.
So I had the same miff... I can't say that I've benchmarked my solution, but it does seem to run reasonably fast...
3 bits:
public static T Transform<T>(this object convertFrom) where T : class, new()
{
return (T) (new ServiceExtension().Transform(convertFrom, typeof (T)));
}
private class ServiceExtension
{
public object Transform(object convertFrom, Type convertTo)
{
object _t = Activator.CreateInstance(convertTo);
if (convertFrom == null) return _t;
var convertType = convertFrom.GetType();
foreach (
var property in _t.GetType().GetProperties().Where(f => f.CanWrite && f.GetSetMethod(true).IsPublic)
)
{
if (property.GetCustomAttributes(typeof (TransformAttribute), true).Any())
{
var transform =
(property.GetCustomAttributes(typeof (TransformAttribute), true).FirstOrDefault() as
TransformAttribute);
var transformname = transform.RelatedField ?? property.Name;
if (convertType.GetProperty(transformname) == null)
throw new ArgumentException(
string.Format(
"We were unable to find property:\"{0}\" on {1}. Please check the RelativeField value on the {2} for \"{0}\"",
transformname, convertFrom.GetType().Name, convertTo.Name));
var theValue = convertType.GetProperty(transformname).GetValue(convertFrom);
if (isCollection(theValue))
{
foreach (var item in (theValue as ICollection))
{
var someVal = new object();
var newToType = property.PropertyType.GetGenericArguments().FirstOrDefault();
if (!String.IsNullOrEmpty(transform.FullyQualifiedName))
someVal =
Transform(
item.GetType().GetProperty(transform.FullyQualifiedName).GetValue(item),
newToType);
else
someVal = Transform(item, newToType);
if (property.GetValue(_t) == null)
throw new NullReferenceException(
string.Format(
"The following property:{0} is null on {1}. Likely this needs to be initialized inside of {1}'s empty constructor",
property.Name, _t.GetType().Name));
property.PropertyType.GetMethod("Add")
.Invoke(property.GetValue(_t), new[] {someVal});
//property.SetValue(_t, theValue.Transform(theValue.GetType()));
}
}
else
property.SetValue(_t, theValue);
}
//property.SetValue(_t, property.GetValue(convertFrom, null), null);
}
return _t;
}
public bool isCollection(object o)
{
return o is ICollection
|| typeof (ICollection<>).IsInstanceOfType(o);
}
}
public class TransformAttribute : Attribute
{
public string RelatedField { get; private set; }
public string FullyQualifiedName { get; set; }
public TransformAttribute()
{
}
public TransformAttribute(string relatedField)
{
RelatedField = relatedField;
}
}
such that the end result is: myObject.Transform()
But the decorations let you account for differences between your POCO and your ViewModel