views:

57

answers:

5

Hi

I would like to use an objects property as the key for a dictionary. Can this be done?

The ultimate goal for this is to use this so can see if the property is locked or not, in various states that an object can be in. These locked value is not persisted, just exist in the business rules for the model.

Ideal code to see if field is locked would look like this;

bool ageLocked = myObject.IsFieldLocked( x => x.Age);

bool nameLocked = myObject.IsFieldLocked(x => x.Name);

IsFieldLocked being an extension method for the type of myObject.

I would like the dictionary to live in myObject and be replaceable with different dictionary variations based on the state of the object, for example, has placed an order or awaiting order would have different dictionary definitions.

Hopefully I would be able to use a a factory to create the different dictionary variations;

Factory.CreateAwaitingOrderLockedFields()

Factory.CreateOrderPlacedLockedFields()

Defining the dictionary looking something like this

new Dictionary< ***MissingMagic***, bool>()
{
  { x => x.Age , true},
  { x => x.Name, false}
}

Aim is to avoid the key being a string, strongly typed key by far more desirable.

A: 

I think you should just use inheritance. Create a base class LockedField then create AwaitingOrderLockedField and OrderPlacedLockedField that inherit this class.

class LockedField {
}

class AwaitingOrderLockedField : LockedField {
}

class OrderPlacedLockedField : LockedField {
}

You dictionary will be IDictionary<LockedField, bool>

sh_kamalh
A: 

You can declare the dictionaty with any type as key;

Dictionary<Form,bool>

would create a dictionary where form elements are used as key.

Is this What you where asking?

If several different objects are to be used as key you could use Dictionary<object,bool>or let all objects inherit from another object Dictionary<masterobject,bool>.

David Mårtensson
+2  A: 

I'd define the dictionary simply as a Dictionary<string, bool>.

The extension method then could look something like this:

public static bool IsFieldLocked<TField>(this MyClass self, Expression<Func<MyClass, TField>> propertyExpression)
{
    // note: null checks etc omitted for brevity

    var lambda = (LambdaExpression)propertyExpression;
    MemberExpression memberExpression;
    if (lambda.Body is UnaryExpression)
    {
        var unaryExpression = (UnaryExpression)lambda.Body;
        memberExpression = (MemberExpression)unaryExpression.Operand;
    }
    else
    {
        memberExpression = (MemberExpression)lambda.Body;
    }

    string propertyName = memberExpression.Member.Name;

    return self.InternalLockedFieldsDictionary[propertyName];
}
herzmeister der welten
Brilliant the internal storage type string is encapsulated and got strongly typed checking on the dictionary construction and field locking checking. Many Thanks
c00ke
Is performance an issue with this solution?
c00ke
@c00ke, yes there actually is some overhead in creating expression trees unfortunately. But how often do you call that method though? You'd have to test if it really has an impact on your scenario.
herzmeister der welten
It will be used by my view models in an mvc app. Going to use the IsLockedField function to enable / disable ui elements from a spark view. I suppose therefore depends on how big the view model is.
c00ke
A: 

You will need to implement the IComparable interface in the class you want to use as key in a dictionary:

public class MyObject : IComparable {
  public int CompareTo(MyObject obj) {
    // Comparison Logic
  }
}

Since this is not really an option for a member of an object you may was well use Dictionary<string, bool> with the field name as the key and a bit of reflection in your IsFieldLocked() method to strip out the string from the strongly typed field.

Steven de Salas
A: 

Here is my cut down solution based on advice from herzmeister der welten

  public class MyDtoOne : BaseFieldLockingDto<MyDtoOne>
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public MyDtoOne()
        {
            LockedFields = new LockedFields<MyDtoOne>
                               {
                                   { x => x.Age, false }, 
                                   { x => x.Name, true }
                               };
        }
    }

    public class MyDtoTwo : BaseFieldLockingDto<MyDtoTwo>
    {
        public DateTime DateOfBirth { get; set; }

        public MyDtoTwo()
        {
            LockedFields = new LockedFields<MyDtoTwo>
                               {
                                   {x => x.DateOfBirth, false}
                               };
        }
    }

    public class BaseFieldLockingDto<TBaseObject>
    {
        public LockedFields<TBaseObject> LockedFields { get; set; }

        public bool IsFieldLocked<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            return LockedFields.IsFieldLocked(propertyExpression);
        }
    }

    public class LockedFields<TBaseObject> : Dictionary<string, bool>
    {
        public void Add<TField>(Expression<Func<TBaseObject, TField>> propertyExpression, bool isLocked)
        {
            Add(GenerateKey(propertyExpression), isLocked);
        }

        private static string GenerateKey<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            return GetLambdaPropertyName(propertyExpression);
        }

        public bool IsFieldLocked<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            if (Count == 0)
                return false;

            string propertyName = GetLambdaPropertyName(propertyExpression);

            if (ContainsKey(propertyName) == false)
                return false;

            return this[propertyName];
        }

        private static string GetLambdaPropertyName<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            var lambda = (LambdaExpression) propertyExpression;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression) lambda.Body;
                memberExpression = (MemberExpression) unaryExpression.Operand;
            }
            else
            {
                memberExpression = lambda.Body as MemberExpression;
            }

            if (memberExpression == null)
            {
                throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.",
                                                          propertyExpression));
            }

            return memberExpression.Member.Name;
        }
    }

With this I can do the following;

             private static void Main(string[] args)
    {
        var myDtoOne = new MyDtoOne();

        bool ageLocked = myDtoOne.IsFieldLocked(x => x.Age);
        bool nameLocked = myDtoOne.IsFieldLocked(x => x.Name);


        Console.WriteLine(string.Format("Age locked is {0}", ageLocked ? "true" : "false"));
        Console.WriteLine(string.Format("Name locked is {0}", nameLocked ? "true" : "false"));

        myDtoOne.LockedFields = new LockedFields<MyDtoOne> {{x => x.Age, true}, {x => x.Name, false}};


        bool ageLocked1 = myDtoOne.IsFieldLocked(x => x.Age);
        bool nameLocked1 = myDtoOne.IsFieldLocked(x => x.Name);

        Console.WriteLine(string.Format("Age locked is {0}", ageLocked1 ? "true" : "false"));
        Console.WriteLine(string.Format("Name locked is {0}", nameLocked1 ? "true" : "false"));


        var myDtoTwo = new MyDtoTwo();

        bool dateOfBirth = myDtoTwo.IsFieldLocked(x => x.DateOfBirth);

        Console.WriteLine(string.Format("Date of birth locked is {0}", dateOfBirth ? "true" : "false"));

        myDtoTwo.LockedFields = new LockedFields<MyDtoTwo>() {{x => x.DateOfBirth, true}};

        bool dateOfBirth1 = myDtoTwo.IsFieldLocked(x => x.DateOfBirth);

        Console.WriteLine(string.Format("Date of birth locked is {0}", dateOfBirth1 ? "true" : "false"));

        Console.ReadLine();
    }
}
c00ke