3
foreach (Dimension dimensions in Enum.GetValues(typeof(Dimension)))
{
    var r = new ReferenceTable(dimensions).referenceItems;
    List<TVRawDataRecord> qry = TVRawDataList.Where(p => !r.Any(d => d.Value == p.BrandVariant))
                                             .ToList();                
    DimensionItem di = new DimensionItem(qry, dimensions);
    newDimensions.Add(di); 
 }

I am trying to create a Linq query that compares a list of TVRawDataRecords to those in an enum of Dimensions and where there is no match then add them to a new DimensionIem list. This all works fine but I need to substitute dynamically the p.BrandVariant in my Where statement with the dimensions enum value as the dimension value is the same as the TVRawDataRecord property name. This would mean I can have just these few lines of code to loop through 8 dimensions etc.

Can someone explain how I include the dimension in my Where statement? Thanks!

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Paul
  • 55
  • 6
  • Why not substitute the property with the dimension value in your code? What happens when you do? What's not working? – nawfal Nov 22 '12 at 21:31
  • The dimension value is the name of the field I'm after so I'm essentially trying to substitute p.BrandVariant with 'p + dimension' in each loop. – Paul Nov 22 '12 at 22:01
  • that just compiles. Did you check yourself? You mean `p.Dimension` coincides with `Dimension` enum? Thats not an issue at all? Did you try? – nawfal Nov 22 '12 at 22:09
  • My issue is to replace p.BrandVariant dynamically on each foreach loop with the equivalent of 'p. + dimension' so the first pass will be p.BrandVariant, then p.Creative and so on - one for each enum. So I think the question is how I can update the Where statement dynamically. – Paul Nov 22 '12 at 23:02
  • So what is the linkage between p.Property and Dimension enum? Which enum value maps to which property of p? Or are they just one subsequent after the other etc? – nawfal Nov 22 '12 at 23:12
  • You can regard Property and enum as the same name/value. There is 8 properties and 8 enums that map on the same name. – Paul Nov 22 '12 at 23:15
  • That's a bizarre design, really. Well let me think of something – nawfal Nov 22 '12 at 23:18

1 Answers1

1

First and foremost this is really a bizarre thing to do. You should think of an alternate design first. There are a couple coming to me now.

Anyway you could use reflection to achieve what you are trying to achieve., well almost..

foreach (Dimension dimension in Enum.GetValues(typeof(Dimension)))
{
    var r = new ReferenceTable(dimension).referenceItems;
    var qry = TVRawDataList.Where(p => !r.Any(d => IsAMatch(p, dimension, d.Value)))
                           .ToList();     

    DimensionItem di = new DimensionItem(qry, dimension);
    newDimensions.Add(di); 
}

bool IsAMatch<T>(TVRawDataRecord obj, Dimension dimension, T valueToMatch)
{
    return valueToMatch == dimension.MapToTvRecordProperty<T>(obj);
}

T MapToTvRecordProperty<T>(this Dimension dimension, TVRawDataRecord obj)
{
    return obj.GetPropertyValue<T>(dimension.ToString());
}

T GetPropertyValue<T>(this TVRawDataRecord obj, string propertyName)
{
     var property = typeof(TVRawDataRecord).GetProperty(propertyName);
     if (property == null)
         return null; //or throw whatever

     return (T)property.GetValue(obj, null);
}

Strictly untested, uncompiled. But this should give an idea how it is done. You can make the GetPropertyValue function more generic, but that's a different thing. The T type argument in Map function (that maps a dimension enum to property of TVRawDataRecord class) is passed since you need to know the return type of the property.

I would say a better alternate design is just to make a simple function that uses if else logic to return the right type. So change Map function to this:

T MapToTvRecordProperty<T>(this Dimension dimension, TVRawDataRecord obj)
{
    switch (dimension)
    {
        case Dimension.BrandVariant:
            return obj.BrandVariant;
        case Dimension.Creative:
            return obj.Creative;

        .....

        default:
            throw;
    }
}

The advantage is that even if in future you change the name of any of the variables, your code wouldn't break (unlike the reflection approach). But the catch here is to choose the return type T. The second example wouldnt compile since return type doesnt match what is being returned. If all the properties are of the same type, then you can choose that type. If it's so really variable, then you will have to cast your property first to object and then to T, but still better than reflection!!

An even better approach would be to specify attributes either to property or to enum.

And best of all if BrandVariant and Creative etc are classes of their own, you can make them all implement an interface which will have a property readonly Dimension on them and you can access that property of your tv record properties to get the right dimension value!

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368