With the help of a colleague we have come up with a way to apply business rules to an Entity. We are injecting the entity with a contract defining the rules. While this works great on its own, Entity Framework does not like it. Reason being is that we introduced a parameter into the base entity’s constructor. EF does not like parameterized constructors, for good reason. Here is some code that demonstrates the contract:
/// <summary>
/// Class to define the Base Entity that will be inherited by All Entities with an Id of Guid
/// </summary>
public abstract class BaseEntity
{
protected BaseEntity(IEntityContract entityContract)
{
if(!entityContract.IsValid())
{
throw new EntityContractException();
}
DateCreated = DateTime.Now;
DateModified = DateTime.Now;
}
/// <summary>
/// Key Field for all entities
/// </summary>
///
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
/// <summary>
/// Date entity was created
/// </summary>
public DateTime DateCreated { get; set; }
/// <summary>
/// Last date Modified
/// </summary>
public DateTime DateModified { get; set; }
/// <summary>
/// keep track of Row Version used for concurrency
/// </summary>
[Timestamp]
public Byte[] RowVersion { get; set; }
}
public class Address : BaseEntity
{
public Address(IEntityContract entityContract)
: base(entityContract)
{
}
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public Guid RegionId { get; set; }
public virtual Region Region { get; set; }
}
public abstract class BaseEntityContract<TEntity> : Interfaces.IEntityContract where TEntity : BaseEntity
{
private readonly List<string> _errors = new List<string>();
private readonly List<BusinessRule<TEntity>> _businessRules = new List<BusinessRule<TEntity>>();
public bool IsValid()
{
var contractIsvalid = true;
foreach (var rule in _businessRules.Where(br => !br.Rule((TEntity)(Interfaces.IEntityContract)this)))
{
_errors.Add(rule.Description);
contractIsvalid = false;
}
return contractIsvalid;
}
/// <summary>
/// Adds a business rule to be used in validating the {TEntity} contract.
/// </summary>
/// <param name="rule">The function used to represent the business rule.</param>
/// <param name="description">The descriptive message describing the rule.</param>
protected void RegisterBusinessRule(Func<TEntity, bool> rule, string description)
{
_businessRules.Add(new BusinessRule<TEntity>(rule, description));
}
}
public class AddressEntityContract : BaseEntityContract<Address>
{
public AddressEntityContract()
{
this.RegisterBusinessRule(a => a.Line1.length > 0, "Line 1 cannot be empty.");
}
}
I know you can apply some of the validation directly to the properties of an entity and through the fluent API, but this seems more persistence related than business related to me. For example, on the data side, let’s assume a description column can be 255 characters long, but the business needs only allows 100 characters. We can define the initial persistence related attributes during the creation of the database / model. I really like the idea of a contract, but don’t see it working with Entity Framework. The only other thought I had was to have the contracts (or Business Class) sit in-between the entity and EF. My DAL would pass the entities into the Business Rules class and if everything is ok pass them to my service or vice versa . I am hoping someone has a better approach than this with regards to business rules that apply to an entity
I had a couple thoughts about this last night.
One option is to handle the rules inside DTOs rather than the actual domain entities. This assumes that the domain entities are always valid either coming from the application (via the DTO’s) or constructed by Entity Framework (from the data store).
The other option is to consider the contracts as the entities and pass them into Entity Framework and then inject the contracts into the domain.