asiftasleem / nbuilder

Automatically exported from code.google.com/p/nbuilder
0 stars 0 forks source link

Cannot assign unique values to private set/readonly properties #81

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?

var persons = Builder<Person>
             .CreateListOfSize(10)
             .All()
             .With(x => x.PublicId = Guid.NewGuid())
             .With(x => x.PrivateId, Guid.NewGuid())
             .Build();

public class Person
{
   public Guid PublicId { get; set; }
   public Guid PrivateId { get; private set; }
}

What is the expected output? What do you see instead?
When you run the above code, you will notice that PublicId is assigned a unique 
Guid while PrivateId is assigned the same Guid throughout the list.

This works for PublicId because the value is assigned by a delegate and 
Guid.NewGuid() is called for each item in the list where as it is only called 
once for PrivateId which is passed in as a value.

I think we should look into adding an additional override that allows the 
private property to be set via a delegate.

I'm imagining it would allow this:

var persons = Builder<Person>
                .CreateListOfSize(10)
                .All()
                .With(i => i.PublicId = Guid.NewGuid())
                .With(n => n.PrivateId, () => { return Guid.NewGuid(); })
                .Build();

Or even this:

var persons = Builder<Person>
                .CreateListOfSize(10)
                .All()
                .With(i => i.PublicId = Guid.NewGuid())
                .With(n => n.PrivateId, Guid.NewGuid)
                .Build();

I've played around with implementing this and I think the following extension 
methods should be able to accomplish what we are after.

The following needs to be added to OperableExtensions:

public static IOperable<T> With<T, TProperty>(this IOperable<T> operable, 
Expression<Func<T, TProperty>> property, Func<TProperty> value)
        {
            var declaration = GetDeclaration(operable);
            declaration.ObjectBuilder.With(property, value);
            return (IOperable<T>)declaration;
        }

The following needs to be added to SingleObjectBuilderExtensions:

public static ISingleObjectBuilder<T> With<T, TProperty>(this 
ISingleObjectBuilder<T> objectBuilder, Expression<Func<T, TProperty>> property, 
Func<TProperty> value)
        {
            var member = ((MemberExpression)property.Body).Member;

            if (member is FieldInfo)
            {
                return objectBuilder.Do(load => ((FieldInfo)member).SetValue(load, value.Invoke()));
            }
            else
            {
                return objectBuilder.Do(load => ((PropertyInfo)member).SetValue(load, value.Invoke(), null));
            }
        }

I'll need to look into this further and unit test it, but so far it seems to do 
what I'm after... and of course we'll have to remember to provide the 
appropriate "And" overrides...

Please use labels and text to provide additional information.
This issue is present in NBuilder versions 3.0 to 3.0.1.1 (current version)

Original issue reported on code.google.com by joshuajr...@gmail.com on 2 Dec 2011 at 5:15