47

I have the following method:

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    bool isModified = false;

    if (entity.Title != item.Title)
    {
        isModified = true;
        entity.Title = item.Title;
    }

    if (entity.ServerId != item.Id)
    {
        isModified = true;
        entity.ServerId = item.Id;
    }

    return isModified;
}

I wonder if you could suggest a better way to implement the method.

The problem is obvious: 5 lines of almost copy-pasted code per property is too much. May be there's a solution using Func-s / Expression-s out of my vision.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Sergey Metlov
  • 25,747
  • 28
  • 93
  • 153
  • 30
    Even though I like the question and would love to see a good answer, shouldn't this be on codereview? – Dion V. Apr 28 '15 at 07:58
  • 3
    As one of ways, you need some kind of object-object property mapping here, try to see http://www.codeproject.com/Articles/742461/Csharp-Using-Reflection-and-Custom-Attributes-to-M http://stackoverflow.com/questions/17675408/how-to-map-properties-of-two-different-objects http://stackoverflow.com/questions/20410234/if-two-objects-have-same-properties-can-the-values-from-one-be-autoassigned – Ilia Maskov Apr 28 '15 at 08:02
  • number of properties can changed? or it static? – Grundy Apr 28 '15 at 08:04
  • @agent5566 yes, mappers are good but then some kind of mapping configuration will be required because I'd like to map differently named and typed properties. – Sergey Metlov Apr 28 '15 at 08:04
  • @Grundy of course properties are different in different classes. – Sergey Metlov Apr 28 '15 at 08:05
  • 2
    @SergeyMetlov i think no magic can be here, you have to do some kind of mapping (`if` conditions, configs, attributes..) somewere ;) – Ilia Maskov Apr 28 '15 at 08:07
  • 1
    @SergeyMetlov Sometimes, when people desire simplified versions of `if` conditions, the code ends up less readable. – Yuval Itzchakov Apr 28 '15 at 08:10
  • @YuvalItzchakov agree with you. But 5*n lines of code... – Sergey Metlov Apr 28 '15 at 08:11
  • I'm curious why do you need to do this? The word entity tells me it's related to entity framework is it not? – Janne Matikainen Apr 28 '15 at 08:28
  • @JanneMatikainen SQLite.NET – Sergey Metlov Apr 28 '15 at 08:29

15 Answers15

47

You have a case of temporal coupling there, i.e. you're mixing the check whether the entity has changed with the assignments. If you separate the two, your code becomes much cleaner:

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    bool isModified = this.IsEntityModified(entity, item);

    if (isModified)
    {
        this.UpdateEntity(entity, item);
    }

    return isModified;
}

private bool IsEntityModified(Product entity, ProductModel item)
{
    return entity.Title != item.Title || entity.ServerId != item.ServerId;
}

private void UpdateEntity(Product entity, ProductModel item)
{
    entity.Title = item.Title;
    entity.ServerId = item.Id;
}

Doing any smart and funky stuff (TM) with Func<> or anything like that, doesn't seem helpful in this case as it wouldn't transport your intention as clearly.

Falco
  • 3,287
  • 23
  • 26
