Protobuf-net: How to serialize complex collection? - c#

I'm trying to serialize this type of object using protobuf-net:
[ProtoContract]
public class RedisDataObject
{
[ProtoMember(1)]
public string DataHash;
[ProtoMember(2, DynamicType = true)]
public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
}
[Serializable]
public enum ContextActions
{
Insert,
Update,
Delete
}
I'm using List<object> because I'm storing there different class instances of other classes I have in my code.
But I'm getting this error message:
Unable to resolve a suitable Add method for System.Collections.Generic.Dictionary...
This is clearly because of the dictionary, but I couldn't find a solution how to resolve this issue.

Your basic problem is that DynamicType = true applies to that specific property only, and serializes type information for the value of that specific property only. It doesn't apply recursively to any of the properties contained by that object. However, your object value is nested deep within several levels of container:
public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
What you need to do is to serialize type information for each object inside this dictionary of tuples of lists. You can do this by introducing a surrogate value type:
[ProtoContract]
public struct DynamicTypeSurrogate<T>
{
[ProtoMember(1, DynamicType = true)]
public T Value { get; set; }
}
public static class DynamicTypeSurrogateExtensions
{
public static List<DynamicTypeSurrogate<T>> ToSurrogateList<T>(this IList<T> list)
{
if (list == null)
return null;
return list.Select(i => new DynamicTypeSurrogate<T> { Value = i }).ToList();
}
public static List<T> FromSurrogateList<T>(this IList<DynamicTypeSurrogate<T>> list)
{
if (list == null)
return null;
return list.Select(i => i.Value).ToList();
}
}
And then modifying your RedisDataObject to serialize a surrogate dictionary as follows:
[ProtoContract]
public class RedisDataObject
{
[ProtoMember(1)]
public string DataHash;
[ProtoIgnore]
public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
[ProtoMember(2)]
private Dictionary<ContextActions, List<Tuple<string, List<DynamicTypeSurrogate<object>>>>> SurrogateValue
{
get
{
if (Value == null)
return null;
var dictionary = Value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.ToSurrogateList())).ToList()));
return dictionary;
}
set
{
if (value == null)
Value = null;
else
{
Value = value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.FromSurrogateList())).ToList()));
}
}
}
}
Note also the restrictions on DynamicType mentioned here:
DynamicType - stores additional Type information with the type (by default it includes the AssemblyQualifiedName, although this can be controlled by the user). This makes it possible to serialize weak models, i.e. where object is used for property members, however currently this is limited to contract types (not primitives), and does not work for types with inheritance (these limitations may be removed at a later time). Like with AsReference, this uses a very different layout format
While the documentation above exists at the former project site and has not been moved to the current site, the restriction on non-contract types definitely still exists as of version 2.0.0.668. (I tested that adding an int value to the List<object> fails; I have not checked whether the restriction on inheritance still exists.)

