【问题标题】:How to use Breeze with Generic Unit of Work and Repositories?如何将 Breeze 与通用工作单元和存储库一起使用?
【发布时间】:2014-01-09 22:32:06
【问题描述】:

使用这个:

https://genericunitofworkandrepositories.codeplex.com/

以及以下一组博文:

http://blog.longle.net/2013/05/11/genericizing-the-unit-of-work-pattern-repository-pattern-with-entity-framework-in-mvc/

我们正在尝试将这些存储库与 Breeze 一起使用,因为它可以很好地处理客户端 javascript 和 OData。

我想知道我们如何将这些与Breeze 一起使用来正确处理覆盖BeforeSaveEntity

我们有相当多的业务逻辑需要在保存期间发生(修改属性,如ModifiedByModifiedTimeCreatedBy 等)但是当我们更改它们时,它们不会被微风更新,所以我们必须在保存后重新查询(我们已经尝试手动将更改映射回来,但它需要我们复制所有的业务逻辑)。

我们的第二个选项是检查每个entity 的类型,然后为其请求正确的存储库,在内部处理保存,然后在客户端上执行新的获取请求以获取更新的信息。虽然这很健谈,所以我们希望有更好的方法。在绕过微风的保存而不返回错误或之后必须重新获取数据的情况下更新这些对象的正确方法是什么?

保存期间任何带有业务逻辑的 Breeze 示例都会非常有帮助,特别是如果它发生在服务、存储库或其他东西中,而不是直接在 BeforeSaveEntity 方法中。