bstenzel
  • 1,231
  • 9
  • 14
  • 1
    @Falco, I've approved the suggested edit (`if (isModified)`). However, this **is** a micro optimization. I can absolutely relate to making an optimization like that - it kinda hurts the programmer's soul not to - but it does make the code less clear and the benefit is unproven. – bstenzel Apr 28 '15 at 09:41
  • 5
    Using this solution you'll have to perform the maintenance in two separate methods, as opposed to at a few lines that are close together. – CodeCaster Apr 28 '15 at 09:45
  • 3
    It depends... First I think it makes the code more clear: I only update the Object if it is modified! That sounds very intuitive. Second: If there are any side-effects from calling the setters on the Object, they will not be called. If there are some calculated values which are recaluclated on assignments, we skip them. So if I have a view-state with 1000 objects, only one is changed and I call this for all objects, it can make a big performance difference! – Falco Apr 28 '15 at 09:46
  • I can see where you're coming from @Falco. I wouldn't argue about a change like that, but I'd still prefer seeing proof e.g. unit tests showing that (expensive) calls are not made or a db profiler showing that an update statement affects less rows. – bstenzel Apr 28 '15 at 10:06
  • 1
    @CodeCaster, the above code has one method with the high-level concept plus two methods with the low-level details. It's compliant with single responsibility as well as single level of abstraction. It's clean code as far as I am concerned. And in my maintenance experience being clear about your intention beats low level of indirection every day. – bstenzel Apr 28 '15 at 10:12
  • 5
    I'm not sure what you're saying. I'm saying that maintenance (adding/removing properties) will be harder using this code, as property access is spread out over two methods. – CodeCaster Apr 28 '15 at 10:15
  • Yes, easy to screw up things if you separate the logic. – Patrick Hofman Apr 28 '15 at 11:08
  • 5
    I have a hard time grasping the notion of deliberately making code less clear in some abstract sense of maintainability. Clearer code is always more maintainable in my book. And if you really fear to screw changes like the described ones up, you probably don't work test-driven. You should, though. – bstenzel Apr 28 '15 at 11:29
  • @bstenzel unclear code is bad but repetition also are bad. You will repeat the propeties in both methods. Of course tests can ensure that the code is working but you still lost time writing each property two times in different places. That's not dramatic but it should be taken into account. – Guillaume Apr 29 '15 at 09:52
  • The function reminds me of compareAndSet that is often used for atomic operations. But it's result is logical not of that. – Phil Wright Apr 29 '15 at 12:56
  • 3
    @bstenzel You're right that the code is less clear and more brittle. With that said I do think that this is probably the best approach if the intent is to maintain the exact same method behavior. The fact that the code still looks weird after improvement requires going beyond the method. For example, why is an entity being passed to a method with the intention of that method changing its properties? Or why does it matter that the update only occurs if the entity has been changed? There are probably deeper smells. – moarboilerplate Apr 29 '15 at 14:56
  • 1
    @CodeCaster Your argument doesn't really hold any water. If you're adding a property to a class, you'd better well read the entire class to get a sense of how its put together. The property declaration itself may have quite a few lines separating it from these methods. Yes, someone could just add the property and then not modify any of these methods. As it is, it looks like the OPs problem space is already across three classes, and you'd better know what you're doing BEFORE you go adding anything. – Andy Apr 29 '15 at 15:17
  • 2
    @Andy no, you shouldn't have to read an entire DTO class when just adding a single property. My point is: the code was very unmaintainable to begin with, and this approach doesn't quite improve that. All of the examples here are very nice for two or three properties, but a class containing 20 such properties will confuse people who are new to the codebase or who haven't looked at it for a few weeks. Then it's easy to forget to add `|| entity.NewProperty != item.NewProperty` to the `IsEntityModified()` method. Like @moarboilerplate says, the core question to this question is _"Why do this?"_. – CodeCaster Apr 29 '15 at 15:36
  • 1
    @CodeCaster you say you shouldn't have to read the code, then give two very good examples of exactly why you'll need to read the code. Nothing is going to help people that are new or haven't looked at the code in a while except for looking at the code. And lets assume the OP has good reasons for the code he has and help him improve it, which this answer certainly does. – Andy Apr 29 '15 at 22:03
  • I'd liked to see nicer solution but it seems like this one is the best C# may provide. Accept. – Sergey Metlov Apr 30 '15 at 11:34
12

Something like this should work

protected bool ModifyExistingEntity(Person entity, ProductModel item)
{
    bool isModified = CompareAndModify(() => entity.Title = item.Title, () => entity.Title != item.Title);
    isModified |= CompareAndModify(() => entity.ServerId = item.Id, () => entity.ServerId != item.Id);

    return isModified;
}

private bool CompareAndModify(Action setter, Func<bool> comparator)
{
    if (comparator())
    {
        setter();
        return true;
    }
    return false;
}

