C# Not able to clone the entity object

100 Views Asked by At

I have an entity that has nested references down to 8th level of depth.

I have tried using System.Text.Json.Serializer with option `ReferenceHandler'

Also JsonConvert.SerializeObject with its option ReferenceHandler.Ignore

Both of the above mentioned options could not able to clone; i was never getting back to the callee function. Max i waited was 10 minutes.

I guess I will have to re-invent the clone functionality.

Meanwhile my question would be : are there other optimal alternative options i can work with? that supports cloning of DB entities and handles reference circulation.

UPDATE

Below is the recursive functionality i have created using the Reflection technology of C#. It handles the reference circular dependency as well as has the feature to skip entities that are not needed to be cloned; in my case these were the 'reference tables' and with it i am able to clone entities to Nth level in microseconds/seconds however i am certain it can be more optimized and concised.

public object Clone(string[] skipEntites, Type circularType = null)
{
    var newEntity = Activator.CreateInstance(this.GetType());

    PropertyInfo[] entityProperties = newEntity.GetType().GetProperties();

    foreach (PropertyInfo entityProperty in entityProperties)
    {
        var value = this.GetType().GetProperty(entityProperty.Name).GetValue(this);

        if (TryGetArgumentTypesFromHashSet(value, out Type[] argumentTypes))
        {
            if (circularType != null && circularType.Name.Contains(argumentTypes[0].Name))
            {
                continue;
            }

            Type hashSetType = typeof(HashSet<>).MakeGenericType(argumentTypes[0]);
            var hashSet = Activator.CreateInstance(hashSetType);
            MethodInfo addMethod = hashSetType.GetMethod("Add");

            var entities = (IEnumerable)value;
            foreach (IEntity entity in entities)
            {
                addMethod.Invoke(hashSet, new object[] { entity.Clone(skipEntites, GetType()) });
            }

            SetValue(entityProperty, hashSet, newEntity, skipEntites);
            continue;
        }

        if (value != null && circularType != null && circularType.Name.Contains(value.GetType().Name))
        {
            continue;
        }

        if (circularType != null
            && circularType.Name.Contains(entityProperty.Name.Substring(0, entityProperty.Name.Length-2))
            && entityProperty.Name.Contains("Id")
            && (entityProperty.PropertyType == typeof(int)
               || entityProperty.PropertyType == typeof(long)
               || entityProperty.PropertyType == typeof(int?)
               || entityProperty.PropertyType == typeof(long?))
            )
        {
            entityProperty.SetValue(newEntity, 0);
            continue;
        }

        SetValue(entityProperty, value, newEntity, skipEntites);
    }

    return newEntity;
}

private bool TryGetArgumentTypesFromHashSet(object value, out Type[] argumentTypes)
{
    argumentTypes = null;

    if (value == null)
    {
        return false;
    }

    Type type = value.GetType();
    argumentTypes = type.GetGenericArguments();

    return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(HashSet<>) && argumentTypes.Length > 0;
}

private void SetValue(PropertyInfo entityProperty, object value, object newEntity, string[] skipEntites)
{
    if (value != null && skipEntites.Any(x => value.GetType().Name.Contains(x)))
    {
        return;
    }

    if (value is IEntity referenceObject)
    {
        var newReference = referenceObject.Clone(skipEntites, GetType());

        entityProperty.SetValue(newEntity, newReference);
        return;
    }

    if (entityProperty.Name == "Id" && (entityProperty.PropertyType == typeof(int) || entityProperty.PropertyType == typeof(long)))
    {
        entityProperty.SetValue(newEntity, 0);
        return;
    }

    entityProperty.SetValue(newEntity, value);
}
0

There are 0 best solutions below