With help from dbc, and all links mentioned in his answer and comments
[ProtoContract]
[ProtoInclude(1, typeof(ObjectWrapper<int>))]
[ProtoInclude(2, typeof(ObjectWrapper<decimal>))]
[ProtoInclude(3, typeof(ObjectWrapper<DateTime>))]
[ProtoInclude(4, typeof(ObjectWrapper<string>))]
[ProtoInclude(5, typeof(ObjectWrapper<double>))]
[ProtoInclude(6, typeof(ObjectWrapper<long>))]
[ProtoInclude(8, typeof(ObjectWrapper<Custom>))]
[ProtoInclude(9, typeof(ObjectWrapper<CustomType[]>))]
public abstract class ObjectWrapper
{
protected ObjectWrapper() { }
abstract public object ObjectValue { get; set; }
public static ObjectWrapper Create(object o) {
Type objectType = o.GetType();
Type genericType = typeof(ObjectWrapper<>);
Type specializedType = genericType.MakeGenericType(objectType);
return (ObjectWrapper)Activator.CreateInstance(specializedType, new object[] { o });
}
}
Downside is you have to register all types you are using in your list of objects. Every time a new type that is not included in the ProtoInclude series surfaces out, you would get an InvalidOperationException with message Unexpected sub-type: ObjectWrapper`1[[NewType]].
[ProtoContract]
public class RedisDataObjectWrapper {
[ProtoMember(1)] public string DataHash;
[ProtoIgnore] public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
[ProtoMember(2)]
private Dictionary<ContextActions, List<Tuple<string, List<ObjectWrapper>>>> AdaptedValue {
get {
if (Value == null) return null;
var dictionary = Value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>ObjectWrapper.Create(x)).ToList() )).ToList()));
return dictionary;
}
set {
if (value == null) Value = null;
else {
Value = value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>x.ObjectValue).ToList() )).ToList()));
} } } }

Related

Mapping similar objects using reflection: Object does not match target type

I am at a complete loss here, despite looking at multiple SO posts and anything else I can think of.
My goal here is to make a really, really simple mapper. Something I can basically use as a tool in some unit tests. It doesn't need to be sophisticated or anything -- just map high-level primitive and string values of one object to another. So the basic algorithm is:
Get all properties from TFrom
Get all properties from TTo
Get all properties that are in both, matched by name.
I know this could be a bug in that they could have the same name but a different type, but let's set that aside. It's not what I'm running into here -- the properties and types match between classes.
Create an instance of TTo that we can copy to.
For each property that was mapped between the objects:
Get the value off of the from object
Convert the value to the type of the property
Set the value on the to object
The problem is that no matter what I do, and no matter what the type of the property is (int or string, for example) I get the following:
Object does not match the target type.
Here is the code I'm using:
public TTo Map<TFrom, TTo>(TFrom from)
{
if (from == null) return default;
var fromProps = GetProperties(typeof(TFrom));
var toProps = GetProperties(typeof(TTo));
// Props that can be mapped from one to the other
var propsToCopy = fromProps.Intersect(toProps, new PropertyComparer()).ToList();
var returnObject = (TTo)Activator.CreateInstance(typeof(TTo));
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(returnObject, convertedValue, null);
}
return returnObject;
}
public PropertyInfo[] GetProperties(Type objectType)
{
var allProps = objectType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
return allProps.Where(p => p.PropertyType.IsPrimitive ||
p.PropertyType == typeof(string)).ToArray();
}
private class PropertyComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
And here's an example of a way I would call it, with sample classes:
public class Foo
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
public class FooOther
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
var foo = new Foo { IntProp = 1, StringProp = "foo" };
var mappedFoo = Map<Foo, FooOther>(foo);
About the only hint I've gotten out of Visual Studio is from the watch window: if the property type is a string, the watch window reports the type of convertedValue as object. If the property type is an int, the watch window reports object {int}.
The PropertyInfo you are using is still coupled to the type the property it is representing is a member of, so you aren't able to use it to set the value of an object of another type without the error you are getting.
Here's a shortened example of the behavior:
public class A {
public string Id {get;set;}
}
public class B {
public string Id {get;set;}
}
void Main()
{
var test = new A() { Id = "Test"};
var prop = test.GetType().GetProperty("Id");
var b = (B)Activator.CreateInstance(typeof(B));
var fromValue = prop.GetValue(test);
var converted = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(b, converted, null); // Exception
}
This makes sense if you think of the PropertyInfo as a member of A. To fix this, you'll want to get a property info that's specific to your type. I can fix up my example with the following:
var propTo = typeof(B).GetProperty(prop.Name);
propTo.SetValue(b, converted, null);
Console.WriteLine(b.Id); // Output: Test
Bringing that together, if you change the contents of your foreach to the following you should be in the clear:
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
var propTo = typeof(TTO).GetProperty(prop.Name);
propTo.SetValue(returnObject, convertedValue, null);
}

Recursion on nested Generic classes

I have an issue.
Say I have a Generic class which can have generic properties of other classes and can even have a list of other classes.
If i have a function like
public void Read<T>() where T: class, new()
{
// Create an instance of our generic class
var model = new T();
var properties = typeof(T).GetProperties();
// Loop through the objects properties
for(var property in properties) {
// Set our value
SetPropertyValue(property, model, "test");
}
}
private void SetPropertyValue(PropertyInfo property, object model, string value) {
// Set our property value
property.SetValue(model, value, null);
}
that would work if I had a class like this:
public class Person
{
public string Name { get; set; }
}
and I invoked the Read method like this:
Read<Person>();
But if my Person model was like this:
public class Person
{
public string Name { get; set; }
public Company Company { get; set; }
}
public class Company
{
public string Name { get; set; }
}
And I tried to invoke the Read method again, it would fail because of the property have it's own list of properties.
What would be better is if it traversed them too. Is there a way to do that?
This answer can help.
You should end with something like this:
if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String) || ... )
SetPropertyValue(property, model, "test");
else
// You will have to recode this line,
// it's just to show you the idea how you can work around
SetPropertyValue(property, model, Read.MakeGeneric(property.Type)());
You will also need to return your model variable from your Read method.
The condition is depending on what type you want to overwrite, if it's like on your example, you can change the condition to match only strings and add a check on the else to check property that are objects.
You can set the property value directly if it is a string, otherwise you can return value of a method similar to Read that takes Type as a parameter to create a model and fill its properties recursively.
public void Read<T>() where T : class, new()
{
// Create an instance of our generic class
var model = new T();
var properties = typeof(T).GetProperties();
// Loop through the objects properties
foreach(var property in properties)
{
// Set our value
SetPropertyValue(property, model, "test");
}
}
private void SetPropertyValue(PropertyInfo property, object model, string value)
{
if (property.PropertyType == typeof(string))
{
// Set our property value
property.SetValue(model, value, null);
}
else
{
var submodel = Read(property.PropertyType);
property.SetValue(model, submodel, null);
}
}
private object Read(Type type)
{
if (!IsTypeSupported(type))
{
throw new ArgumentException();
}
var model = type.GetConstructor(new Type[0]).Invoke(new object[0]);
var properties = type.GetProperties();
foreach (var property in properties)
{
SetPropertyValue(property, model, "test");
}
return model;
}
private bool IsTypeSupported(Type type)
{
return type.IsClass && type.GetConstructor(new Type[0]) != null;
}

updating value by extension method without return

now I use like this;
c1 = c1.AddItem(d1);
public static T AddItem<T, TItem>(this T entity, TItem item) where T : BaseClass<T>
{
//somecode
return entity;
}
but I want to be able to change value without return;
c1.AddItem(d1);
public static void AddItem<T, TItem>(this T entity, TItem item) where T : BaseClass<T>
{
//somecode
}
any idea how to make this work?
FULL CODE
public class C : BaseClass<C>
{
public virtual int CId { get; set; }
public virtual string Text { get; set; }
public virtual IList<D> DList { get; set; }
public C()
{
DList = new List<D>();
}
}
public static T AddItem<T, TItem>(this T entity, TItem item) where T : BaseClass<T>
{
var propertyList = item.GetType().GetProperties().ToList();
var prop = propertyList.Find(c => c.PropertyType == typeof(T));
if (propertyList.Any(c => c.PropertyType == typeof(T)))
{
propertyList.Find(c => c.PropertyType == typeof(T)).SetValue(item, entity);
}
else
{
((IList<T>)propertyList.Find(c => c.PropertyType == typeof(IList<T>)).GetValue(item)).Add(entity);
}
var a = ((IList<TItem>)entity.GetType().GetProperties().ToList().Find(c => c.PropertyType == typeof(IList<TItem>)).GetValue(entity));
if (a.GetType().Name == "PersistentGenericBag`1")
{
entity = entity.Load(x => (IList<TItem>)x.GetType().GetProperties().ToList().Find(c => c.PropertyType == typeof(IList<TItem>)).GetValue(x));
a = ((IList<TItem>)entity.GetType().GetProperties().ToList().Find(c => c.PropertyType == typeof(IList<TItem>)).GetValue(entity));
}
a.Add(item);
entity.Save();
return entity;
}
public static T Load<T, TItem>(this T entity, Func<T, IList<TItem>> listGetter) where T : class
{
using (var session = NHibernateHelper<T>.OpenSession())
{
T reAttached = session.Merge<T>(entity);
NHibernate.NHibernateUtil.Initialize(listGetter.Invoke(reAttached));
return reAttached;
}
}
Your problem appears to be caused by this line:
entity = entity.Load(x => (IList<TItem>)x.GetType().GetProperties().ToList().Find(c => c.PropertyType == typeof(IList<TItem>)).GetValue(x));
Now that you have posted the contents of the Load method, we can see that it, in turn, calls:
T reAttached = session.Merge<T>(entity);
// ...
return reAttached;
My knowledge of Hibernate is limited, but chances are good that, if not always, at least sometimes, the call to Merge will return a reference to a different object instance from what is passed in.
This has the cascading effect that, in your extension method, the local variable entity gets reassigned to a completely new object. Because the entity reference variable is a copy of the c1 reference variable (and not a reference to it), when it gets reassigned, the change is not reflected in the c1 variable. Your c1 variable effectively still points to the original entity instance before it got changed by the call to BaseClass.Load().
Basically, as others have already stated, to code an extension method that doesn't need to return an entity reference, you have to limit yourself to changing the state of the entity object through its methods/properties. You can't actually change the object reference completely, because this will never be reflected outside the method call.
In your case, it looks like you should stick with your original extension method that returns the entity reference.
Relevant reading: Parameter passing in C#
In particular, the section Sidenote: what is the difference between passing a value object by reference and a reference object by value? applies to what is happening here.
Strings are immutable, with other class-types your code should work.
public class BaseClass<TItem>
{
public BaseClass()
{
ItemList = new List<TItem>();
}
public List<TItem> ItemList { get; set; }
}
public static void AddItem<T, TItem>(this T entity, TItem item)
where T :BaseClass<TItem>
{
//somecode
entity.ItemList.Add(item);
}
public class TItem
{
public string Name { get; set; }
}
static void Main(string[] args)
{
BaseClass<TItem> myEntity = new BaseClass<TItem>();
TItem newItem = new TItem();
newItem.Name = "Hi";
myEntity.AddItem(newItem);
}