Not sure if this is readable. It is subjective.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • 15
    This should work. I would definitely not want to see that in my codebase ;P – Yuval Itzchakov Apr 28 '15 at 08:10
  • That makes the logic "if-then-assign" more readable (CompareAndModify) but makes the codes that uses it (ModifyExistingEntity) unreadable. It's better to work on calling code readability by building an implementation that hides complex, boring or things ;) the repetition is still here ! – Guillaume Apr 28 '15 at 10:07
  • 3
    @Guillaume I agree. Readers will double take there. If you are concerned about readability I recommend [bstenzel's answer](http://stackoverflow.com/a/29913912/2530848). I answered just based on OP's this concern "The problem is obvious: 5 lines of almost copy-pasted code per property is too much" – Sriram Sakthivel Apr 28 '15 at 10:10
  • Here is a another version: `static bool CompareAndModify(TProp oldValue, TProp newValue, Action setAction) { if (EqualityComparer.Default.Equals(oldValue, newValue)) { return false; } setAction(newValue); return true; }` It should be called like this: `isModified |= CompareAndModify(entity.ServerId, item.Id, x => entity.ServerId = x);` I only have to type `item.Id` once and `entity.ServerId` twice, in this call. To improve that I would have to use expression trees like in Patrick Hofman's answer. – Jeppe Stig Nielsen Apr 28 '15 at 10:55
  • Yuck.. plus this has the same problem the OP is worried about; lines of mostly copy / pasted code which are all almost identical. – Andy Apr 29 '15 at 15:18
  • @Andy What do you mean? This eliminates the code repetition 5 lines per property to single line. Would you like to make it to zero :\ – Sriram Sakthivel Apr 29 '15 at 15:21
  • @SriramSakthivel less lines, but suffers from the same logical problems. Sometimes less lines is just winding an existing problem into a tighter ball. – moarboilerplate Apr 29 '15 at 17:17
  • @SriramSakthivel it still leaves a lot of noise code that makes it mire difficult to see what's going n. – Andy Apr 29 '15 at 21:55
12

I think an extension of this answer might work for you:

public static bool SetIfModified<CLeft, T>(Expression<Func<CLeft, T>> exprLeft, CLeft leftType, T rightValue)
{
    var getterLeft = exprLeft.Compile();

    if (EqualityComparer<T>.Default.Equals(getterLeft(leftType), rightValue))
    {
        var newValueLeft = Expression.Parameter(exprLeft.Body.Type);
        var assignLeft = Expression.Lambda<Action<CLeft, T>>(Expression.Assign(exprLeft.Body, newValueLeft), exprLeft.Parameters[0], newValueLeft);

        var setterLeft = assignLeft.Compile();

        setterLeft(leftType, rightValue);
        return true;
    }
    else
    {
        return false;
    }
}

It takes an expression to check the value. It compiles and executes it dynamically.

Use it like this:

public class Product { public string Title { get; set; } }
public class ProductModel { public string Title { get; set; } }

static void Main(string[] args)
{
    Product lc = new Product();
    ProductModel rc = new ProductModel();
    rc.Title = "abc";
    bool modified = SetIfModified(l => l.Title, lc, r.Title);

    // modified is true
    // lc.Title is "abc"

}
Community
  • 1
  • 1
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
10

Use T4 for Metaprogramming

Another approach - very often when we have duplicated code that is actually simple and probably very quick. In this case, each duplicated if block is not the same - it holds a little knowledge - the mapping from one property to another.

It is annoying to write and maintain the duplicated blocks.
One way to avoid writing useful repetitive code is to automatically generate it.

With my solution, the mapping is straightforward:

var mappings = new []{
    new Mapper("ProductModel", "Product")
    { 
        "Title",               // ProductModel.Title goes to Product.Title
        {"Id", "ServiceId"},   // ProductModel.Id goes to Product.ServiceId
    },
};

Here is a Text Template (build-in feature to Visual Studio):

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
    // Consider including the namespace in the class names.
    // You only need to change the mappings.
    var product = new Mapper("Product", "ProductEntity") { "Name", {"Id", "ServiceId"} };
    var person = new Mapper("Person", "DbPerson") { "Employee", {"Name", "FullName"}, {"Addredd", "HomeAddress"} };

    var mappings = new [] {product, person};
#>
// !!!
// !!!  Do not modify this file, it is automatically generated. Change the .tt file instead.     !!!
// !!!
namespace Your.Mapper
{
    partial class Mapper
    {
        <# foreach(var mapping in mappings) { 
        #>/// <summary>
        /// Set <paramref name="target"/> properties by copying them from <paramref name="source"/>.
        /// </summary>
        /// <remarks>Mapping:<br/>
        <#foreach(var property in mapping){
        #>/// <see cref="<#=mapping.SourceType#>.<#=property.SourceProperty#>"/> → <see cref="<#=mapping.TargetType#>.<#=property.TargetProperty#>"/> <br/>
        <#}
        #>/// </remarks>
        /// <returns><c>true</c> if any property was changed, <c>false</c> if all properties were the same.</returns>
        public bool ModifyExistingEntity(<#=mapping.SourceType#> source, <#=mapping.TargetType#> target)
        {
            bool dirty = false;
            <# foreach(var property in mapping) {
            #>if (target.<#=property.TargetProperty#> != source.<#=property.SourceProperty#>)
            {
                dirty = true;
                target.<#=property.TargetProperty#> = source.<#=property.SourceProperty#>;
            }           
            <#}
            #>return dirty;
        }
        <#
         } 
        #>

    }
}

<#+
class Mapper : IEnumerable<PropertyMapper>
{
    private readonly List<PropertyMapper> _properties;

    public Mapper(string sourceType, string targetType)
    {
        SourceType = sourceType;
        TargetType = targetType;
        _properties = new List<PropertyMapper>();
    }

    public string SourceType { get; set; }
    public string TargetType { get; set; }

    public void Add(string fieldName)
    {
        _properties.Add(new PropertyMapper {SourceProperty = fieldName, TargetProperty = fieldName});
    }

    public void Add(string sourceProperty, string targetProperty)
    {
        _properties.Add(new PropertyMapper { SourceProperty = sourceProperty, TargetProperty = targetProperty });
    }

    IEnumerator<PropertyMapper> IEnumerable<PropertyMapper>.GetEnumerator() { return _properties.GetEnumerator(); }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _properties.GetEnumerator(); }
}

