1

请坚持我,因为这个问题可能很长!我们目前正在为我工​​作的基于 Web 的应用程序进行架构和原型设计。这是一个具有很多错综复杂和大量数据的大型应用程序。

在前端技术方面,我们将使用 Durandal、Knockout 和 TypeScript(编写我们所有的 JavaScript 和 jQuery 代码)构建一个 SPA。

后端经过了非常仔细的考虑和架构。简而言之,我们将拥有一套应用程序服务(使用 nHibernate、AutoFac 等),它们将使用精心设计的域模型。然后,WebAPI 将使用应用程序服务中的方法将数据呈现回前端。

在构建具有大量 DB 交互的 SPA 时,一个明显的想法是考虑 Breeze。Breeze 出色的原因有很多。问题是,我们不确定它是否适合我们的架构。除了一些简单的原型之外,我们没有人使用过 Breeze,所以如果有人可以帮助回答以下问题,我们将不胜感激!

1 - 我们是否正确假设默认使用 Breeze 会绕过我们的业务逻辑并直接进入 nHibernate 或 DB?如果这个假设是正确的,有没有办法绕过它?任何建议/链接?我们的想法是,我们必须为 Breeze 编写一个适配器,以路由到我们的业务逻辑并将元数据提供回 Breeze。这会带来其他问题,例如在系统的不同部分拥有部分和完全水合的模型,投影外键。这对我们来说是一个相当有争议的问题!

2 - 使用 TypeScript 的众多原因之一是通过智能感知为我们提供静态类型对象。我们是否正确假设如果我们使用 Breeze,我们就不会开箱即用?

3 - 我们根本不想要任何缓存。是否可以完全关闭 Breeze 的这一部分?

4 - 如果我们没有使用查询功能(我们绝对没有,我们有一个搜索计划,这意味着不能同时使用 Breeze 查询功能),我们没有使用缓存功能,我们可以将一些东西放在适当的位置(例如 T4 模板以自动从 DTO 生成 TypeScript 对象)以帮助 Breeze 允许的开发速度和减少代码,我们是否否定了使用 Breeze 的意义?

我做了很多搜索,但我只能找到为什么应该使用 Breeze。虽然我承认它很棒,但就我目前的理解而言,我只是不确定它是否适合我们的应用程序。任何建议或推荐阅读将不胜感激。由于微风在现场相当新,我很难找到很多信息!

4

3 回答 3

2

1)您可以拦截任何查询或保存在服务器上的微风中。对于保存,这是通过 ContextProvider 上的 BeforeSaveEntities 和 BeforeSaveEntity 可覆盖方法完成的。对于查询,只需通过服务器端端点方法中的自定义代码即可完成。这两种技术都在 Breeze 文档中进行了描述,并且 Breeze zip 中的 DocCode 示例中有示例。

2) 我们有客户将 Breeze 与 Typescript 一起使用,并使用 Breeze 元数据生成 Typescript 类定义。我们计划在以后制作此类代码的一个版本以供一般使用。(我们目前为 Typescript 相关任务提供咨询支持。请通过微风@ideablade.com 与我们联系。)

3) 我们计划在一个月内为 Breeze 的下一个版本提供noTracking选项。目前,只需执行“投影”查询即可重现相同的效果。

4) 使用您自己的 Breeze 查询系统没有问题。请参阅 Breeze 文档中对“namedQuery”的讨论。如果您想通过额外的客户端过滤来增强基本查询,您甚至可以混合和匹配。

到你的大点。如果您不打算使用任何缓存或任何微风查询增强功能,那么微风可能适合。

然而,缓存的价值是你真正需要评估的。如果您打算重新创建此功能,您可能需要自己重新创建

  1. 以断开连接的方式运行您的应用程序。这包括本地存储的序列化和反序列化。

  2. 减少客户端和服务器之间已检索数据的往返次数。随着应用程序变得越来越大,这可能是您的应用程序的主要性能改进。

  3. 能够查询当前机器内存中的内容。

  4. 单独检索时自动连接彼此相关的实体图。

  5. 更改跟踪以及将实体恢复到其最初查询状态的能力。

还有其他几个问题,我有点懒得一一列举。

希望这可以帮助!

于 2013-10-31T18:48:24.187 回答
1

我赞成杰的回答,并将继续努力。Jay 没有提到它,但现在已经支持 NHibernate。

我对任何跳过 IQueryable 支持的人都没有意见。Breeze对此很酷。您仍然可以使用轻量级的 EntityQuery 来访问任何 GET 端点并根据需要向其传递参数;那是在您“进阶”并开始调整 ajax 适配器之前。

或者您可以使用任何您喜欢的 AJAX 设备来检索/保存数据并将微风融入您的流程。在即将发布的版本中,无论您如何获取这些数据,将 JSON 数据作为实体合并到缓存中都会变得更加容易。

许多人会选择一种混合方法,在这种方法中,他们使用香草微风来处理简单的东西,比如倾向于主导 API 的参考列表。但他们会针对需要特殊处理的关键“20%”API 改用自定义方法。

我很想知道您为什么根本不想使用缓存。我明白为什么您可能不希望它用于某些查询。但从来没有

您意识到您的客户端数据在您收到它的那一刻与服务器不同步。这真的只是时间问题。缓存会延长数据逐渐过时的时间。但它在离开服务器的那一刻就已经腐烂了。

