如何使用Breeze与通用工作单元和存储库?

时间:2013-12-21 00:48:06

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

使用此:

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等属性)但是当我们更改它们时它们不是'由breeze更新,因此我们必须在保存后重新查询(我们已尝试手动映射更改,但它要求我们复制所有业务逻辑)。

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

保存期间Breeze with Business Logic的任何示例都非常有用,特别是如果它直接在BeforeSaveEntity方法中发生在服务,存储库或其他内容中。

1 个答案:

答案 0 :(得分:10)

这是一个很多问题,每个问题都是一个很大的主题。我能做的最好的事情就是指向某个方向。

在我开始讨论之前,让我解释一下为什么你没有看到设置ModifiedByModifiedTimeCreatedBy属性的影响,等)&#34 ;. 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客户端开发。它们在服务器端逻辑上很薄。也就是说,您将找到有用的线索,用于在" NorthwindRepository.cs"中的BeforeSaveEntities扩展点中应用自定义业务逻辑。和`NorthwindEntitySaveGuard.cs" DocCode示例中的文件。您将了解如何根据发出请求的用户将保存限制为某些类型和某些类型的某些记录。

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

按自己的方式行事

现在有很多方法可以实现存储库和UoW模式。

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

处理Breeze客户端的SaveChanges请求有点棘手。也许你可以从ContextProviderEFContextProvider派生;也许不吧。 Study the "ContextProvider.cs" documentationsource code,尤其是SaveChanges方法,您将看到您需要做些什么来保持Breeze客户端的满意度和界面,但是您想要处理更改集用你的UoW保存。

假设您在客户端没有任何更改(假设,而不是给定的......您可以根据需要更改保存协议),{{1}只需做两件事:

  1. 解释" saveBundle"来自客户。
  2. 返回与SaveChanges
  3. 结构相似的内容

    SaveResult是一个JSON包,您可能不想自行解压缩。幸运的是,您可以从saveBundle派生一个类,只需将ContextProvider转换为" SaveMap",saveBundle对象的字典即可在分析用于验证和保存的更改集时,几乎任何人都想要使用它。

    以下可能会做到这一点:

    EntityInfo

    然后由你来决定如何使用" SaveMap"并发送到您的业务逻辑。

    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 } 是一个简单的结构:

    SaveResult

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