是否有更有效的方法来合并模型而不是手工检查每个属性?

时间:2017-08-17 04:46:54

标签: c#

我正在构建一个API,我只想在调用者将这些属性包含在HTTP调用的主体中时,为某些属性发送更新。

我目前正在做以下......但它看起来很笨重。有更简单的方法吗?

    public void MergePerson(Person basePerson, Person updatedPerson)
    {
        if (updatedPerson.Title != null)
            basePerson.Title = updatedPerson.Title;

        if (updatedPerson.Initials != null)
            basePerson.Initials = updatedPerson.Initials;

        if (updatedPerson.FirstName != null)
            basePerson.FirstName = updatedPerson.FirstName;

        if (updatedPerson.LastName != null)
            basePerson.LastName = updatedPerson.LastName;

        if (updatedPerson.Sex != null)
            basePerson.Sex = updatedPerson.Sex;

        if (updatedPerson.DateOfBirth != null)
            basePerson.DateOfBirth = updatedPerson.DateOfBirth;

        if (updatedPerson.JobTitle != null)
            basePerson.JobTitle = updatedPerson.JobTitle;

        <<< snip >>>

由于这个原因......我不得不将像bool这样的东西设置为不可为空,这样我就可以确认它们实际上是传递的,而不是设置为默认的false。

我担心的是有一天我会在模型中添加新字段而忘记在这里添加它们。

2 个答案:

答案 0 :(得分:2)

您要做的事情称为修补程序

检查此代码,

public class Car 
{

    public int Id { get; set; }

    [Required]
    [StringLength(20)]
    public string Make { get; set; }

    [Required]
    [StringLength(20)]
    public string Model { get; set; }

    public int Year { get; set; }

    [Range(0, 500000)]
    public float Price { get; set; }
}

public class CarPatch
{
    [StringLength(20)]
    public string Make { get; set; }

    [StringLength(20)]
    public string Model { get; set; }

    public int? Year { get; set; }

    [Range(0, 500000)]
    public float? Price { get; set; }
}   

public class CarsController : ApiController
{
    private readonly CarsContext _carsCtx = new CarsContext();

    // PATCH /api/cars/{id}
    public Car PatchCar(int id, CarPatch car)
    {
    var carTuple = _carsCtx.GetSingle(id);
    if (!carTuple.Item1)
    {
        var response = Request.CreateResponse(HttpStatusCode.NotFound);
        throw new HttpResponseException(response);
    }

    Patch<CarPatch, Car>(car, carTuple.Item2);

    // Not required but better to put here to simulate the external storage.
    if (!_carsCtx.TryUpdate(carTuple.Item2))
    {
        var response = Request.CreateResponse(HttpStatusCode.NotFound);
        throw new HttpResponseException(response);
    }

    return carTuple.Item2;
}

// private helpers
private static ConcurrentDictionary<Type, PropertyInfo[]> TypePropertiesCache = 
    new ConcurrentDictionary<Type, PropertyInfo[]>();

private void Patch<TPatch, TEntity>(TPatch patch, TEntity entity)
    where TPatch : class
    where TEntity : class
{
    PropertyInfo[] properties = TypePropertiesCache.GetOrAdd(
        patch.GetType(), 
        (type) => type.GetProperties(BindingFlags.Instance | BindingFlags.Public));

    foreach (PropertyInfo prop in properties)
    {
        PropertyInfo orjProp = entity.GetType().GetProperty(prop.Name);
        object value = prop.GetValue(patch);
        if (value != null)
        {
            orjProp.SetValue(entity, value);
        }
    }
}
}

source

答案 1 :(得分:0)

您可以使用反射轻松完成此操作:

public static void MapPersons(object basePerson, object updatedPerson)
{
    var sourceT = basePerson.GetType();
    var sourceProps = sourceT.GetProperties();

    var targetT = updatedPerson.GetType();
    var targetProps = targetT.GetProperties();

    foreach (var sProp in sourceProps)
    {
        var sourceValue = sProp.GetValue(basePerson, null);
        var propIndex = Array.IndexOf(targetProps, sProp);
        if (propIndex != -1)
        {
            var tProp = targetProps[propIndex];
            tProp.SetValue(updatedPerson, sourceValue);
        }
    }
}

但是这里有很多强大的工具,比如Automapper。他们会为你处理。

相关问题