我知道标题很混乱。以下是我的方案
我的服务合同对象如下所示。
class UpdateRequest
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public decimal Salary { get; set; }
}
现在,上面类的契约对象被传递给更新数据库中记录的方法。假设我想用Id:33更新员工,但我只想更改其名称,我想单独留下薪水。
一种方法是不要担心这些细节并更新所有内容,但这需要我服务的客户端也传递Salary值,以免被覆盖到默认(十进制)值。
我能想到的另一种方法是创建以下类型
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 };
}
}
还有其他方法吗? .NET没有上面已有的东西吗?我假设如果他们有像Nullable这样的东西他们也必须为这个问题找到一些东西。
Nullable类型不会帮助我。在某些用例中,我必须将某些table-column的现有值更改为null。那会把它扔掉。
更新:有些人可能会说,我的服务应该针对该类型的默认值检查属性值。 &#39; bool&#39;的默认值是假的,如果我的方法被调用bool属性值为false,它将破坏我的逻辑。
答案 0 :(得分:4)
你的TrackedValue
课程非常适合。但是,你有点重新发明了轮子。这被称为Maybe<T>
monad。
查看“Maybe”monad,你会得到很多细节,但这里是:
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;
}
}
它非常类似于可空类型,但它不仅限于值类型。
所以你可以拥有这个:
class UpdateRequest
{
public int EmployeeId { get; set; }
public Maybe<string> EmployeeName { get; set; }
public Maybe<decimal> Salary { get; set; }
}
例如,如果您只是尝试更新薪水,那么您可以像这样初始化类:
var request = new UpdateRequest()
{
EmployeeId = 1234,
EmployeeName = Maybe<string>.Nothing,
Salary = new Maybe<decimal>(50000m),
};
或者,您可以像这样定义.ToMaybe()
扩展方法:
public static Maybe<T> ToMaybe<T>(this T value)
{
return new Maybe<T>(value);
}
然后初始化看起来像这样:
var request = new UpdateRequest()
{
EmployeeId = 1234,
EmployeeName = Maybe<string>.Nothing,
Salary = 50000m.ToMaybe(),
};
或者你可以像这样添加一个隐式运算符:
public static implicit operator Maybe<T>(T v)
{
return v.ToMaybe();
}
现在初始化更简单:
var request = new UpdateRequest()
{
EmployeeId = 1234,
EmployeeName = Maybe<string>.Nothing,
Salary = 50000m,
};
然后你应该为类型定义两个“绑定”运算符 - 在linq中也称为SelectMany
- 就像这样:
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()));
}
现在你可以针对可能的值进行LINQ查询。
Maybe<decimal> tax =
from salary in request.Salary
select salary * 0.1m;
然后,如果request.Salary
为Nothing
,那么tax
将为Nothing
。否则将执行计算。
我希望这会有所帮助。
答案 1 :(得分:1)
一个选项是,您可以将功能推送到更新方法,而不是在您的类中使用,而是让用户指定要更新的列。
我在过去实现过这个问题时,当我们在执行大型查询时遇到了perf问题,并且我们正在传递一些大型对象,其中包含不必要的数据(我们有一个很少改变或需要的巨大描述字段)。 / p>
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)
}
我们还围绕此创建了一些易于使用的包装器,它提供了一个排除“庞大”属性的选项,例如:
public bool UpdateObjects(List<UpdateRequest> objects, bool includeDescription)
有一个基于反射设置属性的示例:Setting a property by reflection with a string value