class PropertyMapper
{
    public string SourceProperty { get; set; }
    public string TargetProperty { get; set; }
}
#>

This template generates the following code: https://gist.github.com/kobi/d52dd1ff27541acaae10

Advantages:

  • Heavy lifting is done at compile time (actually once before compile time) - the generated code is quick.
  • Generated code is documented.
  • Easy to maintain - you can change all mappers in a single point.
  • Generated methods are documented.
  • No copy-paste bugs.
  • This is pretty fun.

Disadvantages:

  • Use of strings to get property names. Keep in mind - this isn't production code, it is just used to generate code. It is possible to use the real types and Expression trees (an example below).
  • Static analysis will probably miss the usage in the template (even if we use Expressions, not all tools look into tt files).
  • Many people don't know what is going on.
  • If you are using Expressions, it is tricky to reference your types.

Notes:

  • I've named the arguments source and target, and changed their order so source is always first.

There has been some concern that I'm using strings instead of the real properties. Although this is a minor problem in this case (the output is compiled), here is an addition that works with your real objects.

At the top, add this (3rd one should be your namespace):

<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="System.Linq.Expressions" #>
<#@ import namespace="ConsoleApplicationT4So29913514" #>  

At the bottom, add:

class Mapper<TSource, TTarget> : Mapper
{
    public Mapper()
        : base(typeof(TSource).FullName, typeof(TTarget).FullName)
    {

    }

    private static string GetExpressionMemberAccess(LambdaExpression getProperty)
    {
        var member = (MemberExpression)getProperty.Body;
        //var lambdaParameterName = (ParameterExpression)member.Expression;
        var lambdaParameterName = getProperty.Parameters[0]; // `x` in `x => x.PropertyName`
        var labmdaBody = member.ToString();
        //will not work with indexer.
        return labmdaBody.Substring(lambdaParameterName.Name.Length + 1); //+1 to remove the `.`, get "PropertyName"
    }

