copy class / object recursive with sub classes - c#

I have a class. I have no influence on thios class, it is coming from somewhere else, from a 3rd party.
I have my own class. When I update it is the same. But it might have missing objects later.
I need to copy Class lets calls it here "source" to my class "target".
source has structs, lists with ints and strings.
First I tried to get down trough it without Reference, it looked like it worked, but the target was empty after that because of the missing reference.
(if anybody wants to see that code let me know).
Now I made a 2nd attempt now: I am not sure if it is the right way, I am having problems copying the values to the right place in the target,
stepping through the class works. Please help me out I need an urgent solution. And please with copying Class existing source to existing target (if the items exisits there in the same struct),
please no suggestions to it totally different because I have no influence on Class source, and Class Target itself, I just need to copy values and subclasses.
Here my code so far. Working through class and subclasses works, have problems setting the values (to the right place):
void refcopyObject(ref object source,ref object target,object svalue,object tvalue)
{
if (source != null && target != null)
{
if (source.GetType() == (typeof(string)))
{
target = source;
}
else
if (source.GetType() == (typeof(int)))
{
target = source;
}
else
if (source.GetType() == (typeof(IntPtr)))
{
target = source;
}
else
{
FieldInfo[] fifsource = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
FieldInfo[] fiftarget = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
if (fifsource.Length > 0)
{
for (int i = 0; i < fifsource.Length; i++)
{
if (fifsource.GetType() == fiftarget.GetType())
{
if (i < fiftarget.Length)
{
object psource = source.GetType().GetFields();
object ptarget = target.GetType().GetFields();
object vsource = source.GetType().GetFields().GetValue(source);
object vtarget = target.GetType().GetFields().GetValue(target);
refcopyObject(ref psource, ref ptarget, vsource, vtarget);
}
}
}
}
else
{
//Unten angekommen
copySubObject(ref source, ref target, svalue, tvalue);
////So gehts nicht, dann wird die Referenz wieder verloren
//FieldInfo[] fifs = svalue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
//if (fifs.Length > 0)
//{
// FieldInfo[] fift = tvalue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
// for (int i = 0; i < fifs.Length; i++)
// {
// if (fifs.GetType() == fift.GetType())
// {
// if (i < fift.Length)
// {
// object psource = svalue.GetType().GetFields().GetValue(svalue);
// object ptarget = tvalue.GetType().GetFields().GetValue(tvalue);
// if (ptarget == null)
// {
// //Ganz unten angekommen, Problem bei Listen
// if (psource.GetType() == (typeof(string)))
// {
// tvalue.GetType().GetFields().SetValue(tvalue,psource);
// }
// if (psource.GetType() == (typeof(int)))
// {
// tvalue.GetType().GetFields().SetValue(tvalue, psource);
// }
// }
// else
// {
// refcopyObject(ref psource, ref ptarget, null, null);
// }
// }
// }
// }
//}
}
}
}
}
I guess the problems start where the comments start. I got to a struct or list at that part, which contain strings or int...
Thanks a lot!
Quick Reply

