使用Entity Framework时实现业务逻辑

4
我是Entity Framework的新手,对于如何实现业务逻辑感到困惑。
我正在使用Code First方法,并且已经生成了我的POCO。我将它们放在一个单独的项目中,以便可在多个项目中使用。
我想知道如何为一个对象实现业务逻辑,在尝试保存项目到数据库时进行检验。例如,如果我定义了一个规则,即除非输入名称否则无法保存MyObject,则该如何操作?
下面是一个简单的POCO示例:
public class MyObject() {
    public String name { get; set; };

    public MyObject() {}
}

显然,我有很多对象,每个对象都有不同的业务规则。我来自于Csla (http://www.lhotka.net/cslanet/)业务框架背景,你可以定义一个业务对象,并具有Save方法,在调用Save时,框架运行ValidationRules,从而确定是否需要调用数据库。我想使用Entity Framework实现类似的功能。如果有代码示例或读物参考,将不胜感激。谢谢。

你不能将部分类分割到不同的项目中,partial 只是表示一个类可以在多个源代码文件中定义,它并不是扩展现有类的方法。 - Ben Robinson
1
我认为这是一个有趣的问题,但它有点太广泛了,你能否尝试将你的问题缩小到...一些可以在不到一章书的篇幅内回答的东西?(特别是因为这样的大主题会导致许多小而不完整的答案) - Adriano Repetti
我已经为您更新了我的问题。 - Gillardo
你可以使用数据注释属性进行简单的验证,例如[Required] - Gert Arnold
1个回答

4

我通常在应用程序中构建一个服务层来处理业务逻辑,该层与数据访问层交互以持久化任何数据。

public class JobService : IJobService
{
    private readonly IJobRepository _jobRepository;

    public JobService(IJobRepository jobRepository)
    {
        _jobRepository = jobRepository;
    }

    public IEnumerable<Job> GetAllJobs()
    {
        return _jobRepository.All().OrderBy(x => x.Name);
    }

    public Job GetJobById(Guid Id)
    {
        return _jobRepository.FindBy(x => x.Id == Id);
    }

    public void UpdateJob(Job job, User user)
    {
        job.LastUpdatedDate = DateTime.Now;
        job.LastUpdatedBy = user;

        _jobRepository.Update(job);
    }

    public void DeleteJob(Job job)
    {
        _jobRepository.Delete(job);
    }

    public void SaveJob(Job job, User user)
    {
        job.CreatedDate = DateTime.Now;
        job.CreatedBy = user;

        _jobRepository.Insert(job);
    }
}

您的服务可以尽可能复杂,同时仍然抽象化您的数据访问层。我不会给您的POCO类添加任何方法,因为那样会违反POCO类的目的。服务还是验证业务规则的好地方。虽然我没有提供这方面的示例,但验证可轻松添加到您的服务方法中。

编辑

在编写较大型应用程序时,我通常使用合同来定义数据访问层。我们使用接口来定义类结构的原因是,即使实现这些类的方式不同,也无需更改使用这些类的任何代码。例如,您可以使用EF实现存储库模式,但后来可能发现您想使用NHibernate。要进行更改,您只需更改存储库的实现,而不是实际使用存储库的代码。

我的存储库合同通常如下:

/// <summary>
/// This interface is implemented by all repositories to ensure implementation of fixed methods.
/// </summary>
/// <typeparam name="TEntity">Main Entity type this repository works on</typeparam>
/// <typeparam name="TKey">Primary key type of the entity</typeparam>
public interface IRepository<TKey, TEntity> : IRepository where TEntity : class, IEntity<TKey>
{
    /// <summary>
    /// Inserts a new entity.
    /// </summary>
    /// <param name="entity">Entity to insert</param>
    TEntity Insert(TEntity entity);

    /// <summary>
    /// Inserts multiple entities.
    /// </summary>
    /// <param name="entities">Entities to insert</param>
    IEnumerable<TEntity> Insert(IEnumerable<TEntity> entities);

    /// <summary>
    /// Updates an existing entity.
    /// </summary>
    /// <param name="entity">Entity</param>
    TEntity Update(TEntity entity);

    /// <summary>
    /// Updates or saves an entity
    /// </summary>
    /// <param name="entity">Entity</param>
    TEntity SaveOrUpdate(TEntity entity);

    /// <summary>
    /// Deletes an entity.
    /// </summary>
    /// <param name="id">Id of the entity</param>
    bool Delete(TKey id);

    /// <summary>
    /// Deletes an entity.
    /// </summary>
    /// <param name="entity">Entity to be deleted</param>
    bool Delete(TEntity entity);

    /// <summary>
    /// Deletes an entity.
    /// </summary>
    /// <param name="entities">Entities to be deleted</param>
    bool Delete(IEnumerable<TEntity> entities);

    /// <summary>
    /// Used to get an IQueryable that is used to retrieve entities from entire table.
    /// </summary>
    /// <returns>IQueryable to be used to select entities from database</returns>
    IQueryable<TEntity> All();

    /// <summary>
    /// Gets an entity.
    /// </summary>
    /// <param name="expression">LINQ expression used to evaluate and find an entity</param>
    /// <returns>Entity</returns>
    TEntity FindBy(Expression<Func<TEntity, bool>> expression);

    /// <summary>
    /// Used to get an IQueryable that is used to retrieve entities from evaluated LINQ expression.
    /// </summary>
    /// <param name="expression">LINQ expression used to evaluate and find entities</param>
    /// <returns>IQueryable to be used to select entities from database</returns>
    IQueryable<TEntity> FilterBy(Expression<Func<TEntity, bool>> expression);

    /// <summary>
    /// Gets an entity.
    /// </summary>
    /// <param name="id">Primary key of the entity to get</param>
    /// <returns>Entity</returns>
    TEntity FindBy(TKey id);
}

然后是IJobRepository:

// We create separate repositories inheriting from IRepository in case we need specific repository methods for that entity
public interface IJobRepository : IRepository<Guid, Job>
{
}

如何实现存储库取决于您,但通常对于EF,您需要将DbSet或DbContext传递给存储库并执行相应操作。

您的服务合同将取决于业务逻辑。有些实体您可能只需要从持久性存储中读取。其他实体则需要创建服务方法来验证/插入/操作等。

当您使用控制反转(IoC)以构造函数参数注入这些合同的实现时,该设计模式最为有效。我通常使用Castle Windsor,但是市场上还有许多IoC依赖注入框架可供选择。


我喜欢这个想法,谢谢你。我可以问一下IJobRepository和IJobService会声明哪些方法以及它们为什么需要(好处等)吗?抱歉,我对EF还不熟悉。 - Gillardo
1
我还建议您考虑在POCO实体或ViewModel中使用FluentValidation。FluentValidation允许您将验证从实体类中抽象出来。 - Christopher.Cubells
1
非常感谢。我是MVC的新手,这是我所能期望的最好的答案。现在正在研究IOC、FluentValidation和Repositories。 - Gillardo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接