关于将业务规则契约应用于实体框架中的实体的建议

发布于 2024-11-07 05:26:06 字数 3554 浏览 4 评论 0原文

在同事的帮助下,我们想出了一种将业务规则应用于实体的方法。我们正在向该实体注入一份定义规则的合同。虽然这本身效果很好,但实体框架不喜欢它。原因是我们在基本实体的构造函数中引入了一个参数。 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.");
        }
    }

我知道您可以通过 Fluent API 将一些验证直接应用于实体的属性,但这似乎与与我相关的业务比持久性更相关。例如,在数据方面,假设描述列可以是255个字符长,但业务需求只允许100个字符。我们可以在创建数据库/模型期间定义初始持久性相关属性。 我真的很喜欢契约的想法,但不认为它可以与实体框架一起使用。我唯一的另一个想法是让合同(或商务舱)位于实体和 EF 之间。我的 DAL 会将实体传递到业务规则类中,如果一切正常,则将它们传递给我的服务,反之亦然。我希望有人在适用于实体的业务规则方面有比这更好的方法

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

长亭外,古道边 2024-11-14 05:26:06

昨晚我对此有一些想法。

一种选择是处理 DTO 内部的规则而不是实际的域实体。这假设域实体始终有效,无论是来自应用程序(通过 DTO)还是由实体框架(来自数据存储)构建。

另一种选择是将合同视为实体并将其传递到实体框架中,然后将合同注入到域中。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文