Well, the simplest way to get a deep copy of a serializable object is to serialize it to a MemoryStream, and then deserialize it back to a new object:
public static T DeepCopy<T>(T other)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, other);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
Note that this requires your source type to be marked with the [Serializable] attribute, and since you don't have access to that code, it doesn't depend on you:
[Serializable]
public class MyClass
{
...
}
Non-serializable classes
There are some solutions which use Reflection to get a deep copy (like CodeProject: Deep copy of objects in C#), although it's important to test if they handle circular references properly. If your object doesn't reference itself (directly or indirectly), you may try this approach.

Related

Code works in Watch Window, but unable to cast object during code execution

I have these lines of code in my .NET 4.7.2 server application:
object saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly); // this works
((AxdSalesOrder)saveObject).SalesTable[0].TableDlvAddr = null; // this throws error
I can execute the null-set (2nd line above) in the VS2019 Watch Window and it works perfectly and achieves the desired effect. But when I allow it to run in normal execution (whether in debug mode or not), I get an unhandled exception on that 2nd line:
Unable to cast object of type 'AxdSalesOrder' to type
'Elogix.MSAx.Core.Ax2012ElogixServices.AxdSalesOrder'
There is dynamic stuff going in in relation to that type:
public override object GetEntityAsNativeObject(MSAxEntity entity, Assembly dynamicProxyAssembly) {
var salesOrderObject = Activator.CreateInstance(dynamicProxyAssembly.GetType("AxdSalesOrder"));
var salesOrderTable = DynamicEntityUtil.CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly, "AxdEntity_SalesTable");
Array tableLines =
Array.CreateInstance(
salesOrderObject.GetType().GetProperty("SalesTable").PropertyType.GetElementType(), 1);
tableLines.SetValue(salesOrderTable, 0);
salesOrderObject.SetPropertyValue("SalesTable", tableLines);
return salesOrderObject;
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Assembly dynamicProxyAssembly, string objectTypeName) {
return CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly.GetType(objectTypeName));
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Type type) {
if (type == null) {
throw new ArgumentException("Cannot create object from dynamic entity because \"Type\" is null.");
}
if (type.IsArray) {
return CreateArrayFromDynamicEntity(entity, type);
}
return CreateClassFromDynamicEntity(entity, type);
}
private static object CreateClassFromDynamicEntity(DynamicEntity entity, Type type) {
var nativeObject = Activator.CreateInstance(type);
// this will recursively convert the dynamic values to the native type values on the object.
updateValuesFromDynamicValues(entity);
var modifiedProperties = from property in entity.Properties
//where property.State != DynamicPropertyState.Unchanged
select property;
foreach(var property in modifiedProperties) {
Type valueUnderlyingType = Nullable.GetUnderlyingType(property.Type);
if (valueUnderlyingType != null && valueUnderlyingType.IsEnum) {
PropertyInfo info = nativeObject.GetType().GetProperty(property.Name);
Type targetUnderlyingType = Nullable.GetUnderlyingType(info.PropertyType);
if (property.Value == null) {
info.SetValue(nativeObject, null, null);
} else {
object correctedValue = property.Value.CorrectedEnumValue(targetUnderlyingType);
info.SetValue(nativeObject, correctedValue, null);
}
} else if (property.Type.IsEnum) {
if (property.Value == null) {
continue;
}
object correctedValue = property.Value.CorrectedEnumValue(property.Type);
nativeObject.SetPropertyValue(property.Name, correctedValue);
} else {
try {
nativeObject.SetPropertyValue(property.Name, property.Value);
} catch (Exception ex) {
Log.Write(ex.Message);
}
}
}
return nativeObject;
}
Here is how it looks in the VS2019 Watch Window:
Did this in Immediate Window:
var t = saveObject.GetType();
t.FullName
"AxdSalesOrder"
As you can see, the type's FullName is not very full, not qualified by anything, due to the dynamic nature of the type.
I can try it this way:
(saveObject as AxdSalesOrder).SalesTable[0].TableDlvAddr = null;
Again, that works in Watch, but throws this exception when run in normal execution:
Object reference not set to an instance of an object.
Clearly, VS/Watch knows the type, which is why it can execute without errors inside Watch. But the .net runtime apparently doesn't know the type at run time. How can I get this object cast to work in normal code execution like it does when run in Watch?
The answer here is to take advantage of dynamic typing since the static type is not available here:
dynamic saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly);
saveObject.SalesTable[0].TableDlvAddr = null;
Another possible approach would be to use reflection, however since the expression applied to the object involves two properties and an index lookup (.SalesTable[0].TableDlvAddr) this would look much less readable.
The GetEntityAsNativeObject could also benefit from this style, you could consider rewriting it to using dynamic binding rather than reflection.

XLWorkbook : hexadecimal value 0x1D is invalid character ' on memory stream saving [duplicate]

