GustavoAdolfo
GustavoAdolfo

Reputation: 378

complex way for update generic data model with reflection and collections

I am standing on a complex issue for me. I need update some models, but I like to work with a generic class not to rewrite some code individually.

I need to update data that have lists on their properties, with possible exclusion or inclusion of items on these lists, but these lists can be of any other class / type. My questions are commented on the code.

These models are unrealistic and a bit absurds but have similarities with my real models, note that the logic is reversed on these relationships during updates.

Thanks for all.

public class RedNotebook 
{
    [Key]   
    public int Id { get; set; }
    public string PageTitle { get; set; }
    public virtual ICollection<Signature> Signatures { get; set; }  
}

public class BlueNotebook 
{
    [Key]
    public int Id { get; set; }
    public DateTime Entrance { get; set; }
    public DateTime Leave { get; set; }
    public virtual ICollection<Guest> GuestList { get; set; }   
}

public class Signature
{
    [key]
    public int Id { get; set; }
    public string PeopleSignature { get; set; }
    public int IdRedNotebook { get; set; }
    public int IdBlueNotebook { get; set; }
    [ForeignKey("IdRedNotebook")]
    public virtual RedNotebook { get; set; }
    [ForeignKey("IdBlueNotebook")]
    public virtual BlueNotebook { get; set; }   
}

public class Guest
{
    [key]
    public int Id { get; set; }
    public string Name { get; set; }
    public int SeatNumber { get; set; } 
    public int IdBlueNotebook { get; set; }
    [ForeignKey("IdBlueNotebook")]
    public virtual BlueNotebook { get; set; }   
}

/**********************/