    public void Add<TProperty>(Expression<Func<TSource, TProperty>> getSourceProperty, Expression<Func<TTarget, TProperty>> getTargetProperty)
    {
        Add(GetExpressionMemberAccess(getSourceProperty), GetExpressionMemberAccess(getTargetProperty));
    }

    /// <summary>
    /// The doesn't really make sense, but we assume we have <c>source=>source.Property</c>, <c>target=>target.Property</c>
    /// </summary>
    public void Add<TProperty>(Expression<Func<TSource, TProperty>> getProperty)
    {
        Add(GetExpressionMemberAccess(getProperty));
    }
}

Usage:

var mappings = new Mapper[] {
    new Mapper<Student,StudentRecord>
    {
        {s=>s.Title, t=>t.EntityTitle},
        {s=>s.StudentId, t=>t.Id},
        s=>s.Name,
        s=>s.LuckyNumber,
    },
    new Mapper<Car,RaceCar>
    {
        c=>c.Color,
        c=>c.Driver,
        {c=>c.Driver.Length, r=>r.DriverNameDisplayWidth},
    },
};

The whole file should look like this: https://gist.github.com/kobi/6423eaa13cca238447a8
Output still looks the same: https://gist.github.com/kobi/3508e9f5522a13e1b66b

Notes:

  • Expressions are only used to get the property name as a string, we are not compiling them or running them.
  • In C# 6 we will have the nameof() operator, which is a nice compromise between Expressions and no-magic-strings.
Community
  • 1
  • 1
Kobi
  • 135,331
  • 41
  • 252
  • 292
  • 1
    I like your approach. We do something similar using XSLT, which works like a charm. – Patrick Hofman Apr 28 '15 at 11:45
  • 1
    I feel that Data Mapper is the best approach here too, but a t4 template ties it too much to string magic. I think something like AutoMapper is a better fit IMO. – moarboilerplate Apr 29 '15 at 02:19
  • @Moarboilerplate - That's a good point, I referred to that a little under "Disadvantages". The strings are here for simplicity, but there are alternatives. I have a half-working version that uses generics and expressions. In C# 6 we will also have the [`nameof` operator](http://stackoverflow.com/q/26573102/7586), which makes working with strings less painful. – Kobi Apr 29 '15 at 06:09
  • 1
    @moarboilerplate - I went a too far and added templates that use expressions. – Kobi Apr 29 '15 at 18:08
7

There is no magic wand to simplify this.

You could have the entity itself provide an IsModified property, which is then set by the property setters, such as:

public string Title {
   get { return _title; }
   set {
         if (value != _title)
         {
             _title = value;
             IsModified = true;
         }
    }
}

If that is too much work, your solution is fine.

Roy Dictus
  • 32,551
  • 8
  • 60
  • 76
  • 5
    If you have to modify the entity, you're probably better off implementing INotifyPropertyChanged, then handling that event and setting isModified=true when a property change occurs. – Teevus Apr 28 '15 at 08:12
  • That is an option, but that is more work. I believe the point of the question was to do this with as little work as possible. – Roy Dictus Apr 28 '15 at 08:53
7

If you want to make it readable, you can create a class for this purpose with really simple usage avoiding repetitive code :

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    return new Modifier<Product>(entity)
               .SetIfNeeded(e => e.Title, item.Title);
               .SetIfNeeded(e => e.ServerId, item.Id);
               .EntityWasModified;
}

Implementation:

I took some code from Patrick Hofman to generate a setter from the getter expression.

public class Modifier<TEntity>
{    
    public Modifier(TEntity entity)
    {
        Entity = entity;
    }

    public TEntity Entity { get; private set; }

    public bool EntityWasModified { get; private set; }

