1

I know title is very confusing. Below is my scenario

I have a contract object for my Service like the following.

class UpdateRequest
{
   public int EmployeeId { get; set; }
   public string EmployeeName { get; set; }
   public decimal Salary { get; set; }
}

Now the contract object of above class is passed to a method that updates a record in the database. Suppose I want to update an employee with an Id: 33 but I only want to change its name and I want to leave the Salary alone.

One way is to not worry about such details and update everything, but that would require the clients of my service to pass me the Salary value too so it does not get overwritten to a default(decimal) value.

Another way I could think of is to create the following type

public class TrackedValue<TInternal> : where TInternal:new
{
  private TInternal value = default(TInternal);
  public TInternal Value { get; }
  private bool initialized;

  public static implicit operator TrackedValue<TInternal>(TInternal v)
  {
     return new TrackedValue<TInternal> { value = v, initialized = true };
  }
}

Is there any other way ? Does .NET not have anything like above already available ? I am assuming if they have things like Nullable they have got to have something for this problem too.

Nullable types wont help me. There are use-cases where I have to change an existing value of some table-column to null. That would throw it off.

UPDATE: Some people might say, My service should check property values against default for that type. Default value for 'bool' is false, if my methods are called with a bool property value to false, it will break my logic.

fahadash
  • 3,133
  • 1
  • 30
  • 59
  • 1
    Use nullable types and check for null? – D Stanley Nov 18 '14 at 01:42
  • the only way I can think to handle this case is to make everything nullable; if it's null, it hasn't been initialized. You could also look at something like the Entity Framework which does change tracking, and can identify which properties have *changed*... – Michael Edenfield Nov 18 '14 at 01:43
  • @DStanley What if clients of my service wants to set some column value to null ? I have a few use cases that do that. – fahadash Nov 18 '14 at 01:43
  • 1
    "Traditional" way is to store old (current values) and compare the new value to the old one before saving. – Onots Nov 18 '14 at 01:45
  • @Onots I have already discussed that in my question. It is a burden on my clients to "fill" the huge contract objects (my objects have about 20 properties) with all the values even when they are changing only one or two columns. – fahadash Nov 18 '14 at 01:46
  • @fahadesh, you can bear the burden yourself by retrieving the current data on each update request, determine which values changed and save only those. – Onots Nov 18 '14 at 01:53

2 Answers2

4

Your TrackedValue class is pretty much spot on. However, you've sort of re-invented the wheel. This is already known as the Maybe<T> monad.

Look up the "Maybe" monad and you'll get a lot of detail, but here it is:

public class Maybe<T>
{
   public readonly static Maybe<T> Nothing = new Maybe<T>();

   public T Value { get; private set; }
   public bool HasValue { get; private set; }

   public Maybe()
   {
       HasValue = false;
   }

   public Maybe(T value)
   {
       Value = value;
       HasValue = true;
   }
}

It's very much like nullable types, but it isn't limited to value types.

So you could have this:

class UpdateRequest
{
   public int EmployeeId { get; set; }
   public Maybe<string> EmployeeName { get; set; }
   public Maybe<decimal> Salary { get; set; }
}

If you're trying to only update the salary, for example, then you could initialize the class like this:

var request = new UpdateRequest()
{
    EmployeeId = 1234,
    EmployeeName = Maybe<string>.Nothing,
    Salary = new Maybe<decimal>(50000m),
};

Alternatively you could define a .ToMaybe() extension method like this:

   public static Maybe<T> ToMaybe<T>(this T value)
   {
       return new Maybe<T>(value);
   }

Then the initialization looks like this:

var request = new UpdateRequest()
{
    EmployeeId = 1234,
    EmployeeName = Maybe<string>.Nothing,
    Salary = 50000m.ToMaybe(),
};

Or you could add an implicit operator like this:

   public static implicit operator Maybe<T>(T v)
   {
       return v.ToMaybe();
   }

Now the initialization is even simpler:

var request = new UpdateRequest()
{
    EmployeeId = 1234,
    EmployeeName = Maybe<string>.Nothing,
    Salary = 50000m,
};

You should then define the two "bind" operators for the type - aka SelectMany in linq - like so:

  public static Maybe<U> Select<T, U>(this Maybe<T> m, Func<T, U> k)
   {
        return m.SelectMany(t => k(t).ToMaybe());
   }

    public static Maybe<U> SelectMany<T, U>(this Maybe<T> m, Func<T, Maybe<U>> k)
   {
       if (!m.HasValue)
       {
           return Maybe<U>.Nothing;
       }
       return k(m.Value);
   }

   public static Maybe<V> SelectMany<T, U, V>(this Maybe<T> m, Func<T, Maybe<U>> k, Func<T, U, V> s)
   {
       return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToMaybe()));
   }

Now you can do LINQ queries against maybe values.

Maybe<decimal> tax =
    from salary in request.Salary
    select salary * 0.1m;

Then, if request.Salary was Nothing then tax would be Nothing. Otherwise the calculation would be performed.

I hope this helps.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
1

One option is, instead of being tricky in your class, you can push the functionality to the update method instead, and let your users specify which columns they want updated.

I've implemented this in the past when we ran into perf issues when large queries were being executed and we were passing some large objects around with unnecessary data (we had a ginormous description field that was rarely changed or needed).

public bool UpdateObjects(List<UpdateRequest> objects, List<string> propertiesToUpdate)
{
    // code here to update objects matched on primary key field, 
    // and set only the specified properties (using reflection)
}

We also created a few easy-to use wrappers around this which gave an option to exclude the 'bulky' property, like:

public bool UpdateObjects(List<UpdateRequest> objects, bool includeDescription)

There is an example of setting properties based on reflection here: Setting a property by reflection with a string value

Community
  • 1
  • 1
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • I had lost faith in humanity when I started getting the comments to my question. But yours and @Enigmativity 's answer restored my faith. Almost all the people who commented on my question either did not understand the problem or read the question very well. Coming to your solution, it is good but it sounds error-prone because you are relying on spelling to be correct in strings – fahadash Nov 18 '14 at 03:40