How to check all properties of an object whether null or empty?

I have an object lets call it ObjectA
and that object has 10 properties and those are all strings.
var myObject = new {Property1="",Property2="",Property3="",Property4="",...}
is there anyway to check to see whether all these properties are null or empty?
So any built-in method that would return true or false?
If any single of them is not null or empty then the return would be false. If all of them are empty it should return true.
The idea is I do not want to write 10 if statement to control if those properties are empty or null.
Thanks
You can do it using Reflection
bool IsAnyNullOrEmpty(object myObject)
{
foreach(PropertyInfo pi in myObject.GetType().GetProperties())
{
if(pi.PropertyType == typeof(string))
{
string value = (string)pi.GetValue(myObject);
if(string.IsNullOrEmpty(value))
{
return true;
}
}
}
return false;
}
Matthew Watson suggested an alternative using LINQ:
return myObject.GetType().GetProperties()
.Where(pi => pi.PropertyType == typeof(string))
.Select(pi => (string)pi.GetValue(myObject))
.Any(value => string.IsNullOrEmpty(value));
I suppose you want to make sure that all properties are filled in.
A better option is probably by putting this validation in the constructor of your class and throw exceptions if validation fails. That way you cannot create a class that is invalid; catch exceptions and handle them accordingly.
Fluent validation is a nice framework (http://fluentvalidation.codeplex.com) for doing the validation. Example:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Property1).NotNull();
RuleFor(customer => customer.Property2).NotNull();
RuleFor(customer => customer.Property3).NotNull();
}
}
public class Customer
{
public Customer(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
new CustomerValidator().ValidateAndThrow();
}
public string Property1 {get; set;}
public string Property2 {get; set;}
public string Property3 {get; set;}
}
Usage:
try
{
var customer = new Customer("string1", "string", null);
// logic here
} catch (ValidationException ex)
{
// A validation error occured
}
PS - Using reflection for this kind of thing just makes your code harder to read. Using validation as shown above makes it explicitly clear what your rules are; and you can easily extend them with other rules.
The following code returns if any property is not null.
return myObject.GetType()
.GetProperties() //get all properties on object
.Select(pi => pi.GetValue(myObject)) //get value for the property
.Any(value => value != null); // Check if one of the values is not null, if so it returns true.
Here you go
var instOfA = new ObjectA();
bool isAnyPropEmpty = instOfA.GetType().GetProperties()
.Where(p => p.GetValue(instOfA) is string) // selecting only string props
.Any(p => string.IsNullOrWhiteSpace((p.GetValue(instOfA) as string)));
and here's the class
class ObjectA
{
public string A { get; set; }
public string B { get; set; }
}
A slightly different way of expressing the linq to see if all string properties of an object are non null and non empty:
public static bool AllStringPropertyValuesAreNonEmpty(object myObject)
{
var allStringPropertyValues =
from property in myObject.GetType().GetProperties()
where property.PropertyType == typeof(string) && property.CanRead
select (string) property.GetValue(myObject);
return allStringPropertyValues.All(value => !string.IsNullOrEmpty(value));
}
Note if you've got a data structural hierarchy and you want to test everything in that hierarchy, then you can use a recursive method. Here's a quick example:
static bool AnyNullOrEmpty(object obj) {
return obj == null
|| obj.ToString() == ""
|| obj.GetType().GetProperties().Any(prop => AnyNullOrEmpty(prop.GetValue(obj)));
}
To only check if all properties are null:
bool allPropertiesNull = !myObject.GetType().GetProperties().Any(prop => prop == null);
you can use reflection and extension methods to do this.
using System.Reflection;
public static class ExtensionMethods
{
public static bool StringPropertiesEmpty(this object value)
{
foreach (PropertyInfo objProp in value.GetType().GetProperties())
{
if (objProp.CanRead)
{
object val = objProp.GetValue(value, null);
if (val.GetType() == typeof(string))
{
if (val == "" || val == null)
{
return true;
}
}
}
}
return false;
}
}
then use it on any object with string properties
test obj = new test();
if (obj.StringPropertiesEmpty() == true)
{
// some of these string properties are empty or null
}
No, I don't think there is a method to do exactly that.
You'd be best writing a simple method that takes your object and returns true or false.
Alternatively, if the properties are all the same, and you just want to parse through them and find a single null or empty, perhaps some sort of collection of strings would work for you?
You can try the following query :
if the object is "referenceKey" (where few properties may be null )
referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) == null)
I need to count the properties where the value is set to not Null, so I have used the following query :
var countProvidedReferenceKeys = referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) != null).Count();

how to get child members value from dynamically casted class?

Well I'm tring to get class members values from a dynamically casted class but I'm unable to find its child class members values.
Right now I'm getting TotalWeight members property, but I also want to get child member property of AnotherClass like AnotherClass.child. How can I get those members?
string ClassName="something";
Type types = Type.GetType(ClassName, false);
var d = from source in types.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);
if (myobj.GetType().GetProperty(memberInfo.Name) != null)
{
value = myobj.GetType()
.GetProperty(memberInfo.Name)
.GetValue(myobj, null);
//how to get child members value here?
}
}
//Where class something has member
public class something
{
private decimal _totalWeight;
private Anotherclass _another;
public decimal TotalWeight
{
get
{
return this._totalWeight;
}
set
{
this._totalWeight = value;
}
}
public Anotherclass Another
{
get
{
return this._another;
}
set
{
this._another= value;
}
}
}
You can get type of your properties and after that work with this type as in start of your code.
foreach( var memberInfo in members )
{
propertyInfo = types.GetProperty( memberInfo.Name );
if( propertyInfo != null )
{
Console.WriteLine( propertyInfo.PropertyType );
// type of every property
}
}

Categories

Resources