Code Monkey home page Code Monkey logo

anyclone's Introduction

AnyClone

nuget nuget Build status Codacy Badge Codacy Badge

A CSharp library that can deep clone any object using only reflection.

No requirements for [Serializable] attributes, supports standard ignore attributes.

Description

I built this library as almost all others I tried on complex objects either didn't work at all, or failed to account for common scenarios. Serialization required too much boiler plate (BinarySerialization, Protobuf, or Json.Net) and fails to account for various designs. Implementing IClonable was too much of a chore and should be unnecessary. Various projects that use expression trees also failed to work, IObservable patterns were difficult to implement on large, already written code base.

Installation

Install AnyClone from the Package Manager Console:

PM> Install-Package AnyClone

Usage

using AnyClone.Extensions;

var originalObject = new SomeComplexTypeWithDeepStructure();
var myClonedObject = originalObject.Clone();

Capture Errors

using AnyClone.Extensions;

var originalObject = new SomeComplexTypeWithDeepStructure();
// capture errors found with your object where impossible situations occur, and add [IgnoreDataMember] to those properties/fields.

var myClonedObject = originalObject.Clone((ex, path, property, obj) => {
  Console.WriteLine($"Cloning error: {path} {ex.Message}");
  Assert.Fail();
});

Get differences between cloned objects using AnyDiff

using AnyClone.Extensions;
using AnyDiff;

var object1 = new MyComplexObject(1, "A string");
var object1Snapshot = object1.Clone();

var diff = AnyDiff.Diff(object1, object1Snapshot);
Assert.AreEqual(diff.Count, 0);

// change something anywhere in the object tree
object1.Name = "A different string";

diff = AnyDiff.Diff(object1, object1Snapshot);
Assert.AreEqual(diff.Count, 1);

Ignoring Properties/Fields

There are unfortunately a few situations that can't be resolved, such as cloning delegates, events etc. Fortunately, you can specify attributes that AnyClone will skip properties decorated with them. By default, AnyClone will ignore properties decorated with the following attributes: IgnoreDataMemberAttribute, NonSerializedAttribute, JsonIgnoreAttribute. If you wish to disable this behavior, or provide other attributes that you wish to decorate properties to ignore you can provide a custom CloneConfiguration:

using AnyClone;
using AnyClone.Extensions;

var originalObject = new SomeComplexTypeWithDeepStructure();
var myClonedObject = originalObject.Clone(CloneConfiguration.UsingAttributeNamesToIgnore("IgnoreDataMemberAttribute", "MyCustomAttribute"));

Both attribute names and attribute types can be specified:

var myClonedObject = originalObject.Clone(CloneConfiguration.UsingAttributeNamesToIgnore("IgnoreDataMemberAttribute", "MyCustomAttribute"));
var myOtherClonedObject = originalObject.Clone(CloneConfiguration.UsingAttributesToIgnore(typeof(IgnoreDataMemberAttribute), typeof(MyCustomAttribute)));

Other Applications

If you need to perform serialization instead of deep copying, try out AnySerializer which doesn't require attribute decoration and works on complex structures.

anyclone's People

Contributors

replaysmike avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

anyclone's Issues

Exception Thrown - Constructor on type 'System.Windows.TriggerCollection' not found.

Attempting to clone a derived TreeViewItem Class fails as follows:

System.MissingMethodException
HResult=0x80131513
Message=Constructor on type 'System.Windows.TriggerCollection' not found.
Source=mscorlib
StackTrace:
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at System.Activator.CreateInstance(Type type, Object[] args)
at TypeSupport.ObjectFactory.CreateEmptyObject(ExtendedType type, <7bda29db-1b56-429f-8967-85a9285c2b07>TypeRegistry typeRegistry, Func1 initializer, Object[] dimensions) at TypeSupport.<d27d0cc4-2c86-49c9-a412-185d46fea44b>ObjectFactory.CreateEmptyObject(<c3e10fc2-1636-4cf0-bd89-c643d1266985>ExtendedType type) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Object destinationObject, Type destinationObjectType, Int32 currentDepth, Int32 maxDepth, CloneConfiguration configuration, ObjectTreeReferenceTracker objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Object destinationObject, Type destinationObjectType, Int32 currentDepth, Int32 maxDepth, CloneConfiguration configuration, ObjectTreeReferenceTracker objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Object destinationObject, Type destinationObjectType, Int32 currentDepth, Int32 maxDepth, CloneConfiguration configuration, ObjectTreeReferenceTracker objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Object destinationObject, Type destinationObjectType, Int32 currentDepth, Int32 maxDepth, CloneConfiguration configuration, ObjectTreeReferenceTracker objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.Clone(T sourceObject, CloneConfiguration configuration)
at AnyClone.CloneExtensions.Clone[T](T sourceObject, CloneConfiguration configuration)
at AnyClone.CloneExtensions.Clone[T](T sourceObject)
at *******************(Object sender, MouseEventArgs e) in ************************:line 2243
at System.Windows.Input.MouseEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
at System.Windows.Input.InputManager.ProcessStagingArea()
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at TestForm.Application.Main()