I have a C# app that exports to Excel using ClosedXML. It works fine but just ran into an issue where when i hit the :
var ms = new MemoryStream();
workbook.SaveAs(ms);
I get an exception:
' ', hexadecimal value 0x0B, is an invalid character
Its definitely data related because it I look at certain data it works fine but other data it causes this issue.
how can i figure out which character is causing the issue? Also, once I figure that out, what is the best way of finding where this character is within my data?
Since you have invalid characters in the data / strings you put into the ClosedXML sheet, you have to find them and get them out.
The simplest solution is to add
.Replace((0x0B).ToString(), " ")
to all your strings to get rid of the vertical tabs and replace them with spaces.
Since ClosedXML is an open source project, the simplest way of tracking the error down would be building it from the source *, and then running your code against the library in debug mode.
Once you see the full stack trace, you should be able to identify the spot from which the error is coming. Good chances are that it is a bug in the way the ClosedXML project uses Microsoft XML libraries, because the error that you mentioned is reported by a library outside the ClosedXML project.
* I downloaded the project, and tried building it. Everything in the closedxml-79843.zip package builds correctly.
Since ClosedXML doesn't prevent you from using the 0x0B character in values, you'll either have to scrub your data of it yourself (as suggested by #Raidri), or you could force and exception, or do a string replace when the value is set. I've created a sample program below which uses Castle's Dynamic Proxy to wrap the IXLWorksheet and IXLCell interfaces. Firstly, we proxy the IXLWorksheet values (which returned from adding a new worksheet as in the example below, or by indexing an existing worksheet). This needs to be done manually via a method call; everything else from then on is set up. When accessing cells (via the Cell methods, or the ActiveCell property) a proxied IXLCell value is returned which checks the data being set via the Value property and the SetValue method. The check is done in the ValidateMethodInterceptor as per the comments. This whole mechanism can be left in your codebase and turned on/off via a switch in the Program.Proxy method if you so desire.
As a further alternative, the package EPPlus (which has similar functionality to ClosedXML) doesn't crash when confronted with the VT character. Instead it replaces it with the value _x00B_. Perhaps a switch would be more beneficial?
internal class Program
{
private static void Main(string[] args)
{
var stream = new MemoryStream();
using (stream)
{
using (var workbook = new XLWorkbook())
{
using (var worksheet = Proxy(workbook.Worksheets.Add("Sheet 1")))
{
worksheet.Cell("A1").Value = "This is a test";
worksheet.Cell("A2").Value = "This \v is a test";
workbook.SaveAs(stream);
}
}
}
}
public static IXLWorksheet Proxy(IXLWorksheet target)
{
var generator = new ProxyGenerator();
var options = new ProxyGenerationOptions { Selector = new WorksheetInterceptorSelector() };
return generator.CreateInterfaceProxyWithTarget<IXLWorksheet>(target, options);
}
}
public class WorksheetInterceptorSelector : IInterceptorSelector
{
private static readonly MethodInfo[] methodsToAdjust;
private readonly ProxyCellInterceptor proxyCellInterceptor = new ProxyCellInterceptor();
static WorksheetInterceptorSelector()
{
methodsToAdjust = typeof(IXLWorksheet).GetMethods()
.Where(x => x.Name == "Cell")
.Union(new[] { typeof(IXLWorksheet).GetProperty("ActiveCell").GetGetMethod() })
.ToArray();
}
#region IInterceptorSelector Members
public IInterceptor[] SelectInterceptors(System.Type type, System.Reflection.MethodInfo method, IInterceptor[] interceptors)
{
if (!methodsToAdjust.Contains(method))
return interceptors;
return new IInterceptor[] { proxyCellInterceptor }.Union(interceptors).ToArray();
}
#endregion
}
public class CellInterceptorSelector : IInterceptorSelector
{
private static readonly MethodInfo[] methodsToAdjust = new[] { typeof(IXLCell).GetMethod("SetValue"), typeof(IXLCell).GetProperty("Value").GetSetMethod() };
private ValidateMethodInterceptor proxyCellInterceptor = new ValidateMethodInterceptor();
#region IInterceptorSelector Members
public IInterceptor[] SelectInterceptors(System.Type type, MethodInfo method, IInterceptor[] interceptors)
{
if (method.IsGenericMethod && method.Name == "SetValue" || methodsToAdjust.Contains(method))
return new IInterceptor[] { proxyCellInterceptor }.Union(interceptors).ToArray();
return interceptors;
}
#endregion
}
public class ProxyCellInterceptor : IInterceptor
{
#region IInterceptor Members
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
//Wrap the return value
invocation.ReturnValue = Proxy((IXLCell)invocation.ReturnValue);
}
#endregion
public IXLCell Proxy(IXLCell target)
{
var generator = new ProxyGenerator();
var options = new ProxyGenerationOptions { Selector = new CellInterceptorSelector() };
return generator.CreateInterfaceProxyWithTarget<IXLCell>(target, options);
}
}
public class ValidateMethodInterceptor : IInterceptor
{
#region IInterceptor Members
public void Intercept(IInvocation invocation)
{
var value = invocation.Arguments[0];
//Validate the data as it is being set
if (value != null && value.ToString().Contains('\v'))
{
throw new ArgumentException("Value cannot contain vertical tabs!");
}
//Alternatively, you could do a string replace:
//if (value != null && value.ToString().Contains('\v'))
//{
// invocation.Arguments[0] = value.ToString().Replace("\v", Environment.NewLine);
//}
invocation.Proceed();
}
#endregion
}