public void UpdateData(T newData, out string msg)
{
    try
    {
        var propId = newData.GetType().GetProperty("Id");
        if (propId == null)
        {
            msg = "Unable to identify the identity of the reported data.";
            return;
        }
        int id = Convert.ToInt32(propId.GetValue(newData));
        if (id <= 0)
        {
            msg = "Unable to identify the identity of the reported data.";
            return;
        }
        //instance a determined DbContext and Model<T>
        var contexto = new CtxCliente(DAO.Classes.Util.InstanciarConexao(strCripto, (DAO.Conectores) Conector));
        var model = contexto.Set<T>();
        var targetData = model.Find(id);

        if (targetData == null)
        {
            model.Add(newData);
            contexto.Entry(model).State = EntityState.Added;
            msg = "An addition was made because there was no previous reference.";
        }
        if (Convert.ToInt32(targetData.GetType().GetProperty("Id").GetValue(targetData)) > 0)
        {
            contexto.Entry(targetData).CurrentValues.SetValues(newData);
            contexto.Entry(targetData).State = EntityState.Modified;
            msg = string.Empty;
        }   

        //TODO - 1) GET THE VIRTUAL PROPERTIES OF WHICH TYPE targetData ICollection
        //TODO - 2) COMPARE THE CONTENT OF VIRTUAL PROPERTIES OF targetData WITH THE CONTENTS OF VIRTUAL PROPERTIES UPDATE, BOTH ICollection
        //TODO - 3) REMOVE EXCESS OF targetData AND / OR ADD THAT AS THE CASE MAY BE MISSING (A - CLEAR DIFFERENCE, B - ADD DIFFERENCE)

        //through the properties to identify those that are of the collection type
        foreach (var propertytargetData in targetData.GetType().GetProperties())
        {
            if (!propertytargetData.PropertyType.IsGenericType) 
                continue;

            var propsNewData = newData.GetType().GetProperty(propertytargetData.Name);

            #region
            //if all list items were removed on update
            if (propsNewData == null && propertytargetData != null)
            {
                // NOT TESTED, MAYBE NOT WORK CORRECTLY
                propertytargetData.SetValue(targetData,null);
            }
            //If an item was included or removed
            else if (propsNewData != null)
            {
                var valTargetData = propertytargetData.GetValue(targetData);
                var valNewData = propsNewData.GetValue(newData);
                var listItemsTargetData = (IEnumerable) valTargetData;
                var listItemsNewData = (IEnumerable) valNewData;
                int countItemsTargetData = listItemsTargetData.Cast<object>().Count();
                int countItemsNewData = listItemsNewData.Cast<object>().Count();

                if (countItemsTargetData > countItemsNewData) //remove discarded
                {
                    foreach (var itemtargetData in listItemsTargetData)
                    {
                        var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
                        var existing = (from object itemListNewData in listItemsNewData 
                                      select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
                                      .Any(iditemListNewData => (int) idItemtargetData == (int) iditemListNewData);

                        if (!existing) //remove                     
                        {
                            //how to remove from the list?????? (targetData)
                        }
                        else //update
                        {
                            foreach (var itemListNewData in listItemsNewData)
                            {
                                var props = itemListNewData.GetType().GetProperties();
                                foreach (var propertyInfo in props)
                                {
                                    foreach (var item in listItemsTargetData)
                                    {
                                        var p = item.GetType().GetProperty(propertyInfo.Name);
                                        if (p != null && !p.PropertyType.IsGenericType)
                                        {
                                            p.SetValue(item, propertyInfo.GetValue(itemListNewData));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else if (countItemsTargetData < countItemsNewData) //Items need to be included
                {
                    foreach (var newItem in listItemsNewData)
                    {
                        var idnewItem = newItem.GetType().GetProperty("Id").GetValue(newItem);
                        if ((int) idnewItem == 0)
                        {
                            //how to insert in list???????? (targetData)
                        }
                        else // remove and/or update some before (reduntant!?)
                        {
                            foreach (var itemtargetData in listItemsTargetData)
                            {
                                var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
                                var existing = (from object itemListNewData in listItemsNewData
                                              select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
                                              .Any(iditemListNewData => (int)idItemtargetData == (int)iditemListNewData);
                                if (!existing) //remove                     
                                {
                                    //how to remove from the list?????? (targetData)
                                }
                                else //update
                                {
                                    foreach (var itemListNewData in listItemsNewData)
                                    {
                                        var props = itemListNewData.GetType().GetProperties();
                                        foreach (var propertyInfo in props)
                                        {
                                            foreach (var item in listItemsTargetData)
                                            {
                                                var p = item.GetType().GetProperty(propertyInfo.Name);
                                                if (p != null && !p.PropertyType.IsGenericType)
                                                {
                                                    p.SetValue(item, propertyInfo.GetValue(itemListNewData));
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        contexto.SaveChanges(); //save data on model
    }
    catch(...){}
}

Upvotes: 0

Views: 363

Answers (1)

user629926
user629926

Reputation: 1940

Haven't tested it . But it should work if both source and dest implement the same ICollection interface and T has an Id property of type System.Int32. It uses the new dynamic keyword that enables you to do duck typing ;

private class IdComparer : IEqualityComparer<object>
    {

        public bool Equals(object x, object y)
        {
            //return ((dynamic) x).Id = ((dynamic) y).Id; //previous with convertion error
            return ((dynamic) x).Id == ((dynamic) y).Id;                
        }

        public int GetHashCode(object obj)
        {
           return ((dynamic) obj).Id;
        }
    }

    private static void Copy(IEnumerable source, IEnumerable dest)
    {
        var cmp = new IdComparer();
        var toRemove = dest.Cast<object>().Except(source.Cast<object>(),cmp).ToList();
        var toAdd= source.Cast<object>().Except(dest.Cast<object>(),cmp).ToList();

        foreach(var item in toAdd)
        {
           // dynamic runtime tries to find method that matches signiture void Add(T value  so we add dummy variable so that it knows to search for bool Add(T value)
           var dummy= ((dynamic) dest).Add(item);
        }

        foreach (var item in toRemove)
        {
          var dummy=  ((dynamic)dest).Remove(item);
        }

    }

Upvotes: 1

Related Questions