Comparing two complex object returns an exception

Hi. I have a fairly complex objects to compare. Its basically Dictionary<string, object>, where the number of keys inside dictionary is 23. Some are plain strings, some are Boolean, but some are StrokeCollection, List< CustomTypeDTO >, OtherCustomTypeDTO etc...you get the point, its all there ๐Ÿ˜„

I'm using FastDeepCloner currently to create snapshot for some of the properties that are collections, because AnyClone also throws exception, something like: "Type is ambiguous". So after the snapshot of some properties, the original object and the one with snapshot properties are compared using AnyDiff. I get the following exception:

Sequence contains no matching element

Stack trace:

at System.Linq.ThrowHelper.ThrowNoMatchException()
at System.Linq.Enumerable.Single[TSource](IEnumerable1 source, Func2 predicate)
at TypeSupport.Extensions.TypeExtensions.GetExtendedProperty(Type type, String name, Type declaringType)
at TypeSupport.ExtendedField..ctor(FieldInfo fieldInfo)
at TypeSupport.ExtendedField.op_Implicit(FieldInfo fieldInfo)
at TypeSupport.Extensions.TypeExtensions.<>c.b__1_0(FieldInfo x)
at System.Linq.Enumerable.SelectArrayIterator2.MoveNext() at System.Linq.Enumerable.ConcatIterator1.MoveNext()
at System.Linq.Enumerable.SelectEnumerableIterator2.ToList() at TypeSupport.Extensions.TypeExtensions.GetFields(Type type, FieldOptions options) at TypeSupport.Extensions.TypeExtensions.GetFields(Type type, FieldOptions options) at AnyDiff.DiffProvider.RecurseProperties(Object left, Object right, Object parent, List1 differences, Int32 currentDepth, Int32 maxDepth, ObjectHashcodeMap objectTree, String path, ComparisonOptions comparisonOptions, ICollection1 propertyList, DiffOptions diffOptions) at AnyDiff.DiffProvider.GetDifferences(String propertyName, Type propertyType, TypeConverter typeConverter, Object left, Object right, Object parent, List1 differences, Int32 currentDepth, Int32 maxDepth, ObjectHashcodeMap objectTree, String path, ComparisonOptions options, ICollection1 propertyList, DiffOptions diffOptions) at AnyDiff.DiffProvider.RecurseProperties(Object left, Object right, Object parent, List1 differences, Int32 currentDepth, Int32 maxDepth, ObjectHashcodeMap objectTree, String path, ComparisonOptions comparisonOptions, ICollection1 propertyList, DiffOptions diffOptions) at AnyDiff.DiffProvider.ComputeDiff[T](T left, T right, Int32 maxDepth, ComparisonOptions comparisonOptions, DiffOptions diffOptions, Expression1[] propertyList)
at AnyDiff.Extensions.Extensions.Diff[T](T left, T right, Int32 maxDepth, ComparisonOptions comparisonOptions, DiffOptions diffOptions, Expression1[] propertyList) at AnyDiff.Extensions.Extensions.Diff[T](T left, T right, Expression1[] propertyList)

Cannot set initonly static field 'Invariant' after type 'System.Globalization.CompareInfo' is initialized. Cannot set initonly static field 'ValueType' after type 'System.RuntimeType' is initialized.

at System.RuntimeFieldHandle.SetValue(RtFieldInfo field, Object obj, Object value, RuntimeType fieldType, FieldAttributes fieldAttr, RuntimeType declaringType, Boolean& domainInitialized)
at System.Reflection.RtFieldInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
at System.Reflection.FieldInfo.SetValue(Object obj, Object value)
at TypeSupport.Extensions.ObjectExtensions.SetFieldValue(Object obj, FieldInfo field, Object valueToSet)
at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Int32 currentDepth, Int32 maxDepth, CloneOptions options, IDictionary2 objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Int32 currentDepth, Int32 maxDepth, CloneOptions options, IDictionary2 objectTree, String path, ICollection1 ignorePropertiesOrPaths)
at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Int32 currentDepth, Int32 maxDepth, CloneOptions options, IDictionary2 objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Int32 currentDepth, Int32 maxDepth, CloneOptions options, IDictionary2 objectTree, String path, ICollection1 ignorePropertiesOrPaths)
at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Int32 currentDepth, Int32 maxDepth, CloneOptions options, IDictionary2 objectTree, String path, ICollection1 ignorePropertiesOrPaths) at AnyClone.CloneProvider1.InspectAndCopy(Object sourceObject, Int32 currentDepth, Int32 maxDepth, CloneOptions options, IDictionary2 objectTree, String path) at AnyClone.CloneProvider1.Clone(T sourceObject, CloneOptions options)
at AnyClone.Cloner.Clone[T](T sourceObject, CloneOptions options)
at AnyClone.Cloner.Clone[T](T sourceObject)

Change of license?

Howdy!

Would there be any chance of changing the license to MIT or some such? I'm in a situation where this library, so far, is the only one that seems to handle the deep cloning of a Swashbuckle.Application.SwaggerDocsConfig object.

Where I need to use it, however, is in a product that cannot be licensed under GPL, so the copyleft nature would in fact prevent me from using this.