Classification of instances in Weka

I'm trying to use Weka in my C# application. I've used IKVM to bring the Java parts into my .NET application. This seems to be working quite well. However, I am at a loss when it comes to Weka's API. How exactly do I classify instances if they are programmatically passed around in my application and not available as ARFF files.
Basically, I am trying to integrate a simple co-reference analysis using Weka's classifiers. I've built the classification model in Weka directly and saved it to disk, from where my .NET application opens it and uses the IKVM port of Weka to predict the class value.
Here is what I've got so far:
// This is the "entry" method for the classification method
public IEnumerable<AttributedTokenDecorator> Execute(IEnumerable<TokenPair> items)
{
TokenPair[] pairs = items.ToArray();
Classifier model = ReadModel(); // reads the Weka generated model
FastVector fv = CreateFastVector(pairs);
Instances instances = new Instances("licora", fv, pairs.Length);
CreateInstances(instances, pairs);
for(int i = 0; i < instances.numInstances(); i++)
{
Instance instance = instances.instance(i);
double classification = model.classifyInstance(instance); // array index out of bounds?
if(AsBoolean(classification))
MakeCoreferent(pairs[i]);
}
throw new NotImplementedException(); // TODO
}
// This is a helper method to create instances from the internal model files
private static void CreateInstances(Instances instances, IEnumerable<TokenPair> pairs)
{
instances.setClassIndex(instances.numAttributes() - 1);
foreach(var pair in pairs)
{
var instance = new Instance(instances.numAttributes());
instance.setDataset(instances);
for (int i = 0; i < instances.numAttributes(); i++)
{
var attribute = instances.attribute(i);
if (pair.Features.ContainsKey(attribute.name()) && pair.Features[attribute.name()] != null)
{
var value = pair.Features[attribute.name()];
if (attribute.isNumeric()) instance.setValue(attribute, Convert.ToDouble(value));
else instance.setValue(attribute, value.ToString());
}
else
{
instance.setMissing(attribute);
}
}
//instance.setClassMissing();
instances.add(instance);
}
}
// This creates the data set's attributes vector
private FastVector CreateFastVector(TokenPair[] pairs)
{
var fv = new FastVector();
foreach (var attribute in _features)
{
Attribute att;
if (attribute.Type.Equals(ArffType.Nominal))
{
var values = new FastVector();
ExtractValues(values, pairs, attribute.FeatureName);
att = new Attribute(attribute.FeatureName, values);
}
else
att = new Attribute(attribute.FeatureName);
fv.addElement(att);
}
{
var classValues = new FastVector(2);
classValues.addElement("0");
classValues.addElement("1");
var classAttribute = new Attribute("isCoref", classValues);
fv.addElement(classAttribute);
}
return fv;
}
// This extracts observed values for nominal attributes
private static void ExtractValues(FastVector values, IEnumerable<TokenPair> pairs, string featureName)
{
var strings = (from x in pairs
where x.Features.ContainsKey(featureName) && x.Features[featureName] != null
select x.Features[featureName].ToString())
.Distinct().ToArray();
foreach (var s in strings)
values.addElement(s);
}
private Classifier ReadModel()
{
return (Classifier) SerializationHelper.read(_model);
}
private static bool AsBoolean(double classifyInstance)
{
return classifyInstance >= 0.5;
}
For some reason, Weka throws an IndexOutOfRangeException when I call model.classifyInstance(instance). I have no idea why, nor can I come up with an idea how to rectify this issue.
I am hoping someone might know where I went wrong. The only documentation for Weka I found relies on ARFF files for prediction, and I don't really want to go there.
For some odd reason, this exception was raised by the DTNB classifier (I was using three in a majority vote classification model). Apparently, not using DTNB "fixed" the issue.