    public Modifier<TEntity> SetIfNeeded<TProperty>(Expression<Func<TEntity, TProperty>> entityPropertyGetter, TProperty modelValue)
    {
        var getter = entityPropertyGetter.Compile();
        var setter = GetSetterExpression(entityPropertyGetter).Compile();

        if (!object.Equals(getter(Entity), modelValue))
        {
            setter(Entity, modelValue);
            EntityWasModified = true;
        }
        return this;
    }

    private static Expression<Action<TEntity, TProperty>> GetSetterExpression(Expression<Func<TEntity, TProperty>> getterExpression)
    {
        var newValue = Expression.Parameter(getterExpression.Body.Type);

        return Expression.Lambda<Action<TEntity, TProperty>>(
            Expression.Assign(getterExpression.Body, newValue),
            getterExpression.Parameters[0], newValue);
    }
}

You may want to cache the result of .Compile to improve performance.

Guillaume
  • 12,824
  • 3
  • 40
  • 48
4

Check out my own (almost as weird as the others) solution

[TestMethod]
public void DifferentTitleAndId_ExpectModified()
{
    var entity = new Product
        {
            Id = 0,
            ServerId = 0,
            Title = "entity title"
        };

    var model = new ProductModel
        {
            Id = 1,
            Title = "model title"
        };

    bool isModified = ModifyExistingEntity(entity, model);

    Assert.IsTrue(isModified);
}

protected bool ModifyExistingEntity(Product entity, ProductModel model)
{
    return
        IsModified(entity.Title, model.Title, x => entity.Title = x) |
        IsModified(entity.ServerId, model.Id, x => entity.ServerId = x);
}

protected bool IsModified<T>(T value1, T value2, Action<T> setter)
{
    return IsModified(() => value1, () => value2, () => setter(value2));
}

protected bool IsModified<T>(Func<T> valueGetter1, Func<T> valueGetter2, Action setter)
{
    if (!Equals(valueGetter1(), valueGetter2()))
    {
        setter();
        return true;
    }

    return false;
}
Sergey Metlov
  • 25,747
  • 28
  • 93
  • 153
4

I have seen the most intricate answers to this question, but I think you would be best suited with a fairly straightforward, no-nonsense simple solution.

I would assume you are using some kind of Data Mapper pattern in your codebase and Product is your DAL/Domain entity and ProductModel is your app-level object. In that case I would simply have a method that compares the two (which can later be moved to a separate layer), and if they aren't equal, map.

But this raises the question, why are you worried about only updating if it's changed? It is probably acceptable to simply update every time.

Also, you should probably not be passing in an entity to a method with the expectation that it gets updated.

I would change the logic as follows:

protected bool UpdateIfChanged(Product entity, ProductModel item)
{
    var areEqual = CompareProductAndProductModel(entity, item);

    if(!areEqual)
        UpdateProduct(MapProductModelToProduct(item));

    return !areEqual;
}

internal bool CompareProductAndProductModel(Product product, ProductModel productModel)
{
    return product.Title == productModel.Title && product.ServerId == productModel.Id; //could be abstracted to an equality comparer if you were inclined
}

The biggest departure that this answer makes from other answers is that it does not modify the Product entity. Instead, it compares the Product and ProductModel, but if changes are detected, it then uses the ProductModel to create a new Product, which is then passed to another DAL method that actually does the updating work. I believe this is probably the most maintainable approach as you do not have to deal with methods that change the state of objects passed in to them (implicit coupling even if the method and caller exist in different places), which means you do not have to mentally track state changes to the entity as you step through code during debugging.

moarboilerplate
  • 1,633
  • 9
  • 23
2

