实体框架N层应用程序 - 如何设计

时间:2015-08-02 19:42:50

标签: c# entity-framework

我正在制定一个我一直在建设/规划一个月的框架。我已经开始开发它,需要一些关于如何构建它的专业指导,我想我要么设计错误,要么只是过度复杂化。

因此,目前的主要项目是一个类库,它分为以下几个部分:

框架,核心等(这些包含所有扩展方法和其他有用的东西)

BusinessEntities (所有实体框架实体,包括使用Fluent API的实体配置)

BusinessLogicLayer DataAccessLayer

现在所有实体都从BaseEntity继承,它也继承了IValidableObject。 BaseEntity如下所示:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace uk.BusinessEntities
{
    public abstract class BaseEntity : IValidatableObject
    {
        public int PrimaryKey { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime? DateModified { get; set; }

        public abstract IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
    }
}

然后对于DataAccessLayer,每个类都继承GenericObject类,如下所示:

using System;
using System.Collections.Generic;
using System.Linq;

namespace uk.DataAccessLayer
{
    public class GenericObject<T> where T : class
    {


        public GenericObject() { }

        public static bool Add(T Entity, out IList<string> validationErrors)
        {

            using (var db = new DatabaseContext())
            {
                validationErrors = new List<string>();
                try
                {
                    db.Set<T>().Add(Entity);
                    db.SaveChanges();
                    return true;
                }
                catch (Exception ex)
                {
                    InsertValidationErrors(ex, validationErrors);
                    return false;
                }

            }

        }

        public static IList<T> Retrieve()
        {
            using (var db = new DatabaseContext())
            {
                IList<T> Query = (IList<T>)(from x in db.Set<T>()
                                            select x).ToList<T>();

                return Query;
            }
        }

        public static bool Update(T Entity, out IList<string> validationErrors)
        {
            validationErrors = new List<string>();
            using (var db = new DatabaseContext())
            {
                try
                {
                    db.Set<T>().Attach(Entity);
                    db.Entry(Entity).State = System.Data.Entity.EntityState.Modified;
                    db.SaveChanges();
                    return true;
                }
                catch (Exception ex)
                {

                    InsertValidationErrors(ex, validationErrors);

                    return false;
                }
            }

        }

        public static bool Delete(T Entity, out IList<string> validationErrors)
        {
            validationErrors = new List<string>();

            using (var db = new DatabaseContext())
            {

                try
                {

                    db.Entry(Entity).State = System.Data.Entity.EntityState.Deleted;
                    db.SaveChanges();
                    return true;

                }
                catch(Exception ex)
                {
                    InsertValidationErrors(ex, validationErrors);
                    return false;
                }
            }
        }

        protected static void InsertValidationErrors(Exception ex,  IList<string> validationErrors)
        {
            validationErrors.Insert(0, ex.Message);
            if (ex.InnerException != null)
            {
                validationErrors.Insert(0, ex.InnerException.Message);
                validationErrors.Insert(0, ex.InnerException.StackTrace);
            }
        }
    }
}

现在我的主要观点是对实体的验证。例如,我们为页面分别存储了两个实体 Page PageURLS 网址。

现在添加页面时,还需要添加PageURL,因此如果开发人员调用了

PageDAL.AddPage(page, errors)

您是否还希望此方法也自动添加网址,还是应该单独调用?

对此的任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

我建议默认使用EF使用的验证(link)。使用此方法,您还可以设置验证错误消息,甚至可以对它们进行本地化。异常错误消息通常对最终用户非常有用。此外,保存时的例外并不一定意味着验证失败。可能是其他事情出了问题。

EF确实很好地将对象图添加到数据库中。所以我会让它完成它的工作。所以是的,让PageDAL.Add(页面)也添加页面网址,因为它最终真的是相同的操作。我没有看到更明确的理由。

不幸的是,这些问题通常无法客观地回答。对我而言,YAGNI和坚持所有原则之间始终是一场斗争。这实际上取决于您构建的系统以及您自己的观点。

我已经在其他方向上犯了很多错误。和你的同行交谈,尤其是那些与你一起工作的人,想出一些事情,不要害怕在路上适应......

对不起,如果这个答案真的不令人满意。

答案 1 :(得分:1)

关于验证,我会在Controller中执行一些验证(简单,而不是面向业务)以拒绝简单的错误输入,以防它们没有被客户端捕获(或者如果跳过了客户端验证)。然后,我将验证业务层中的实体,并返回带有验证结果和验证消息列表的自定义对象。

我认为您不会考虑交易,这就是您不知道如何处理2个相关实体的原因。例如:

public static bool Delete(T Entity, out IList<string> validationErrors)
        {
            validationErrors = new List<string>();

            using (var db = new DatabaseContext())
            {

                try
                {

                    db.Entry(Entity).State = System.Data.Entity.EntityState.Deleted;
                    db.SaveChanges();
                    return true;

                }
                catch(Exception ex)
                {
                    InsertValidationErrors(ex, validationErrors);
                    return false;
                }
            }
        }

您正在创建数据库上下文,插入实体并处理上下文。如果您需要插入许多实体而第二个实体失败,您会怎么做?你这样做的方式,第一个实体将被保存,第二个实体将不被保存。您应该阅读工作单元模式,以便您可以创建一个跨操作的事务。

看看这些文章:

阅读这些文章:

1)documentation

2)https://msdn.microsoft.com/en-us/library/hh404093.aspx

3)http://www.asp.net/mvc/overview/older-versions-1/models-%28data%29/validating-with-a-service-layer-cs

4)http://blog.diatomenterprises.com/asp-net-mvc-business-logic-as-a-separate-layer/