缓存不是永远的。您可以随时刷新缓存中的单个实体。您可以随时清除缓存。并且根据数据的波动性或响应来自服务器的通知(您可能使用 SignalR 执行的边带操作),尽可能多地执行这两项操作。

缓存中的完整实体对于体验 Breeze 的许多好处确实是必不可少的:

  • 在多个视图之间轻松共享公共数据
  • 跨视图公开实体数据的当前状态(包括验证错误消息)。
  • 跟踪脏状态(EntityState.Modified
  • 属性更改时自动验证
  • 导航到相关实体:order.lineitem[0].product.name
  • 借助自组装实体图(即,当 Breeze 接收到查询产品的数据时,它会自动为产品的相关参考实体(例如shippermanufacturerproductTypeproductPricing )构建导航属性……假设这些都在缓存中。
  • 恢复挂起的更改
  • 查询缓存
  • 在本地存储更改并与保留的更改状态一起恢复。

您可以根据需要拥有任意数量的不同缓存,每个缓存都有自己的生命周期。

记住……控制着缓存。

那么你有什么反对缓存的呢?

于 2013-10-31T22:42:25.233 回答
1

保持一个新的缓存

您在 10 月 31 日的评论提醒我,您可以保持资源新鲜并使用缓存。这不是“非此即彼”!

只需执行以下操作:

  1. 在使用它之前总是重新查询该资源(例如,当您的 ViewModel 加载视图时)
  2. 在发出该查询之前清除该资源类型的所有实例的缓存。

您可以将这两种想法都封装在您的datacontext.getXXX方法中。这就是我的意思:

// 清除发票缓存后获取新发票,可选择过滤
函数getInvoices(可选谓词){
    clearCachedInvoices();
    var 查询 = 微风.EntityQuery.from('Invoices');
    if (optionalPredicate) { query = query.where(predicate); }
    返回 manager.executeQuery(query).then(_logSuccess, _queryFailed);
}

// 刷新特定发票
功能刷新发票(发票){
    // Todo: 添加参数错误检查?
    var 查询 = 微风.EntityQuery.fromEntities([invoice]);
    返回 manager.executeQuery(query).then(success, _queryFailed);

    功能成功(数据){
        _logSuccess(数据);
        返回数据.结果[0];// 查询返回数组;来电者想要第一个。
    }
}

函数 clearCachedInvoices() {
    var cachedInvoices = manager.getEntities('Invoice'); // 缓存中的所有发票
    // Todo: 这应该是 Breeze EntityManager 本身的一个函数
    cachedInvoices.forEach(function (entity) { manager.detachEntity(entity); });
}

关于缓存清除的重要注意事项

我首先清除缓存的原因是另一个用户可能已经删除了您之前检索到的一些发票。我假设您希望将它们从缓存中删除,以便用户只能看到实时发票。

如果无法删除发票(例如,您执行“软删除”而不是将发票标记为“非活动”),则不需要此缓存清除步骤(以及随之而来的问题)。我个人对删除非常警惕,因为它们会导致各种问题。我更喜欢软删除。

您需要确保 UI 没有保留以前的发票实体。你已经要求经理重新开始。这意味着每个现有的发票实体引用都指的是一个分离的实体。查询后,每个缓存的发票都是一个新实例。

清除缓存的另一个危险是它会清除所有未决的发票更改。如果您可能有未保存的发票更改(新的、更新的或计划的删除),您不希望运行此方法。您可能希望添加保护逻辑以防止丢失未保存的更改。该逻辑究竟是什么将是特定于应用程序的。它可能会涉及调用manager.hasChanges('Invoice').

如果您始终刷新与发票有关的所有内容,则不必担心丢失参考。

这些约束应该很容易满足您的要求。它们与您最初所说的非常一致:您真的不想缓存。所以这应该是小菜一碟......只需使用上面显示的代码,您就可以轻松获得缓存的好处。

啊......但我不禁想到实际上想要刷新实体对象而不是完全替换它们的人。也许她想在用户有未保存的更改时刷新。然而,她想删除已被其他用户删除的实体。

好吧,我也有她的食谱。

功能 refreshAllInvoices(已删除){
        // 移除的是调用者的数组,应该填充实体
        // 我们从缓存中删除;它填充在下面的成功方法中。
    var cached = manager.getEntities('Invoice'); // 获取缓存中的所有发票
    return 微风.EntityQuery.from('Invoices')
                 .using(manager).execute(成功,_queryFailed);

    函数成功(){  
        删除.长度 = 0; //清空数组  
        var 结果 = data.results; // 查询结果
        // 从“缓存”数组中删除每个结果
        results.forEach(函数(实体){
            var ix = cached.indexof(entity);
            if (ix > -1) { 缓存[ix] = null; }
        });
        // 剩下的必须在服务器上删除
        // 或者是一个我们还没有保存的新实体
        // 循环遍历,分离我们认为已经被删除的那些。
        cached.forEach(函数(实体){
            如果(实体!== null &&
                !entity.entityAspect.entityState.isAdded()) {
                移除.push(实体);// 让调用者知道这个
                manager.detachEntity(实体);
            }
        });

        返回结果;
    }
}
于 2013-11-01T18:05:25.407 回答