"Better" is subjective in this context. Since you were complaining about line count, I have a more concise way:

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    bool isModified = false;

    isModified |= (entity.Title!= item.Title) ? (entity.Title = item.Title) == item.Title : false;
    isModified |= (entity.ServerId != item.Id) ? (entity.ServerId = item.Id) == item.Id : false;

    return isModified;
}
Tremmors
  • 2,906
  • 17
  • 13
  • You can make it shorter with boolean operator &&: `isModified |= (entity.Title != item.Title) && set( entity.Title = item.Title );` Where set is a helper Function which takes one argument and returns true. – Falco Apr 28 '15 at 09:36
  • @Falco I agree we can optimize for total character count that way, but not for total number of lines of code; that was one of the key complaints. The only way I see to reduce total line count would be to combine this all into a single return statement such as: return ((entity.Title!= item.Title) ? (entity.Title = item.Title) == item.Title : false) || ((entity.ServerId != item.Id) ? (entity.ServerId = item.Id) == item.Id : false); – Tremmors Apr 28 '15 at 09:44
  • What I want to optimize for and what the OP wanted most, was not so much copy&paste. The more often I have to write the variable-names on each line, the more prone it is to error, if I copy&paste the line and forget to change one of the 5 places in which I have to enter the right attribute-name. The best solution would be one, where the two attribute-names only appear once in each line... – Falco Apr 28 '15 at 09:48
2

Continueing on @bstenzel answer, shouldn't this also do the trick?

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    bool isEntityModified = entity.Title != item.Title || entity.ServerId != item.ServerId;
    entity.Title = item.Title;
    entity.ServerId = item.Id;
    return isEntityModified;
}

Clean and simple.

Jelle de Fries
  • 885
  • 1
  • 11
  • 20
2

I think you are looking for a way to compare the content of properties of two objects. Your sample contains two properties but I expect your real code contains a lot more (as a Product entity does probably have many properties).

You should start by writing a method that compares your two objects. For your references here are some SO questions regarding the matter:

  1. Properly implement comparison of two objects with different type but semantically equivalent
  2. Comparing object properties in c#

You method would look like:

public static bool IsEqualTo<TSource>(this TSource sourceObj, TDestination destinationObj)
    where TSource : class
    where TDestination : class
{
    // Your comparison code goes here
}

Then you will have to write a second method to copy data between your objects. This questions can guide you through (check Marc answer):

  1. How to deep copy between objects of different types in C#.NET

You method would look like:

public static bool CopyDataTo<TSource>(this TSource sourceObj, TDestination destinationObj)
    where TSource : class
    where TDestination : class
{
    // Your data copy code goes here
}

You final code will look as simple as

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    if (!entity.IsEqualTo(item))
    {
        item.CopyDataTo(entity);
        return true;
    }

    return false;
}
Community
  • 1
  • 1
Moslem Ben Dhaou
  • 6,897
  • 8
  • 62
  • 93
1

I'm not sure if the Product class is extensible for you, nor am I certain if you're looking for a "fancy" answer or just a simpler one...but if so, you can things around a bit and put the logic in the Product class itself; imho you end up with a fairly readable method:

protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
    entity.SetTitle(item.Title);
    entity.SetServerId(item.Id);
    return entity.WasModified();
}

The added bonus is that you neatly encapsulate the behavior into Product (as well as validation, etc.)

public partial class Product
{
    public void SetTitle(string title)
    {
       if(this.Title!=title) //and other validation, etc
       {
         this.Title = title;
         Modified();
       }
    }

    public void SetServerId(int serverId)
    {
       if(this.ServerId!=serverId)
       {
          this.ServerId=serverID;
          Modified();
        }
    }

    private bool _wasModified;

    private void Modified()
    {
        //Or implement INotifyPropertyChanged if you like
        _wasModified=true;
    }

    public bool WasModified()
    {
        return _wasModified;
    }
}

Of course if you don't need any "business logic" and this really is just an unchecked mapping operation, any one of the very clever answers here will do :)

Stephen Byrne
  • 7,400
  • 1
  • 31
  • 51
0

Since you most likely do not want to write the code for every entity and model pair, you should rely on reflection to map the properties which are the same between the entity and model and then just compare those values with the property Infos.

Edit: Added copying of the modified value and locking for multiple threads.

