如何使用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
。
我们在保存期间需要发生相当多的业务逻辑(修改像ModifiedBy
, ModifiedTime
, CreatedBy
等属性)但是当我们更改它们时它们不会被微风更新,所以我们必须在保存后重新查询(我们已经尝试手动映射更改,但它要求我们复制所有业务逻辑)。
我们的第二个选择是检查每个entity
的类型,然后为它请求正确的存储库,在内部处理保存,然后在客户端上执行新的get请求以获取更新的信息。 这很健谈,所以我们希望有更好的方法。 在绕过breeze的保存而不返回错误或之后必须重新获取数据的情况下更新这些对象的正确方法是什么?
保存期间Breeze with Business Logic的任何示例都非常有用,特别是如果它直接在BeforeSaveEntity
方法中发生在服务,存储库或其他内容中。
这是许多问题集合在一起,每个问题都是一个很大的话题。 我能做的最好的事情就是指向某个方向。
在我开始讨论之前,让我解释为什么你没有看到设置“ 像ModifiedBy
, ModifiedTime
, CreatedBy
等属性 ”的效果。 EFContextProvider
不会更新已修改实体的每个属性,而只更新EFContextProvider
中提到的属性,即属性名称的字典和仅更改的属性的原始值。 如果要保存仅在服务器上设置的属性,只需将其添加到原始值映射:
var map = EntityInfo.OriginalValuesMap; map["ModifiedBy"]=null; // the original value does not matter map["ModifiedTime"]=null;
现在Breeze也知道要保存这些属性,它们的新值将返回给客户端。
让我们回到更大的图景。
Breeze首先是客户端JavaScript库。 只要您的服务器使用HTTP和JSON,您就可以在服务器端执行任何您想要的操作,并使Breeze对此感到高兴。
无论您喜欢什么技术,编写提供所需function的服务器都是微不足道的。 Breeze的作者提供了一些开箱即用的.NET组件,使您的工作更轻松,尤其是当您选择Web API,EF和SQL Server堆栈时。
我们的.NET演示通常将所有内容都放入一个Web应用程序中 这不是我们在实践中的表现。 在现实生活中,我们永远不会在Web API控制器中实例化Breeze EFContextProvider
。 该控制器(或多个控制器)将委托给负责业务逻辑和数据访问的外部类,可能是存储库或工作单元(UoW)类。
使用Breeze .NET组件的存储库模式
我们倾向于为模型(通常是POCO),数据访问(ORM)和Web(Web API加客户端资产)项目创建单独的项目。 您将在DocCode示例中以及John Papa的Code Camper示例中看到这种分离,这是他的PluralsSight课程“ 使用Angular和Breeze构建应用程序 ”的伴侣。
这些示例还演示了存储库模式的实现,该模式将多个存储库和UoW的职责混合在一个类中。 这对于这些样品中的小型模型是有意义的。 没有什么可以阻止您将存储库重构为单独的类。
我们将我们的存储库类保存在与EF数据访问材料相同的项目中,因为我们认为在为这个小目的创建另一个项目时没有特别的价值。 如果您决定这样做,那么重构成一个单独的项目并不困难。
Breeze和Code Camper样本都专注于Breeze客户端开发。 它们在服务器端逻辑上很薄。 也就是说,您将在DocCode示例中的“NorthwindRepository.cs”和“NorthwindEntitySaveGuard.cs”文件中的BeforeSaveEntities
扩展点中找到有用的自定义业务逻辑的有用线索。您将看到如何将保存限制为某些类型和某些类型基于发出请求的用户的那些类型的记录。
如果您尝试通过单个端点引导所有保存更改请求,则逻辑可能会非常大。 你不必那样做。 您可以拥有多个保存端点,每个端点专用于特定的业务操作,仅限于以高度特定的方式插入/更新/删除几种类型的实体。 你可以随心所欲。 请参阅“ 保存实体”主题中的“命名保存”。
按自己的方式行事吧
现在有很多方法可以实现存储库和UoW模式。
你可以按照你引用的post提出的方式行事。 在这种情况下,您不需要Breeze .NET组件。 将Web API查询方法( IQueryable
与否)连接到返回IQueryable
(或只是对象)的存储库方法是非常简单的。 Web API不必知道您是否在幕后拥有Breeze EFContextProvider
或完全不同的东西。
处理Breeze客户端的SaveChanges
请求有点棘手。 也许你可以从ContextProvider
或EFContextProvider
派生出来; 也许不吧。 研究“ContextProvider.cs”文档和源代码 ,特别是SaveChanges
方法,您将看到您需要做些什么来保持Breeze客户端的满意度和接口,但是您希望使用UoW处理变更集保存。
假设您在客户端没有任何改变(这是一个假设 ,而不是给定的……如果您愿意,可以更改保存协议),您的SaveChanges
只需做两件事:
- 从客户端解释“saveBundle”。
- 返回与
SaveResult
结构相似的SaveResult
saveBundle
是一个JSON包,您可能不想自己解压缩。 幸运的是,您可以从ContextProvider
派生一个类,您只需将saveBundle
转换为“SaveMap”,这是一个EntityInfo
对象的字典,几乎是任何人在分析用于validation和保存的更改集时都希望使用的。
以下可能会做到这一点:
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(){} /// /// Convert a saveBundle into a SaveMap /// public static Dictionary> 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
按原样使用这些类或构建自己的类。 Breeze客户端关心JSON,而不是这些类型。