Hi,
Do you have an idea on how to set properties with a private set after construction?
For simplicity I've created a Customer object.
public class Customer
{
public int Id { get; init; }
public string Name { get; init; }
public string Email { get; private set; }
public int Age { get; private set; }
public Customer(int id, string name)
{
Id = id;
Name = name ?? throw new ArgumentNullException(nameof(name));
}
public void SetEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException($"'{nameof(email)}' cannot be null or whitespace.", nameof(email));
}
Email = email;
}
public void SetAge(int age)
{
if (age < 18)
{
throw new ArgumentException(nameof(age));
}
Age = age;
}
}
The Builder. Unable to set the Age
property
internal class CustomerBuilder : Builder<Customer>
{
private readonly Faker _faker = new();
protected override Customer CreateInstance()
{
var customer = new Customer(Get(x => x.Id), Get(x => x.Name));
return customer;
}
public override Builder<Customer> ThatIsValid()
{
With(x => x.Id, 5);
With(x => x.Name, _faker.Person.FullName);
With(x => x.Age, 23); // This doesn't work...
return this;
}
}
What does work is the following:
internal class CustomerBuilder : Builder<Customer>
{
private readonly Faker _faker = new();
protected override Customer CreateInstance()
{
var customer = new Customer(Get(x => x.Id), Get(x => x.Name));
customer.SetAge(Get(x => x.Age));
// if I have more properties I can fill them with other methods here
return customer;
}
public override Builder<Customer> ThatIsValid()
{
With(x => x.Id, 5);
With(x => x.Name, _faker.Person.FullName);
With(x => x.Age, 23); // You must also set it of course
return this;
}
}
But let's say I have strange business logic. For example the SetEmail
can only be called when Age
is 0. So actually not being set yet. I could add all those Setxxxx methods in the CreatInstance method but then they always get values. Then I have to overwrite the ThatIsValid
values with in example Age
is 0
again. Not really ideal...
I know it is strange but think of a registration process. I have a Status
property and I need to set some properties only when the status is a particular value. It is not possible now.
Possible solution
I think the solution is to introduce a Has
method in the builder so you can do this:
internal class CustomerBuilder : Builder<Customer>
{
private readonly Faker _faker = new();
protected override Customer CreateInstance()
{
var customer = new Customer(Get(x => x.Id), Get(x => x.Name));
if (Has(x => x.Age))
{
customer.SetAge(Get(x => x.Age));
}
if (Has(x => x.Email))
{
customer.SetEmail(Get(x => x.Email));
}
return customer;
}
public Builder<Customer> ThatIsValidWithAgeNotSet()
{
With(x => x.Id, 5);
With(x => x.Name, _faker.Person.FullName);
return this;
}
public override Builder<Customer> ThatIsValid()
{
With(x => x.Id, 5);
With(x => x.Name, _faker.Person.FullName);
With(x => x.Age, 23);
With(x => x.Email, _faker.Person.Email);
return this;
}
}