同僚の助けを借りて、ビジネス ルールをエンティティに適用する方法を考え出しました。ルールを定義するコントラクトをエンティティに注入しています。これ自体はうまく機能しますが、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 文字しか許可されません。データベース/モデルの作成中に、初期永続性関連の属性を定義できます。コントラクトのアイデアはとても気に入っていますが、Entity Framework で機能するとは思えません。私が持っていた他の唯一の考えは、エンティティと EF の間にコントラクト (またはビジネス クラス) を配置することでした。私の DAL はエンティティをビジネス ルール クラスに渡し、すべてが問題なければサービスに渡すか、その逆を行います。エンティティに適用されるビジネスルールに関して、誰かがこれよりも優れたアプローチをとることを望んでいます