I realise that this is likely too big a pain, but thought it worth asking.

Thanks!

Still maintained?

Hi,
thanks for creating and sharing this great cloning library!

I was using DeepCloner in the past for cloning objects, but ran into some concurrency issues when using the library. After some investigation of the source code of DeepCloner, I came to the conclusion that DeepCloner seems not to be thread-safe, and even if I would be able to fix the issue, the code base would be hard to maintain. I am currently looking for a replacement, so I decided to have a deeper look at the other libraries.

I checked out the "FastDeepCloner" library, which implementation looks solid. But the "FastDeepCloner" contains a lot of code blocks that are untested - though there is a test project, it only covers the very basics.

So I am currently in Favor or this library - I think that this has the most potential. I really liked that there are already many tests in place. I already did some testing, and would like to continue to work with this library.

Before I create an PR requests etc., just wanted to get in touch and see if this library is still actively maintained?

AutoMapper vs AnyClone

It is merely a question rather than a bug or issue.

Is AnyClone preferred over AutoMapper.Map<T, T>() method ? Without being too specific.

And being a little more; do you actually handle anytihing special around properties like ;

        [BsonId]
        public long Id { get; set; }

some thing wrong in this example

    public class CBase<TKey>
    {
        public TKey Id { get; set; }
    }


    public class C3 : CBase<int>
    {
        public new int Id { get; set; }
    }

    public class C2 : CBase<int>
    {

        public C3 c3 { get; set; } = new();
    }

    public class C1 : CBase<int>
    {
        public C2 c2 { get; set; } = new();
    }

    public class CloneAndDiffTest : TestBase
    {

        [Fact]
        public void TestClone()
        {
            var org = Fixture.Create<C1>();
            var c1 = FastDeepCloner.DeepCloner.Clone(org);
            var c2 = AnyClone.CloneExtensions.Clone(org);// pst.MapClone();
            var diffs_0 = org.Diff(c1);
            var diffs_1 = org.Diff(c2);
    }
  }

Clone references

I have and object like that:

Person (string Name, Person subp1, Person subp2)

If i create

P1=new Person ("p1",null,null)

and then

P2= new ("p2",P1,P1)

and i clone this P2 object as P3, its very strange but first child subp1 creates a new object but subp2 gets linked to the original.

P3.subp1 memory address is different of P2.subp1 "BUT" P3.subp2 and P2.subp2 have the same memory address, so any changes on the subp2 original gets replicated on the cloned instance and viceversa.

overriding json ignore

I have some properties in my dto models which have json ignores tags on them bcz we dont need them in the db but we do need them on the client side. so that means when I created my backup object while editing I do need to copy these properties as well, so using this library can I override and hence also copy the properties tagged with json ignore? Thanks

clone System.Text.Json.Serialization.JsonSerializerOptions throws exception.

trying to clone JsonSerializerOptions in dotnet core 3 throws exception.

JsonNamingPolicy.CamelCase is not an enum but some crazy object.

using System.Text.Json.Serialization;

    public void ShouldCloneJsonSerializationOptions()
    {
      var jsonSerializerOptions = new JsonSerializerOptions
      {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
      };

      JsonSerializerOptions clone = jsonSerializerOptions.Clone();
      clone.PropertyNamingPolicy.ShouldBe(JsonNamingPolicy.CamelCase);
    }

Tests fail when run inside Blazor

Mike,

I would love to use this component in my Blazor-State component and thus eliminate the developer burden of creating deep Clone methods.

It works great when I use it on Server Side Blazor (SSB). But I get an exception when running Client Side Blazor (CSB).

CSB runs under mono whereas SSB runs dotnet core. I spent about 6 hours pulled in AnyClone and TypeSupport source. Moved all the tests cases into a Blazor Page.

turns out that in public ExtendedProperty(PropertyInfo propertyInfo) for Generic List and the Capacity Property the following returns null in CSB

_propertyInfo.GetGetMethod(true)

and in SSB it returns the appropriate MethodInfo.

Have you run your tests against a mono build?

Any suggestions?

This looks like it could be an awesome solution for my use case.

Fill free to reach out to me on Discord (StevenTCramer
#9010) or Twitter (StevenTCramer) if more convenient for you.

Thanks!

Does not deep copy mutable value types

Hi fellow deepcopy developer, when I ran your library against my unit tests (I was actually looking to benchmark it against the purely reflection based approach) an issue popped up: AnyClone v1.1.6 does not deep copy value types that contain references to mutable classes. Here's the unit test:

        [TestMethod]
        public void Copy_DeepCopiesMutableFieldsOfValueTypes()
        {
            // Tuple itself is a mutable valuetype, MySingleObject is a class
            var a = new Tuple<MySingleObject>(new MySingleObject());
            var b = AnyClone.CloneExtensions.Clone(a);
            Assert.AreNotSame(a.Item1, b.Item1);    // this assert fails
            Assert.AreEqual(a.Item1, b.Item1);
            Assert.AreEqual(a, b);
        }

In other words this will result in two copies of Tuple that share the same 'MySingleObject' instance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.