【问题讨论】:

    标签: c# asp.net asp.net-web-api odata breeze


    【解决方案1】:

    这是很多问题,每个问题都是一个大话题。我能做的最好的就是为你指明一些方向。

    在开始之前,让我解释一下为什么您没有看到设置“ModifiedByModifiedTimeCreatedBy 等属性”的效果。 EFContextProvider 不会更新修改后的实体 的所有属性,而只会更新EntityInfo.OriginalValuesMap 中提到的那些属性,这是一个包含属性名称和仅已更改属性的原始值的字典。如果要保存仅在服务器上设置的属性,只需将其添加到原始值映射中即可:

    var map = EntityInfo.OriginalValuesMap;
    map["ModifiedBy"]=null; // the original value does not matter
    map["ModifiedTime"]=null;
    

    现在 Breeze 也知道保存这些属性,并且它们的新值将返回给客户端。

    让我们回到大局。

    Breeze 首先是一个客户端 JavaScript 库。只要您的服务器使用 HTTP 和 JSON,您几乎可以在服务器端做任何您想做的事情并让 Breeze 高兴。

    无论您喜欢哪种技术,编写一个能够提供您需要的所有功能的服务器都不是一件容易的事。 Breeze 的作者提供了一些开箱即用的 .NET 组件,让您的工作更轻松,尤其是当您选择 Web API、EF 和 SQL Server 堆栈时。

    我们的 .NET 演示通常将所有内容都放入一个 Web 应用程序中。这不是我们在实践中滚动的方式。在现实生活中,我们永远不会在我们的 Web API 控制器中实例化 Breeze EFContextProvider。该控制器(或多个控制器)将委托给负责业务逻辑和数据访问的外部类,可能是存储库或工作单元 (UoW) 类。

    带有 Breeze .NET 组件的存储库模式

    我们倾向于为模型(通常是 POCO)、数据访问 (ORM) 和 Web(Web API 加上客户端资产)项目创建单独的项目。您会在 DocCode Sample 和 John Papa 的 Code Camper 示例中看到这种分离,这是他的 PluralsSight 课程“Building Apps with Angular and Breeze”的伴侣。

    这些示例还演示了存储库模式的实现,它将多个存储库和 UoW 的职责混合在一个类中。这对于这些样本中的小型模型是有意义的。没有什么可以阻止您将存储库重构为单独的类。

    我们将存储库类与 EF 数据访问材料保存在同一个项目中,因为我们认为为这个小目的创建另一个项目没有特别的价值。如果你下定决心,重构成一个单独的项目并不难。

    Breeze 和 Code Camper 示例都专注于 Breeze 客户端开发。它们在服务器端逻辑上很薄。也就是说,您将在 DocCode 示例中的“NorthwindRepository.cs”和“NorthwindEntitySaveGuard.cs”文件的BeforeSaveEntities 扩展点中找到应用自定义业务逻辑的宝贵线索。您将看到如何将保存限制为某些类型以及基于发出请求的用户的这些类型的某些记录。

    如果您尝试通过单个端点传递所有保存更改请求,则逻辑可能会不堪重负。你不必那样做。您可以有多个保存端点,每个端点都专用于特定的业务操作,仅限于以高度特定的方式插入/更新/删除几种类型的实体。你可以随心所欲。请参阅"Saving Entities" topic 中的“命名保存”。

    随心所欲

    现在有无数种方法可以实现存储库和 UoW 模式。

    您可以按照您引用的帖子所述的方式进行。在这种情况下,您不需要 Breeze .NET 组件。将您的 Web API 查询方法(IQueryable 或不)连接到返回 IQueryable(或只是对象)的存储库方法非常简单。 Web API 不必知道您在幕后是否有 Breeze EFContextProvider 或完全不同的东西。

    处理 Breeze 客户端的 SaveChanges 请求有点棘手。也许您可以从ContextProviderEFContextProvider 派生;也许不吧。 Study the "ContextProvider.cs" documentationsource code,尤其是 SaveChanges 方法,您将看到您需要做些什么才能让 Breeze 客户端满意并与之交互,但是您希望使用 UoW 处理更改集保存。

    假设您在客户端没有更改任何内容(这是一个假设,而不是给定的......您可以根据需要更改保存协议),您的 SaveChanges 只需要做两个事情:

    1. 解释来自客户端的“saveBundle”。
    2. 返回与SaveResult 结构相似的内容

    saveBundle 是一个 JSON 包,您可能不想自己解包。幸运的是,您可以从 ContextProvider 派生一个类,您可以使用它来将 saveBundle 转换为“SaveMap”,这是一个包含 EntityInfo 对象的字典,这几乎是任何人在分析变更集时都希望使用的对象用于验证和保存。

    以下方法可能会奏效:

    using System;
    using System.Collections.Generic;
    using System.Data;
    using Breeze.ContextProvider;
    using Newtonsoft.Json.Linq;
    
    public class SaveBundleToSaveMap : ContextProvider 
    {
        // Never create a public instance
        private SaveBundleToSaveMap(){}
    
        /// <summary>
        /// Convert a saveBundle into a SaveMap
        /// </summary>
        public static Dictionary<Type, List<EntityInfo>> Convert(JObject saveBundle)
        {
            var dynSaveBundle = (dynamic) saveBundle;
            var entitiesArray = (JArray) dynSaveBundle.entities;
            var provider = new SaveBundleToSaveMap();
            var saveWorkState = new SaveWorkState(provider, entitiesArray);
            return saveWorkState.SaveMap;
        }
    
        // override abstract members but DO NOT USE ANY OF THEM
    
    }
    

    然后由您决定如何使用“SaveMap”并分派到您的业务逻辑。

    SaveResult 是一个简单的结构:

    public class SaveResult {
        public List<Object> Entities; // each of the entity type you serialize to the client
        public List<KeyMapping> KeyMappings;
        public List<Object> Errors;
    }
    
    public class KeyMapping {
        public String EntityTypeName;
        public Object TempValue;
        public Object RealValue;
    }
    

    按原样使用这些类或构建您自己的类。 Breeze 客户端关心的是 JSON,而不是这些类型。

    【讨论】:

    • 哇,非常感谢 Ward 的巨大反应,我没想到最早要到下周才能看到任何东西!我们之前尝试过添加map[],但我认为我们可能一直试图让它变得过于复杂,并在那里和我们的常规 Web Api 中复制我们的逻辑。我将看一下示例代码 DocSample。我真的很喜欢 Breeze 在客户端处理方面的出色表现,它只是通过我们许多不同的业务规则让服务器端的一切正常工作,这一直是困难的部分。如果我们有任何其他问题,我会告诉您,现在只想说声谢谢! :)
    • 我正在考虑实现此功能(基本上从 JObject 中提取 EntityInfo 对象)尝试使用最新版本的微风会导致空引用异常。查看源代码 SaveWorkState 构造函数调用 ContextProvider.CreateEntityInfoFromJson 然后执行 JsonSerializer.Deserialize(new JTokenReader(jo), entityType);但是 JsonSerializer 为空。这仍然是实现这一目标的推荐方式吗?
    • 我认为最好开始一个新的 SO 问题,您可以在其中详细说明您的目标,您认为应该在哪里分配责任,并且(最重要的是)提供一个演示问题的小代码示例.谢谢。
    • 谢谢沃德。你能看看这个相关的问题stackoverflow.com/questions/21691579/…
    • 是的。这样做了。看看吧。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多