class Program
{
    static void Main(string[] args)
    {
        if (ModifyExistingEntity(new Product { Name = "bar" }, new ProductModel { Name = "test" }))
            Console.WriteLine("Product modified");

        if (ModifyExistingEntity(new Customer { Number = 1001 }, new CustomerModel { Number = 1002 }))
            Console.WriteLine("Customer was modified");

        if (!ModifyExistingEntity(new Customer { Number = 1001 }, new CustomerModel { Number = 1001 }))
            Console.WriteLine("Customer was not modified");

        Console.ReadKey();
    }

    protected static bool ModifyExistingEntity<TEntity, TModel>(TEntity entity, TModel model)
    {
        var isModified = false;

        GetProperties(entity, model).ForEach(
                propertyInfo =>
                {
                    var item2Value = propertyInfo.Item2.GetValue(model, null);
                    if (Equals(propertyInfo.Item1.GetValue(entity, null), item2Value)) 
                        return;

                    propertyInfo.Item1.SetValue(entity, item2Value);
                    isModified = true;
                });

        return isModified;
    }

    private static readonly object MappingLock = new object();
    private static readonly Dictionary<Tuple<Type, Type>, List<Tuple<PropertyInfo, PropertyInfo>>> Mapping =
            new Dictionary<Tuple<Type, Type>, List<Tuple<PropertyInfo, PropertyInfo>>>();

    protected static List<Tuple<PropertyInfo, PropertyInfo>> GetProperties<TEntity, TModel>(TEntity entity, TModel model)
    {
        lock (MappingLock)
        {
            var key = new Tuple<Type, Type>(typeof (TEntity), typeof (TModel));
            if (Mapping.ContainsKey(key))
            {
                return Mapping[key];
            }

            var modelProperties = typeof (TModel).GetProperties();

            var newMapping = (from propertyInfo in typeof (TEntity).GetProperties()
                let modelPropertyInfo = modelProperties.SingleOrDefault(mp => mp.Name == propertyInfo.Name)
                select new Tuple<PropertyInfo, PropertyInfo>(propertyInfo, modelPropertyInfo))
                .ToList();

            Mapping.Add(key, newMapping);

            return newMapping;
        }
    }
}
Janne Matikainen
  • 5,061
  • 15
  • 21
  • The code is nice as an exercise! But - the OP's code doesn't compare objects: it **sets their properties** and returns a dirty bit. Also, the mapping is not one-to-one on properties, the OP has `ServerId` to `Id`, and even worse, there are probably other properties that should not be mapped. I'd also note `Mapping.Add` might throw an exception if used from multiple threads. – Kobi Apr 28 '15 at 09:34
  • Noted, so added copying of the values for modified properties. Didn't notice the property name mismatch. Guess OP could write the mapping himself them for objects that are not compatible per say. – Janne Matikainen Apr 28 '15 at 10:02
0

If you just want shorter code-lines you can compact it to this C-Style assignments:

ModifyExistingEntity( Product entity, ProductModel item )
{
    bool isModified = false;

    isModified |= ( entity.Title != item.Title )
        && retTrue( entity.Title =  item.Title );

    isModified |= ( entity.ServerId != item.Id )
        && retTrue( entity.ServerId =  item.Id );

    return isModified;
}

static bool RetTrue<T>(T dummy) { return true; } // Helper method.
Falco
  • 3,287
  • 23
  • 26
  • The idea is, with this symmetric assignment it is hard to miss changing one of the attributes on copy&paste, since it will immediately look asymmetric – Falco Apr 28 '15 at 09:49
  • The helper method is missing its return type (`bool`). I think it should be a generic method to avoid boxing value types into `object` boxes. So it becomes: `static bool RetTrue(T dummy) { return true; }` – Jeppe Stig Nielsen Apr 28 '15 at 09:52
0

This is a situation where macro use is appropriate:

#define CheckAndAssign(dst,src) (dst != src && (dst = src, true))

return (  CheckAndAssign (entity.Title, item.Title)
        | CheckAndAssign (entity.ServerId, item.Id));

#undef CheckAndAssign

Well, as long as the language is C, C++, Objective-C or anything else with macros. I'd hope that in C++ you can turn it into a template somehow.

gnasher729
  • 51,477
  • 5
  • 75
  • 98