Getting class variable value using reflection

In my business logic I have created classes for database operations like insert, update etc.
For this purpose I have created a class CDatabase which sets has some methods define in it like openconnection and closeconnection transation etc.
Now my logic class inherit that class
CAnswerLogic : CDatabase
{
OpenConnection();
BeginTrans();
Command.CommandText = "PKG_ANSWER.PROC_ADD_ANSWERS";
}
Can I get the value of Command.CommandText using reflection. Command is a property inside CDatabse class.
I have written a method to return all the method of a class
private IEnumerable<string> GetAllMethod(string pstrClassName)
{
const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
var llistMethod = new List<string>();
var assembly = Assembly.LoadFile(Server.MapPath(#"bin/InfoDomeBLL.dll"));
try
{
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass && type.Name == pstrClassName)
{
var method = type.GetMethods(flags);
foreach (var methodInfo in method)
{
llistMethod.Add(methodInfo.Name);
//var mb = methodInfo.GetMethodBody();
//foreach (LocalVariableInfo lvi in mb.LocalVariables)
//{
// Response.Write("Local variable: " + lvi);
//}
}
var basetype= type.BaseType;
}
}
}
catch (Exception)
{
}
return llistMethod;
}
In the web project i have added the reference of the bll project.
Kindly help me out.
If you use type.GetProperties(flags); instead of type.GetMethods(flags); you will find the property you are looking for. Then, do propertyInfo.GetValue( Command, null ); to get the value.

Any automated way to clone a data object?

I have a bunch of simple data objects of different types (all properties are writable, no hidden state). Is there any automated way to clone such objects?
(yes, I know the way to clone them manually. Just don't want to ^_^)
Serialize them in a (memory)stream and deserialize them back.
Serializing and deserializing would clone your object. Of course, the object would need to be serializable.
public static T Clone<T>(T source)
{
IFormatter formatter = new BinaryFormatter();
using (Stream stream = new MemoryStream())
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
The best approach is to implement IClonable interface in all objects but in case that objects developed not by you its not appropriate way for you.
The second way ( most universal in my opinion) is to use reflection:
public T CommonClone<T>(T Source)
{
T ret = System.Activator.CreateInstance<T>();
Type typeDescr = typeof(T);
if (typeDescr.IsClass != true)
{
ret = Source;
return ret;
}
System.Reflection.FieldInfo[] fi = typeDescr.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
for (int i = 0; i < fi.Length; i++)
{
fi[i].SetValue(ret, fi[i].GetValue(Source));
}
return ret;
}
The code above will copy both public and private fields. If you need to cope only public ones, just remove BindingFlags.NonPublic from GetFields method call.
This way not specified any limitation on objects which can use. Its working both for classes and structures.
using System.Web.Helpers;
public static T Clone<T>(T source)
{
return Json.Decode<T>(Json.Encode(source));
}
You might see this
Deep cloning objects
Take a look at second answer. I use it all the time and it works great. The only drawback of this solution is the fact that class must be serializable

Categories