HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Constructing a Data Contract interface that can be used with Entity Framework 4.1 to ensure entity validity

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
cancontractwithusedensureinterfacevalidityconstructingthatdata

Problem

I need some thoughts about an approach a colleague and I am are taking with Entity Framework. Basically, the entities are represented by contracts. These contracts hold a collection of business rules so when they are finally persisted we know they are valid. We can also check the validity from Entity Framework, but that is not as important. Since EF requires you not to have a parameterized constructor we do not pass the actual domain entity into EF, but rather the contract that represents it.

So how this works is, first a context is established. Next, we create a contract and then create the domain entity by passing it the contract. Inside the constructor of the domain entity a call to an IsValid method is made. This runs through all the business rules. If any one fails, the domain entity is considered invalid and an exception of some kind is thrown. If it is valid, we then add the contract to the context and persist the changes and pass back the domain entity. Here is some code that demonstrates the idea:

```
///
/// Interface the all Contracts will impelement
///
public interface IEntityContract
{
bool IsValid();
}

///
/// Business Rule Loigc
///
public class BusinessRule where TEntity: IEntityContract
{
//Logic to handle business rules
}

///
/// Abstract class that represents a Base Contract
///
///
public abstract class EntityContract: IEntityContract where TEntity: IEntityContract
{

private readonly List> _businessRules = new List>();

public bool IsValid()
{
var entityIsValid = true;
foreach (var businessRule in _businessRules)
{
//IF any rule fails the entityIsValid = fale
}

return entityIsValid;
}

protected void RegisterBusinessRule(BusinessRule rule)
{
_businessRules.Add(rule);
}
}

public interface IUserContrac

Solution

Considering Entity Framework does not like parameterized constructors, I think the only solution is when the entity is attached to the context or on SaveChanges we evaluate the the entity.

Perhaps instead of evaluating the entity directly in the context we do it in a class that employs the repository pattern. Now, this code is obviously not complete and we would want to leverage a Unit Of Work that abstracts the context, possibly. Here is the refactored code

public class User
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }

    public interface IRepository where TEntity : class
    {
        void Create(TEntity entity);
        void Update(TEntity entity);
        void Delete(TEntity entity);
    }

    public class Repository : IRepository where TEntity : class
    {
        private readonly IEntityContract _contract;
        private readonly DbContext _context;

        public Repository(IEntityContract contract, DbContext context)
        {
            _contract = contract;
            _context = context;
        }

        public void Create(TEntity entity)
        {
            //beofre we try to create it, lets evaluate it
            if (!_contract.IsValid(entity))
            {
                //possibly throw an exception or let the client know there was an issue.
            }

            //Now implement your create code.
            _context.Add(entity);
            _context.SaveChanges();
        }

        public void Update(TEntity entity)
        {
            //beofre we try to update it, lets evaluate it
            if (!_contract.IsValid(entity))
            {
                //possibly throw an exception or let the client know there was an issue.
            }

            //Now implement your update code.
            _context.Attach(entity);
            _context.SaveChanges();
        }

        public void Delete(TEntity entity)
        {
           //delete code
        }
    }

    /// 
    /// Interface the all Contracts will impelement
    /// 
    public interface IEntityContract where TEntity: class 
    {
        bool IsValid(TEntity entity);
        void RegisterBusinessRule(BusinessRule rule);
        IList> Rules { get; set; }
    }

    /// 
    /// Business Rule Loigc
    /// 
    public class BusinessRule where TEntity : class
    {
        //Logic to handle business rules
    }

    /// 
    /// Abstract class that represents a Base Contract
    /// 
    /// 
    public abstract class EntityContract : IEntityContract where TEntity : class
    {

        protected EntityContract()
        {
            Rules = new List>();
        }

        protected EntityContract(IList> rules)
        {
            Rules = rules;
        }

        public virtual bool IsValid(TEntity entity)
        {
            var entityIsValid = true;
            foreach (var businessRule in Rules)
            {
                //IF any rule fails the entityIsValid = false
            }

            return entityIsValid;
        }

        public IList> Rules { get; set; }

        public void RegisterBusinessRule(BusinessRule rule)
        {
            Rules.Add(rule);
        }

    }

    public interface IUserContract : IEntityContract
    {

    }

    public class UserContract : EntityContract, IUserContract
    {

    }

    //Domonstration of idea
    public class TestClass
    {
        public void TestUserEntity()
        {

            //Using Entity Framework 4.1 we instantiate a DbContext
            var dataContext = new SomeEntityFrameworkDbContext();

            var respository = new Repository(new UserContract(), dataContext);
            //Create the UserContract

            var user = new User()
            {
                Password = "123456",
                Username = "me@domain.com"
            };

            try
            {
                respository.Create(user);
            }
            catch (Exception ex)
            {

               //lets assume an excpetion gets thrown if the IsVlaid fails.
            }

        }
    }


As you can see, now we take the responsibility of validating the entity away from the entity itself. Which, I feel is better to keep the entity as clean as possible and leave any logic out of the entity. This is how I have been feeling int he past few couple years.

Code Snippets

public class User
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }

    public interface IRepository<TEntity> where TEntity : class
    {
        void Create(TEntity entity);
        void Update(TEntity entity);
        void Delete(TEntity entity);
    }


    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private readonly IEntityContract<TEntity> _contract;
        private readonly DbContext _context;

        public Repository(IEntityContract<TEntity> contract, DbContext context)
        {
            _contract = contract;
            _context = context;
        }

        public void Create(TEntity entity)
        {
            //beofre we try to create it, lets evaluate it
            if (!_contract.IsValid(entity))
            {
                //possibly throw an exception or let the client know there was an issue.
            }


            //Now implement your create code.
            _context.Add(entity);
            _context.SaveChanges();
        }

        public void Update(TEntity entity)
        {
            //beofre we try to update it, lets evaluate it
            if (!_contract.IsValid(entity))
            {
                //possibly throw an exception or let the client know there was an issue.
            }

            //Now implement your update code.
            _context.Attach(entity);
            _context.SaveChanges();
        }

        public void Delete(TEntity entity)
        {
           //delete code
        }
    }

    /// <summary>
    /// Interface the all Contracts will impelement
    /// </summary>
    public interface IEntityContract<TEntity> where TEntity: class 
    {
        bool IsValid(TEntity entity);
        void RegisterBusinessRule(BusinessRule<TEntity> rule);
        IList<BusinessRule<TEntity>> Rules { get; set; }
    }

    /// <summary>
    /// Business Rule Loigc
    /// </summary>
    public class BusinessRule<TEntity> where TEntity : class
    {
        //Logic to handle business rules
    }


    /// <summary>
    /// Abstract class that represents a Base Contract
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public abstract class EntityContract<TEntity> : IEntityContract<TEntity> where TEntity : class
    {

        protected EntityContract()
        {
            Rules = new List<BusinessRule<TEntity>>();
        }

        protected EntityContract(IList<BusinessRule<TEntity>> rules)
        {
            Rules = rules;
        }

        public virtual bool IsValid(TEntity entity)
        {
            var entityIsValid = true;
            foreach (var businessRule in Rules)
            {
                //IF any rule fails the entityIsValid = false
            }

            return entityIsValid;
        }

        public IList<BusinessRule<TEntity>> Rules { get; set; }


        public void RegisterBusinessRule(BusinessRule<TEntity> rule)
        {
            Rule

Context

StackExchange Code Review Q#2448, answer score: 6

Revisions (0)

No revisions yet.