XML Serializing dapper results - c#

I store SQL results into a dynamic List, of which has an underlying DapperRow type. I am trying to serialize/unserialize this List of which XMLserializer complains:
There was an error generating the XML document. ---> System.InvalidOperationException: To be XML serializable, types which inherit from IEnumerable must have an implementation of Add(System.Object) at all levels of their inheritance hierarchy. Dapper.SqlMapper+DapperRow does not implement Add(System.Object).
Is there a way around this (besides the obvious casting the results to my own concrete object), or is it possible to somehow make DapperRow objects conform to System.Xml.XMLserializer constraints?
It states my result array is System.Collections.Generic.List<dynamic> {System.Collections.Generic.List<object>}
Opening the array it says each object is of type object {Dapper.SqlMapper.DapperRow}
I think because DapperRows are now basically IDictionary<string, object> that XML is having issues (I cannot use anything but System.Xml.XmlSerializer so don't suggest an alternative).
I just want to be able to turn a List<dynamic> that I get from Dapper and serialize and deserialize correctly using System.Xml.XmlSerializer

I was able to get something at least serializable by using a serializable dictionary : http://weblogs.asp.net/pwelter34/444961
var results = conn.Query<dynamic>(sql, param);
var resultSet = new List<SerializableDictionary<string, object>>();
foreach (IDictionary<string, object> row in results)
{
var dict = new SerializableDictionary<string, object>();
foreach (var pair in row)
{
dict.Add(pair.Key, pair.Value);
}
resultSet.Add(dict);
}
Its ugly, so I hope more elegant solutions come up

is it possible to somehow make DapperRow objects conform to System.Xml.XMLserializer constraints?
I don't think that this is possible. The DapperRow class is private and it does not have a parameterless constructor.
However, you might be able to use other means to fix your problem.
I suggest that you put your complex serialization logic behind an extension method. This way your original code will stay clean.
I also suggest the following model for storing the data (although you can choose not to use it and use your own logic behind the extension method):
public class Cell
{
public string Name { get; set; }
public object Value { get; set; }
public Cell(){}
public Cell(KeyValuePair<string, object> kvp)
{
Name = kvp.Key;
Value = kvp.Value;
}
}
public class Row : List<Cell>
{
public Row(){}
public Row(IEnumerable<Cell> cells)
: base(cells){}
}
public class Rows : List<Row>
{
public Rows(){}
public Rows(IEnumerable<Row> rows )
:base(rows){}
}
And then the extension method should look something like this:
public static class Extensions
{
public static void Serialize(this IEnumerable<dynamic> enumerable, Stream stream)
{
var rows =
new Rows(
enumerable
.Cast<IEnumerable<KeyValuePair<string, object>>>()
.Select(row =>
new Row(row.Select(cell => new Cell(cell)))));
XmlSerializer serializer = new XmlSerializer(typeof(Rows));
serializer.Serialize(stream, rows);
}
}
Then you would be able to use this code:
var result = connection.Query("SELECT * From Customers");
var memory_stream = new MemoryStream();
result.Serialize(memory_stream);
See how this code is very small because all the complex logic is moved to the extension method.
The model that I suggested allows also for deserialization, just make sure that you use the correct type (e.g. Rows) like this:
XmlSerializer serializer = new XmlSerializer(typeof(Rows));
Rows rows = (Rows)serializer.Deserialize(stream);
You can also have an extension method that just converts the resultset of Dapper to the Rows type and handle the serialization of Rows your self. Such extension method should look something like this:
public static Rows ToRows(this IEnumerable<dynamic> enumerable)
{
return
new Rows(
enumerable
.Cast<IEnumerable<KeyValuePair<string, object>>>()
.Select(row =>
new Row(row.Select(cell => new Cell(cell)))));
}
And then use it like this:
var rows = connection.Query("SELECT * From Customers").ToRows();
XmlSerializer serializer = new XmlSerializer(typeof(Rows));
serializer.Serialize(stream, rows);

Firstly, decorate the DapperResultSet with a [Serializable] attribute. Also create a constructor and in that, assign Rows with an empty List<object>. Use this modified code: (it also contains a modified implemented method)
[Serializable]
public class DapperResultSet : IEnumerable<object>
{
public List Rows { get; set; }
public void Add(dynamic o)
{
Rows.Add(o);
}
public DapperResultSet()
{
Rows = new List<object>();
}
public IEnumerator<object> GetEnumerator()
{
return Rows.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Next in your event handler (or where you would like to do the serialization):
var results = conn.Query<dynamic>(sql, param);
var r = new DapperResultSet();
foreach (var row in results)
{
r.Add(row);
}
//Here is the serialization part:
XmlSerializer xs = new XmlSerializer(typeof(DapperResultSet));
xs.Serialize(new FileStream("Serialized.xml", FileMode.Create), r); //Change path if necessary
For Deserialization,
XmlSerializer xs = new XmlSerializer(typeof(DapperResultSet));
DapperResultSet d_DRS = xs.Deserialize(new FileStream("Serialized.xml", FileMode.Open)); //Deserialized

Challenging request, because Dapper is not designed to be serializable. But let see what can be done.
The first decision is easy - we need to implement IXmlSerializable. The question is how.
Serialization is not a big deal, since we have the field names and values. So we could use similar approach to the SerializableDictionary<TKey, TValue> you mentioned. However, it heavily relies on typeof(TKey) and typeof(TValue)'. We have no problem with key (it's a string), but the type of the value is object. As I mentioned, it's not a problem to write an object value as XML. The problem is deserialization. At that point, all we have is a string and no any clue what that string is. Which means we need to store some metadata in order to be able to deserialize correctly. Of course there are many ways to do that, but I decided to store field names and types separately at the beginning, and then items with values only.
Putting it all together, here is what I ended up:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Samples
{
public class DapperResultSet : IXmlSerializable
{
static readonly Type TableType;
static readonly Type RowType;
static readonly Func<object, string[]> GetFieldNames;
static readonly Func<object, object[]> GetFieldValues;
static readonly Func<string[], object> CreateTable;
static readonly Func<object, object[], object> CreateRow;
static DapperResultSet()
{
TableType = typeof(Dapper.SqlMapper).GetNestedType("DapperTable", BindingFlags.NonPublic);
RowType = typeof(Dapper.SqlMapper).GetNestedType("DapperRow", BindingFlags.NonPublic);
// string[] GetFieldNames(object row)
{
var row = Expression.Parameter(typeof(object), "row");
var expr = Expression.Lambda<Func<object, string[]>>(
Expression.Field(Expression.Field(Expression.Convert(row, RowType), "table"), "fieldNames"),
row);
GetFieldNames = expr.Compile();
}
// object[] GetFieldValues(object row)
{
var row = Expression.Parameter(typeof(object), "row");
var expr = Expression.Lambda<Func<object, object[]>>(
Expression.Field(Expression.Convert(row, RowType), "values"),
row);
GetFieldValues = expr.Compile();
}
// object CreateTable(string[] fieldNames)
{
var fieldNames = Expression.Parameter(typeof(string[]), "fieldNames");
var expr = Expression.Lambda<Func<string[], object>>(
Expression.New(TableType.GetConstructor(new[] { typeof(string[]) }), fieldNames),
fieldNames);
CreateTable = expr.Compile();
}
// object CreateRow(object table, object[] values)
{
var table = Expression.Parameter(typeof(object), "table");
var values = Expression.Parameter(typeof(object[]), "values");
var expr = Expression.Lambda<Func<object, object[], object>>(
Expression.New(RowType.GetConstructor(new[] { TableType, typeof(object[]) }),
Expression.Convert(table, TableType), values),
table, values);
CreateRow = expr.Compile();
}
}
static readonly dynamic[] emptyItems = new dynamic[0];
public IReadOnlyList<dynamic> Items { get; private set; }
public DapperResultSet()
{
Items = emptyItems;
}
public DapperResultSet(IEnumerable<dynamic> source)
{
if (source == null) throw new ArgumentNullException("source");
Items = source as IReadOnlyList<dynamic> ?? new List<dynamic>(source);
}
XmlSchema IXmlSerializable.GetSchema() { return null; }
void IXmlSerializable.WriteXml(XmlWriter writer)
{
if (Items.Count == 0) return;
// Determine field names and types
var fieldNames = GetFieldNames((object)Items[0]);
var fieldTypes = new TypeCode[fieldNames.Length];
for (int count = 0, i = 0; i < Items.Count; i++)
{
var values = GetFieldValues((object)Items[i]);
for (int c = 0; c < fieldTypes.Length; c++)
{
if (fieldTypes[i] == TypeCode.Empty && values[c] != null)
{
fieldTypes[i] = Type.GetTypeCode(values[c].GetType());
if (++count >= fieldTypes.Length) break;
}
}
}
// Write fields
writer.WriteStartElement("Fields");
writer.WriteAttributeString("Count", XmlConvert.ToString(fieldNames.Length));
for (int i = 0; i < fieldNames.Length; i++)
{
writer.WriteStartElement("Field");
writer.WriteAttributeString("Name", fieldNames[i]);
writer.WriteAttributeString("Type", XmlConvert.ToString((int)fieldTypes[i]));
writer.WriteEndElement();
}
writer.WriteEndElement();
// Write items
writer.WriteStartElement("Items");
writer.WriteAttributeString("Count", XmlConvert.ToString(Items.Count));
foreach (IDictionary<string, object> item in Items)
{
writer.WriteStartElement("Item");
foreach (var entry in item)
{
writer.WriteStartAttribute(entry.Key);
writer.WriteValue(entry.Value);
writer.WriteEndAttribute();
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
reader.MoveToContent();
bool isEmptyElement = reader.IsEmptyElement;
reader.ReadStartElement(); // Container
if (isEmptyElement) return;
// Read fields
int fieldCount = XmlConvert.ToInt32(reader.GetAttribute("Count"));
reader.ReadStartElement("Fields");
var fieldNames = new string[fieldCount];
var fieldTypes = new TypeCode[fieldCount];
var fieldIndexByName = new Dictionary<string, int>(fieldCount);
for (int c = 0; c < fieldCount; c++)
{
fieldNames[c] = reader.GetAttribute("Name");
fieldTypes[c] = (TypeCode)XmlConvert.ToInt32(reader.GetAttribute("Type"));
fieldIndexByName.Add(fieldNames[c], c);
reader.ReadStartElement("Field");
}
reader.ReadEndElement();
// Read items
int itemCount = XmlConvert.ToInt32(reader.GetAttribute("Count"));
reader.ReadStartElement("Items");
var items = new List<dynamic>(itemCount);
var table = CreateTable(fieldNames);
for (int i = 0; i < itemCount; i++)
{
var values = new object[fieldCount];
if (reader.MoveToFirstAttribute())
{
do
{
var fieldName = reader.Name;
var fieldIndex = fieldIndexByName[fieldName];
values[fieldIndex] = Convert.ChangeType(reader.Value, fieldTypes[fieldIndex], CultureInfo.InvariantCulture);
}
while (reader.MoveToNextAttribute());
}
reader.ReadStartElement("Item");
var item = CreateRow(table, values);
items.Add(item);
}
reader.ReadEndElement(); // Items
reader.ReadEndElement(); // Container
Items = items;
}
}
}
Some things would have been easier if we modify the Dapper source code, but I assume you don't want to do that.
And here is a sample usage:
static void Test(IEnumerable<dynamic> source)
{
var stream = new MemoryStream();
var sourceSet = new DapperResultSet(source);
var serializer = new XmlSerializer(typeof(DapperResultSet));
serializer.Serialize(stream, sourceSet);
stream.Position = 0;
var reader = new StreamReader(stream);
var xml = reader.ReadToEnd();
stream.Position = 0;
var deserializer = new XmlSerializer(typeof(DapperResultSet));
var target = ((DapperResultSet)deserializer.Deserialize(stream)).Items;
}

Related

Build Expression tree to Add elements to Collection dynamically c#

I have a class and I need to iterate tru each property reading the attribute name to map to my data Source the value, in the cases where I have a ICollection that property will have multiple attributes to map the correct value.
I'm using Expression trees to set the values efficiently to each property but I'm having issues to set the values to the Collection.
I think this is because I need to create an instance of that Collection but I don't know. I'm a bit lost on that one here's what I got:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class MapToAttribute : Attribute
{
public MapToAttribute(string field)
{
Field = field;
}
public string Field { get; private set; }
}
public class MyDataClass
{
[MapTo("one")]
public int propOne { get; set; }
[MapTo("two")]
public string propTwo { get; set; }
[MapTo("item1")]
[MapTo("item2")]
[MapTo("item3")]
public ICollection<int> collection { get; set; }
}
class Program
{
static void Main(string[] args)
{
var setter = SetValues<MyDataClass>();
}
private static IEnumerable<T> SetValues<T>()
where T : new()
{
var properties = GetClassProperties<T>();
var results = new List<T>();
for (int x = 1; x<=100; x++)
{
var row = new T();
//Simulated Datasource
var dataSource = new Dictionary<string, object>();
dataSource.Add("one", x);
dataSource.Add("two", x.ToString());
dataSource.Add("item1", x);
dataSource.Add("item2", x+x);
dataSource.Add("item3", x*x);
foreach (var property in properties)
{
//this line executes the Action
property.Value(row, dataSource[property.Key]);
}
results.Add(row);
}
return results;
}
private static Dictionary<string, Action<T, object>> GetClassProperties<T>()
{
var setters = new Dictionary<string, Action<T, object>>();
var instance = Expression.Parameter(typeof(T));
var argument = Expression.Parameter(typeof(object));
foreach (var property in typeof(T).GetProperties())
{
var names = property.GetCustomAttributes(typeof(MapToAttribute), true)
.Select(p => ((MapToAttribute)p).Field);
var setter = Expression.Lambda<Action<T, object>>(
Expression.Call(
instance,
property.GetSetMethod(),
Expression.Convert(argument, property.PropertyType)
), instance, argument
).Compile();
// Due to the types I cannot just assign a value to a ICollection,
// that's why I tried to create HERE a different setter
// when the property Type is ICollection, I commented out the code failing.
//var getCollection = Expression.Lambda<Func<T, object>>(
// Expression.Call(
// instance,
// prop.GetGetMethod()
// ), instance
// ).Compile();
//Action<T, object> setter = (classInstance, value) =>
// getCollection(classInstance).Add(value);
foreach (var name in names)
{
setters.Add(name, setter);
}
}
return setters;
}
}
First of all to make life easier you will need to initialize collection:
public ICollection<int> collection { get; set; } = new List<int>();
Second try this:
private static Dictionary<string, Action<T, object>> GetClassProperties<T>()
{
var setters = new Dictionary<string, Action<T, object>>();
var instance = Expression.Parameter(typeof(T));
var argument = Expression.Parameter(typeof(object));
foreach (var property in typeof(T).GetProperties())
{
var names = property.GetCustomAttributes(typeof(MapToAttribute), true)
.Select(p => ((MapToAttribute)p).Field)
.ToList();
if (property.PropertyType.IsGenericType) // start checking that prop implements ICollection
{
// get ICollection generic parameter type
var genericParam = property.PropertyType.GetGenericArguments().First();
// construct concrete ICollection type
var propColType = typeof(ICollection<>).MakeGenericType(genericParam);
if (propColType.IsAssignableFrom(property.PropertyType)) // check if is ICollection of genericParam
{
var getCollection = Expression.Call(instance, property.GetGetMethod());
var addMethod = propColType.GetMethod("Add");
var colAddSetter = Expression.Lambda<Action<T, object>>(
Expression.Call(getCollection, addMethod, Expression.Convert(argument, genericParam)),
instance, argument)
.Compile();
foreach (var name in names)
{
setters.Add(name, colAddSetter);
}
continue; // process next property
}
}
// process "ordinary" property
var setter = Expression.Lambda<Action<T, object>>(
Expression.Call(
instance,
property.GetSetMethod(),
Expression.Convert(argument, property.PropertyType)
), instance, argument
).Compile();
setters.Add(names.Single(), setter); // todo throw normal exception instead of Single
}
return setters;
}

Linq Select dynamically from generic <T> List

I want to get a list of each object from my List<T> (except strings, ints etc). And then Invoke (generic, recursive method with reflection). The problem is I am iterating on the property names, and have no idea how to select.
Error CS0021 Cannot apply indexing with [] to an expression of type 'T'
Code:
public static void My method<T>(IEnumerable<T> query)
{
var t = typeof(T);
var Headings = t.GetProperties();
for (int i = iteratorStart; i < Headings.Count(); i++)
{
if (IsValue(Headings[i].PropertyType.FullName))
{
}
else
{
Type type = Type.GetType(Headings[i].PropertyType.FullName);
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var genMet = met.MakeGenericMethod(type);
var nested = query.Select(p => p[Headings[i].Name]);
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
As far as I can see, this is what you want:
public static void Mymethod<T>(IEnumerable<T> query)
{
var t = typeof(T);
int pck = 1234;
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var Headings = t.GetProperties();
for(int i=0; i < Headings.Length; ++i)
{
var prop = Headings[i];
if (prop.PropertyType.IsClass)
{
var genMet = met.MakeGenericMethod(prop.PropertyType);
var nested = query.Select(p => prop.GetValue(p));
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
class ExcelExtension
{
public void ListToExcel<T>(int pck, IEnumerable<object> nested, int i)
{
}
}
Assuming you are using c# 6.0 or higher. You can use generic type parameters like;
public static void MyMethod<T>(IEnumerable<T> query) where T : IList
{
//Your code here
}
This way, you ensure that T is List of something and reaching indexing won't be a problem.
UPDATE
I misunderstood the question earlier. Here is the updated solution.
public static void MyMethod<T>(IEnumerable<T> query)
{
var t = typeof(T);
var Headings = t.GetProperties();
for (int i = iteratorStart; i < Headings.Count(); i++)
{
if (false == IsValue(Headings[i].PropertyType.FullName))
{
Type type = Type.GetType(Headings[i].PropertyType.FullName);
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var genMet = met.MakeGenericMethod(type);
//Assuming you want to get property value here. IF not You can use like Headings[i].GetName
var nested = query.Select(p =>Convert.ChangeType( Headings[i].GetValue(p),Headings[i].GetType()));
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
Error Explanation:
The problem is in the Select(p => p[something here]) part. Since p is not the property list or array but a type of object, it doesn't contain any indexer. You should use reflection like above example.

NHibernate How to add extensions manually?

I would like to use this method to get data dynamically from my database. As explained in that topic, I should add an extension to my nhibernate config.
Could someone say, please, how to add that extension ?
Thanks in advance.
public static class NhTransformers
{
public static readonly IResultTransformer ExpandoObject;
static NhTransformers()
{
ExpandoObject = new ExpandoObjectResultSetTransformer();
}
private class ExpandoObjectResultSetTransformer : IResultTransformer
{
public IList TransformList(IList collection)
{
return collection;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
var expando = new ExpandoObject();
var dictionary = (IDictionary<string, object>)expando;
for (int i = 0; i < tuple.Length; i++)
{
string alias = aliases[i];
if (alias != null)
{
dictionary[alias] = tuple[i];
}
}
return expando;
}
}
}
public static class NHibernateExtensions
{
public static IList<dynamic> DynamicList(this IQuery query)
{
return query.SetResultTransformer(NhTransformers.ExpandoObject)
.List<dynamic>();
}
}
-----------------------------------
USE CASE
-----------------------------------
var results = this.session.CreateSQLQuery("select Id, Title, Body from [Posts]")
.DynamicList(); // Secret sauce!
// results are now dynamic!
Console.WriteLine(results[0].Id);
Console.WriteLine(results[0].Name);
// rock on!
Finally, I only created NhTransformers class and did like this;
var query1 = " select * from mySQLView";
var result1 = this.session.CreateSQLQuery(query1).SetResultTransformer(NhTransformers.ExpandoObject)
.List<dynamic>();
foreach (var obj in result1)
{
// some stuff...
}

How do I clone a generic list in C#?

I have a generic list of objects in C#, and wish to clone the list. The items within the list are cloneable, but there doesn't seem to be an option to do list.Clone().
Is there an easy way around this?
If your elements are value types, then you can just do:
List<YourType> newList = new List<YourType>(oldList);
However, if they are reference types and you want a deep copy (assuming your elements properly implement ICloneable), you could do something like this:
List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);
oldList.ForEach((item) =>
{
newList.Add((ICloneable)item.Clone());
});
Obviously, replace ICloneable in the above generics and cast with whatever your element type is that implements ICloneable.
If your element type doesn't support ICloneable but does have a copy-constructor, you could do this instead:
List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);
oldList.ForEach((item)=>
{
newList.Add(new YourType(item));
});
Personally, I would avoid ICloneable because of the need to guarantee a deep copy of all members. Instead, I'd suggest the copy-constructor or a factory method like YourType.CopyFrom(YourType itemToCopy) that returns a new instance of YourType.
Any of these options could be wrapped by a method (extension or otherwise).
You can use an extension method.
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
For a shallow copy, you can instead use the GetRange method of the generic List class.
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
Quoted from: Generics Recipes
public static object DeepClone(object obj)
{
object objResult = null;
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
This is one way to do it with C# and .NET 2.0. Your object requires to be [Serializable()]. The goal is to lose all references and build new ones.
To clone a list just call .ToList(). This creates a shallow copy.
Microsoft (R) Roslyn C# Compiler version 2.3.2.62116
Loading context from 'CSharpInteractive.rsp'.
Type "#help" for more information.
> var x = new List<int>() { 3, 4 };
> var y = x.ToList();
> x.Add(5)
> x
List<int>(3) { 3, 4, 5 }
> y
List<int>(2) { 3, 4 }
>
After a slight modification you can also clone:
public static T DeepClone<T>(T obj)
{
T objResult;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = (T)bf.Deserialize(ms);
}
return objResult;
}
Unless you need an actual clone of every single object inside your List<T>, the best way to clone a list is to create a new list with the old list as the collection parameter.
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
Changes to myList such as insert or remove will not affect cloneOfMyList and vice versa.
The actual objects the two Lists contain are still the same however.
Use AutoMapper (or whatever mapping lib you prefer) to clone is simple and a lot maintainable.
Define your mapping:
Mapper.CreateMap<YourType, YourType>();
Do the magic:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
If you only care about value types...
And you know the type:
List<int> newList = new List<int>(oldList);
If you don't know the type before, you'll need a helper function:
List<T> Clone<T>(IEnumerable<T> oldList)
{
return newList = new List<T>(oldList);
}
The just:
List<string> myNewList = Clone(myOldList);
If you have already referenced Newtonsoft.Json in your project and your objects are serializeable you could always use:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
Possibly not the most efficient way to do it, but unless you're doing it 100s of 1000s of times you may not even notice the speed difference.
For a deep copy, ICloneable is the correct solution, but here's a similar approach to ICloneable using the constructor instead of the ICloneable interface.
public class Student
{
public Student(Student student)
{
FirstName = student.FirstName;
LastName = student.LastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
// wherever you have the list
List<Student> students;
// and then where you want to make a copy
List<Student> copy = students.Select(s => new Student(s)).ToList();
you'll need the following library where you make the copy
using System.Linq
you could also use a for loop instead of System.Linq, but Linq makes it concise and clean. Likewise you could do as other answers have suggested and make extension methods, etc., but none of that is necessary.
There is no need to flag classes as Serializable and in our tests using the Newtonsoft JsonSerializer even faster than using BinaryFormatter. With extension methods usable on every object.
attention: private members are not cloned
Standard .NET JavascriptSerializer option:
public static T DeepCopy<T>(this T value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(value);
return js.Deserialize<T>(json);
}
Faster option using Newtonsoft JSON:
public static T DeepCopy<T>(this T value)
{
string json = JsonConvert.SerializeObject(value);
return JsonConvert.DeserializeObject<T>(json);
}
I'll be lucky if anybody ever reads this... but in order to not return a list of type object in my Clone methods, I created an interface:
public interface IMyCloneable<T>
{
T Clone();
}
Then I specified the extension:
public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
And here is an implementation of the interface in my A/V marking software. I wanted to have my Clone() method return a list of VidMark (while the ICloneable interface wanted my method to return a list of object):
public class VidMark : IMyCloneable<VidMark>
{
public long Beg { get; set; }
public long End { get; set; }
public string Desc { get; set; }
public int Rank { get; set; } = 0;
public VidMark Clone()
{
return (VidMark)this.MemberwiseClone();
}
}
And finally, the usage of the extension inside a class:
private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;
//Other methods instantiate and fill the lists
private void SetUndoVidMarks()
{
_UndoVidMarks = _VidMarks.Clone();
}
Anybody like it? Any improvements?
public static Object CloneType(Object objtype)
{
Object lstfinal = new Object();
using (MemoryStream memStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
lstfinal = binaryFormatter.Deserialize(memStream);
}
return lstfinal;
}
public class CloneableList<T> : List<T>, ICloneable where T : ICloneable
{
public object Clone()
{
var clone = new List<T>();
ForEach(item => clone.Add((T)item.Clone()));
return clone;
}
}
public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new()
{
List<TEntity> retList = new List<TEntity>();
try
{
Type sourceType = typeof(TEntity);
foreach(var o1 in o1List)
{
TEntity o2 = new TEntity();
foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
{
var val = propInfo.GetValue(o1, null);
propInfo.SetValue(o2, val);
}
retList.Add(o2);
}
return retList;
}
catch
{
return retList;
}
}
If you need a cloned list with the same capacity, you can try this:
public static List<T> Clone<T>(this List<T> oldList)
{
var newList = new List<T>(oldList.Capacity);
newList.AddRange(oldList);
return newList;
}
//try this
List<string> ListCopy= new List<string>(OldList);
//or try
List<T> ListCopy=OldList.ToList();
If I need deep copy of collection, I have favorite approach like this:
public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collectionToDeepCopy)
{
var serializedCollection = JsonConvert.SerializeObject(collectionToDeepCopy);
return JsonConvert.DeserializeObject<IEnumerable<T>>(serializedCollection);
}
You can use extension method:
namespace extension
{
public class ext
{
public static List<double> clone(this List<double> t)
{
List<double> kop = new List<double>();
int x;
for (x = 0; x < t.Count; x++)
{
kop.Add(t[x]);
}
return kop;
}
};
}
You can clone all objects by using their value type members for example, consider this class:
public class matrix
{
public List<List<double>> mat;
public int rows,cols;
public matrix clone()
{
// create new object
matrix copy = new matrix();
// firstly I can directly copy rows and cols because they are value types
copy.rows = this.rows;
copy.cols = this.cols;
// but now I can no t directly copy mat because it is not value type so
int x;
// I assume I have clone method for List<double>
for(x=0;x<this.mat.count;x++)
{
copy.mat.Add(this.mat[x].clone());
}
// then mat is cloned
return copy; // and copy of original is returned
}
};
Note: if you do any change on copy (or clone) it will not affect the original object.
Using a cast may be helpful, in this case, for a shallow copy:
IList CloneList(IList list)
{
IList result;
result = (IList)Activator.CreateInstance(list.GetType());
foreach (object item in list) result.Add(item);
return result;
}
applied to generic list:
List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);
I use automapper to copy an object. I just setup a mapping that maps one object to itself. You can wrap this operation any way you like.
http://automapper.codeplex.com/
I've made for my own some extension which converts ICollection of items that not implement IClonable
static class CollectionExtensions
{
public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
{
var array = new T[listToClone.Count];
listToClone.CopyTo(array,0);
return array.ToList();
}
}
You could also simply convert the list to an array using ToArray, and then clone the array using Array.Clone(...).
Depending on your needs, the methods included in the Array class could meet your needs.
The following code should transfer onto a list with minimal changes.
Basically it works by inserting a new random number from a greater range with each successive loop. If there exist numbers already that are the same or higher than it, shift those random numbers up one so they transfer into the new larger range of random indexes.
// Example Usage
int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);
for(int i = 0; i < toSet.Length; i++)
toSet[i] = selectFrom[indexes[i]];
private int[] getRandomUniqueIndexArray(int length, int count)
{
if(count > length || count < 1 || length < 1)
return new int[0];
int[] toReturn = new int[count];
if(count == length)
{
for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
return toReturn;
}
Random r = new Random();
int startPos = count - 1;
for(int i = startPos; i >= 0; i--)
{
int index = r.Next(length - i);
for(int j = startPos; j > i; j--)
if(toReturn[j] >= index)
toReturn[j]++;
toReturn[i] = index;
}
return toReturn;
}
Another thing: you could use reflection. If you'll cache this properly, then it'll clone 1,000,000 objects in 5.6 seconds (sadly, 16.4 seconds with inner objects).
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Person
{
...
Job JobDescription
...
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Job
{...
}
private static readonly Type stringType = typeof (string);
public static class CopyFactory
{
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
private static readonly MethodInfo CreateCopyReflectionMethod;
static CopyFactory()
{
CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
}
public static T CreateCopyReflection<T>(T source) where T : new()
{
var copyInstance = new T();
var sourceType = typeof(T);
PropertyInfo[] propList;
if (ProperyList.ContainsKey(sourceType))
propList = ProperyList[sourceType];
else
{
propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
ProperyList.Add(sourceType, propList);
}
foreach (var prop in propList)
{
var value = prop.GetValue(source, null);
prop.SetValue(copyInstance,
value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
}
return copyInstance;
}
I measured it in a simple way, by using the Watcher class.
var person = new Person
{
...
};
for (var i = 0; i < 1000000; i++)
{
personList.Add(person);
}
var watcher = new Stopwatch();
watcher.Start();
var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
watcher.Stop();
var elapsed = watcher.Elapsed;
RESULT: With inner object PersonInstance - 16.4, PersonInstance = null - 5.6
CopyFactory is just my test class where I have dozen of tests including usage of expression. You could implement this in another form in an extension or whatever. Don't forget about caching.
I didn't test serializing yet, but I doubt in an improvement with a million classes. I'll try something fast protobuf/newton.
P.S.: for the sake of reading simplicity, I only used auto-property here. I could update with FieldInfo, or you should easily implement this by your own.
I recently tested the Protocol Buffers serializer with the DeepClone function out of the box. It wins with 4.2 seconds on a million simple objects, but when it comes to inner objects, it wins with the result 7.4 seconds.
Serializer.DeepClone(personList);
SUMMARY: If you don't have access to the classes, then this will help. Otherwise it depends on the count of the objects. I think you could use reflection up to 10,000 objects (maybe a bit less), but for more than this the Protocol Buffers serializer will perform better.
There is a simple way to clone objects in C# using a JSON serializer and deserializer.
You can create an extension class:
using Newtonsoft.Json;
static class typeExtensions
{
[Extension()]
public static T jsonCloneObject<T>(T source)
{
string json = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(json);
}
}
To clone and object:
obj clonedObj = originalObj.jsonCloneObject;
For a deep clone I use reflection as follows:
public List<T> CloneList<T>(IEnumerable<T> listToClone) {
Type listType = listToClone.GetType();
Type elementType = listType.GetGenericArguments()[0];
List<T> listCopy = new List<T>();
foreach (T item in listToClone) {
object itemCopy = Activator.CreateInstance(elementType);
foreach (PropertyInfo property in elementType.GetProperties()) {
elementType.GetProperty(property.Name).SetValue(itemCopy, property.GetValue(item));
}
listCopy.Add((T)itemCopy);
}
return listCopy;
}
You can use List or IEnumerable interchangeably.
You can use the List<T>.ConvertAll(Converter<T, T>) method to create a new list containing all the elements of the original list, and use a conversion function that returns the input value.
List<int> originalList = new List<int> { 1, 2, 3, 4, 5 };
List<int> clonedList = new List<int>(originalList.ConvertAll(x => x));

XML Serialization and Schema without xsd.exe

I use XML serialization for the reading of my Config-POCOs.
To get intellisense support in Visual Studio for XML files I need a schema file. I can create the schema with xsd.exe mylibrary.dll and this works fine.
But I want that the schema is always created if I serialize an object to the file system. Is there any way without using xsd.exe?
thank you, this was the right way for me.
solution:
XmlReflectionImporter importer = new XmlReflectionImporter();
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
Type type = toSerialize.GetType();
XmlTypeMapping map = importer.ImportTypeMapping(type);
exporter.ExportTypeMapping(map);
TextWriter tw = new StreamWriter(fileName + ".xsd");
schemas[0].Write(tw);
tw.Close();
The solution posted above by Will worked wonderfully, except I realized that the schema generated did not reflect the attributes on the different class members. For example a class decorated with serialization hint attributes (see the sample below), would have not rendered correctly.
public class Test
{
[XmlAttribute()]
public string Attribute { get; set; }
public string Description { get; set; }
[XmlArray(ElementName = "Customers")]
[XmlArrayItem(ElementName = "Customer")]
public List<CustomerClass> blah { get; set; }
}
To address this, I created a few helper functions that use reflection to traverse the class hierarchy, read the attributes, and populate a XmlAttributeOverrides object that can be passed into the XmlReflectionImporter.
public static void AttachXmlAttributes(XmlAttributeOverrides xao, Type t)
{
List<Type> types = new List<Type>();
AttachXmlAttributes(xao, types, t);
}
public static void AttachXmlAttributes(XmlAttributeOverrides xao, List<Type> all, Type t)
{
if(all.Contains(t))
return;
else
all.Add(t);
XmlAttributes list1 = GetAttributeList(t.GetCustomAttributes(false));
xao.Add(t, list1);
foreach (var prop in t.GetProperties())
{
XmlAttributes list2 = GetAttributeList(prop.GetCustomAttributes(false));
xao.Add(t, prop.Name, list2);
AttachXmlAttributes(xao, all, prop.PropertyType);
}
}
private static XmlAttributes GetAttributeList(object[] attributes)
{
XmlAttributes list = new XmlAttributes();
foreach (var attribute in attributes)
{
Type type = attribute.GetType();
if (type.Name == "XmlAttributeAttribute") list.XmlAttribute = (XmlAttributeAttribute)attribute;
else if (type.Name == "XmlArrayAttribute") list.XmlArray = (XmlArrayAttribute)attribute;
else if (type.Name == "XmlArrayItemAttribute") list.XmlArrayItems.Add((XmlArrayItemAttribute)attribute);
}
return list;
}
public static string GetSchema<T>()
{
XmlAttributeOverrides xao = new XmlAttributeOverrides();
AttachXmlAttributes(xao, typeof(T));
XmlReflectionImporter importer = new XmlReflectionImporter(xao);
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
XmlTypeMapping map = importer.ImportTypeMapping(typeof(T));
exporter.ExportTypeMapping(map);
using (MemoryStream ms = new MemoryStream())
{
schemas[0].Write(ms);
ms.Position = 0;
return new StreamReader(ms).ReadToEnd();
}
}
Hope this helps someone else.
Look at the System.Xml.Serialization.XmlSchemaExporter class. I can't recall the exact details, but there is enough functionality in that namespace to do what you require.
Improvement to Matt Murrell version: to apply XmlAttributes recursively for nested property user type (for example CustomerClass property).
private static void AttachXmlAttributes(XmlAttributeOverrides xao, List<Type> all, Type t)
{
if (all.Contains(t))
{
return;
}
else
{
all.Add(t);
}
var list1 = GetAttributeList(t.GetCustomAttributes(false));
xao.Add(t, list1);
foreach (var prop in t.GetProperties())
{
var propType = prop.PropertyType;
if (propType.IsGenericType) // is list?
{
var args = propType.GetGenericArguments();
if (args != null && args.Length == 1)
{
var genType = args[0];
if (genType.Name.ToLower() != "object")
{
var list2 = GetAttributeList(prop.GetCustomAttributes(false));
xao.Add(t, prop.Name, list2);
AttachXmlAttributes(xao, all, genType);
}
}
}
else
{
var list2 = GetAttributeList(prop.GetCustomAttributes(false));
xao.Add(t, prop.Name, list2);
AttachXmlAttributes(xao, all, prop.PropertyType);
}
}
}
private static XmlAttributes GetAttributeList(object[] attributes)
{
var list = new XmlAttributes();
foreach (var attr in attributes)
{
Type type = attr.GetType();
switch (type.Name)
{
case "XmlAttributeAttribute":
list.XmlAttribute = (XmlAttributeAttribute)attr;
break;
case "XmlRootAttribute":
list.XmlRoot = (XmlRootAttribute)attr;
break;
case "XmlElementAttribute":
list.XmlElements.Add((XmlElementAttribute)attr);
break;
case "XmlArrayAttribute":
list.XmlArray = (XmlArrayAttribute)attr;
break;
case "XmlArrayItemAttribute":
list.XmlArrayItems.Add((XmlArrayItemAttribute)attr);
break;
}
}
return list;
}

Categories