lundi 14 novembre 2016

Ignore properties with custom attribute set, using reflection

I have written a generic method that generates a collection of generic types from a Datatable. I have looked for different implementations, but most of them were performing very poorly performance wise when dealing with lots of properties and a lot of records. This one is performing quite well so far.

I tried to improve the method by adding a custom attribute ( DataField ) on top of the properties, this way I could just include it on the properties and I can skip matching it up with the columns, or specify a custom name for the property that will match the datatable's column name.

I looked at the code and it looks like a huge mess now I'm really not proud of it and I want to have a nicer implementation. Can anyone give me some tips? Would appreciate it a lot.

Tried to include comments not sure how much it helped. Thank you, here is the code:

private static void SetItemFromRow<T>(T item, DataRow row) where T : new()
    {
        // Get all properties with attributes.
        PropertyInfo[] propWithAttributes = item.GetType().GetProperties().Where(x => Attribute.IsDefined
          (x, typeof(DataField))).ToArray();

        foreach (DataColumn col in row.Table.Columns)
        {
            // Find property that matches the column name.
            PropertyInfo p = item.GetType().GetProperty(col.ColumnName);
            bool ignoreProperty = false;

            if (p != null)
            {
                // If no attribute exists set the property value. Break out from the loop to go to the next column (Property).
                if (!propWithAttributes.Contains(p))
                {
                    if (row[col] != DBNull.Value)
                    {
                        p.SetValue(item, row[col], null);
                        continue;
                    }
                }

                // If the property has a custom attribute then check if its ignore property is true. If so we break out from the loop and go to the next column (Property). 
                var attrs = p.GetCustomAttributes(typeof(DataField), false).ToArray() as DataField[]; ;

                if (attrs != null)
                    foreach (var attr in attrs)
                    {
                        if (attr.Ignore)
                            ignoreProperty = true;
                    }

                if (ignoreProperty) continue;
            }

            SetPropertyWithCustomName(item, propWithAttributes, row, col);    
        }
    }

Now we have all properties set on the object that had a matching column name, also we skipped all the properties that we wanted to ignore. Final step is to set the properties that have a DataField attribute with the Name defined.

            private static void SetPropertyWithCustomName<T>(T item, PropertyInfo[] propWithAttributes,  DataRow row,  DataColumn col)
        where T : new()
    {

        foreach (var prop in propWithAttributes)
        {
            // Get the attributes for the property.
            var attrs = prop.GetCustomAttributes(typeof(DataField), false).ToArray() as DataField[];
            bool match = false;

            if (attrs != null)
            {
                foreach (var attr in attrs)
                {
                    // Check if the column name matches the custom name on the property.
                    if (col.ColumnName == attr.Name)
                    {
                        var p = item.GetType().GetProperty(prop.Name);
                        if (row[col] != DBNull.Value)
                        {
                            p.SetValue(item, row[col], null);
                            match = true;
                            break;
                        }
                    }
                }

            if (match) break;

            }
        }
    }





Aucun commentaire:

Enregistrer un commentaire