简体   繁体   English

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

[英]Entity Framework N-Tier Application - How to design

I am working on a framework that I have been building/planning for a month now. 我正在制定一个我一直在建设/规划一个月的框架。 I have started to develop it and need some expertise guidance on how to structure it, I think i am either designing it wrong or simply overcomplicating it. 我已经开始开发它并需要一些关于如何构建它的专业指导,我想我要么设计错误,要么只是过度复杂化。

So the main project at the moment is a class library and it is split into the following: 所以目前的主要项目是一个类库,它分为以下几个:

Framework , Core etc (These house all extension methods and other useful goodies) 框架 ,核心等(这些包含所有扩展方法和其他有用的东西)

BusinessEntities (All the entity framework Entities, including configuration for entities, using Fluent API) BusinessEntities (所有实体框架实体,包括实体配置,使用Fluent API)

BusinessLogicLayer DataAccessLayer BusinessLogicLayer DataAccessLayer

Now all entities inherit from BaseEntity which also inherits IValidableObject. 现在所有实体都继承自BaseEntity,它也继承了IValidableObject。 BaseEntity looks like this: 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);
    }
}

Then for the DataAccessLayer each class inherits the GenericObject class which looks like this: 然后对于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);
            }
        }
    }
}

Now my main point lies all with validation of the entities. 现在我的主要观点都在于验证实体。 For example we got two entities Page and PageURLS URLS are stored separately for pages. 例如,我们有两个实体页面PageURLS URLS分别存储在页面中。

Now when adding a Page the PageURL also needs to be added as well, so if a developer called 现在,在添加页面时,还需要添加PageURL,因此如果开发人员调用了

PageDAL.AddPage(page, errors)

Would you also expect this method to also add the URL automatically or should this be a separate call? 您是否还希望此方法也自动添加URL或者这是一个单独的调用?

Any help on this would be greatly appreciated. 任何有关这方面的帮助将不胜感激。

I would suggest using the validation used by EF by default (link) . 我建议默认使用EF使用的验证(链接) With this approach you can also set validation error messages, and even localize them. 使用此方法,您还可以设置验证错误消息,甚至可以对它们进行本地化。 Exception error messages aren't usually really useful to endusers. 异常错误消息通常对最终用户不是很有用。 In addition, an exception when saving doesn't necessarily mean that validation failed. 此外,保存时的异常并不一定意味着验证失败。 It could be that something else went wrong. 可能是其他问题出了问题。

EF really does a good job of adding object graphs to the database. EF确实很好地将对象图添加到数据库中。 So I would let it do its job. 所以我会让它完成它的工作。 So yes, have the PageDAL.Add(page) also add the page urls, since it's really the same operation in the end. 所以,是的,让PageDAL.Add(页面)也添加页面网址,因为它最终是相同的操作。 I don't see a reason to be more explicit. 我没有理由更明确。

Unfortunately these kind of questions usually can't be answered objectively. 不幸的是,这些问题通常无法客观地回答。 For me it's always a struggle between YAGNI and adhering to all the principles. 对我而言,YAGNI和坚持所有原则之间始终是一场斗争。 It really depends on the system you're building and on your own point of view. 这实际上取决于您正在构建的系统以及您自己的观点。

I already made lots of mistakes in either on or the other direction. 我已经在其他方向上犯了很多错误。 Talk to your peers, especially the ones that will work on the project with you, figure something out, and don't be afraid to adapt on the way... 和你的同行交谈,特别是与你一起工作的人,找出一些东西,不要害怕适应途中......

I'm sorry if this answer isn't really satisfactory. 如果这个答案不能令人满意,我很抱歉。

Regarding validation, I would perform some validations (simple, not business oriented) in the Controller to reject simple wrong inputs in case they were not caught on the client-side (or if the client-side validations were skipped). 关于验证,我会在Controller中执行一些验证(简单,不是面向业务)来拒绝简单的错误输入,以防它们没有被客户端捕获(或者如果跳过了客户端验证)。 Then I would validate the entities in the Business Layer and return a custom object with a validation result and a list of validation messages. 然后,我将验证业务层中的实体,并返回带有验证结果和验证消息列表的自定义对象。

I think you're not considering transactions and that's why you don't know how to deal with 2 related entities. 我认为你不考虑交易,这就是你不知道如何处理2个相关实体的原因。 For example: 例如:

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;
                }
            }
        }

You are creating a Database context, inserting an entity and disposing the context. 您正在创建数据库上下文,插入实体并处理上下文。 If you need to insert many entities and the second entity fails, what would you do? 如果您需要插入许多实体而第二个实体失败,您会怎么做? the way you are doing it, the first entity will be saved and the second one would not be saved. 你这样做的方式,第一个实体将被保存,第二个实体将不被保存。 You should read about Unit of Work pattern so you can create a transaction accross operations. 您应该阅读工作单元模式,以便您可以创建跨操作的事务。

Take a look at these articles: 看看这些文章:

Read these articles: 阅读这些文章:

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

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

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

4) http://sampathloku.blogspot.com.ar/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html 4) http://sampathloku.blogspot.com.ar/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM