在一位同事的帮助下,我们想出了一种将业务规则应用于实体的方法。我们正在向实体注入定义规则的合同。虽然这本身很好用,但 Entity Framework 并不喜欢它。原因是我们在基础实体的构造函数中引入了一个参数。EF 不喜欢参数化构造函数,这是有充分理由的。下面是一些演示合约的代码:
/// <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.");
}
}
我知道您可以通过流畅的 API 将一些验证直接应用于实体的属性,但这似乎与持久性相关而不是与我相关的业务。例如,在数据方面,我们假设描述列可以是 255 个字符,但业务需要只允许 100 个字符。我们可以在创建数据库/模型的过程中定义初始持久性相关的属性。我真的很喜欢合同的想法,但看不到它与实体框架一起使用。我唯一的另一个想法是让合同(或商务舱)位于实体和 EF 之间。我的 DAL 会将实体传递给业务规则类,如果一切正常,则将它们传递给我的服务,反之亦然。我希望有人在适用于实体的业务规则方面有比这更好的方法