SPA建筑问题中的微风适用性

时间:2013-10-31 10:09:11

标签: c# architecture breeze single-page-application

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

前端技术明智,我们将使用Durandal,Knockout和TypeScript构建SPA(编写我们所有的JavaScript和jQuery代码)。

后端经过精心设计和构建。简化后,我们将提供一套应用程序服务(使用nHibernate,AutoFac等),这些服务将使用精心设计的域模型。然后,WebAPI将使用应用程序服务中的方法将数据呈现回前端。

在构建具有大量数据库交互的SPA时,一个明显的想法是考虑Breeze。有很多有据可查的原因,为什么Breeze很棒。问题是,我们不确定它是否适合我们的架构。我们没有人使用Breeze超越一些简单的原型,所以如果有人可以帮助回答以下问题,我们将非常感激!

1 - 我们是否正确假设默认情况下使用Breeze会绕过我们的业务逻辑并直接转到nHibernate或DB?如果这个假设是正确的,有没有办法绕过它?有什么建议/链接吗?我们的想法是,我们必须为Breeze编写一个适配器,以便路由到我们的业务逻辑并将元数据提供给Breeze。然后,这会出现其他问题,例如在系统的不同部分中具有部分和完全水合的模型,从而突出外键。这对我们来说是一个非常有争议的问题!

2 - 使用TypeScript的众多原因之一是为我们提供具有intellisense的静态类型对象。如果我们使用Breeze,我们是否正确,我们不会开箱即用?

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

4 - 如果我们没有使用查询功能(我们绝对没有,我们有一个搜索计划,这意味着使用Breeze查询功能并不是一个选项),我们没有使用缓存功能我们可以把东西放到位(比如T4模板从DTO自动生成TypeScript对象),以帮助开发速度和减少Breeze可以允许的代码,我们是否否定了使用Breeze的观点?

我做了很多搜索,但我能找到的就是为什么要使用Breeze。虽然我承认这很棒,但据我所知,到目前为止,我只是不确定它是否符合我们的应用程序。任何建议或推荐阅读将不胜感激。由于Breeze在现场很新,我很难找到很多信息!

3 个答案:

答案 0 :(得分:2)

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

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

3)我们计划在一个月内向下一版Breeze提供 noTracking 选项。目前,仅通过执行“投影”查询就可以再现这种相同的效果。

4)使用自己的查询系统与Breeze没有任何问题。请参阅Breeze文档中对“namedQuery”的讨论。如果您希望通过额外的客户端过滤扩充基本查询,您甚至可以混合搭配。

更重要的是。如果您不打算使用任何缓存或任何微风查询增强功能,那么Breeze可能非常合适。

但是,缓存的值是您真正需要评估的值。如果您计划

,可能需要自己重新创建此功能
  
      
  1. 以断开连接的方式运行您的应用。这包括从本地存储进行序列化和反序列化。

  2.   
  3. 减少客户端和服务器之间为已检索数据的往返次数。随着应用变得越来越大,这可能会对您的应用产生重大的性能提升。

  4.   
  5. 能够查询当前计算机内存中的内容。

  6.   
  7. 单独检索时,自动挂钩彼此相关的实体图。

  8.   
  9. 更改跟踪以及将实体还原到其原始查询状态的功能。

  10.   

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

希望这有帮助!

答案 1 :(得分:1)

我赞同周杰伦的回答,并将继续。 Jay没有提到它,但现在支持NHibernate。

我对跳过IQueryable支持的人没有任何问题。微风很酷。您仍然可以使用breeze EntityQuery命中任何GET端点并根据需要传递参数;那就是在你进入“高级”并开始调整ajax适配器之前。

或者您可以使用您喜欢的任何AJAX设备来检索/保存数据并将微风编织到您的过程中。在即将发布的版本中,无论您如何获取这些数据,都可以更轻松地将JSON数据合并到缓存中。

许多人会选择一种混合方法,在这种方法中,他们使用香草微风来轻松获取类似API的参考列表。但是他们将转向使用需要特殊处理的关键“20%”API的自定义方法。

我很想知道为什么你不想使用缓存。我明白为什么你可能不希望它用于一些查询。但从不

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

缓存不是永远的。您可以随时刷新缓存中的各个实体。您可以随时清除缓存。根据数据的不稳定性或者响应来自服务器的通知(您可能使用SignalR进行边带处理),可以根据需要随时进行操作。

缓存中的完整实体对于体验Breeze的许多好处确实至关重要:

  • 轻松地在多个视图之间共享公共数据
  • 在视图中公开实体数据的当前状态(包括验证错误消息)。
  • 跟踪脏状态( EntityState.Modified
  • 属性更改时的自动验证
  • 导航至相关实体: order.lineitem [0] .product.name
  • 缩小查询负载,这要归功于自组装实体图(即,当Breeze收到查询产品的数据时,它会自动将导航属性构建到产品的相关参考实体,例如托运人制造商 productType productPricing ...假设这些都在缓存中。
  • 还原待定更改
  • 查询缓存
  • 存储在本地更改并与保留的更改状态一起恢复。

您可以根据需要拥有尽可能多的不同缓存,每个缓存都有自己的生命周期。

请记住...... 控制缓存。

那你对缓存有什么看法?

答案 2 :(得分:1)

保持新缓存

您对10月31日的评论提醒我,您可以保持资源新鲜并使用缓存。它不是“要么/或”!

请执行以下操作:

  1. 在使用资源之前始终重新查询该资源(例如,当您的ViewModel加载视图时)
  2. 在发出该查询之前,清除该资源类型的所有实例的缓存。
  3. 您可以在datacontext.getXXX方法中封装这两种想法。这就是我的意思:

    // get fresh invoices, optionally filtered, after clearing the cache of invoices
    function getInvoices(optionalPredicate) {
        clearCachedInvoices();
        var query = breeze.EntityQuery.from('Invoices');
        if (optionalPredicate) { query = query.where(predicate); }
        return manager.executeQuery(query).then(_logSuccess, _queryFailed);
    }
    
    // refresh a specific invoice
    function refreshInvoice(invoice) {
        // Todo: add parameter error checking?
        var query = breeze.EntityQuery.fromEntities([invoice]);
        return manager.executeQuery(query).then(success, _queryFailed);
    
        function success(data) {
            _logSuccess(data);
            return data.results[0]; // queries return arrays; caller wants the first.
        }
    }
    
    function clearCachedInvoices() {
        var cachedInvoices = manager.getEntities('Invoice'); // all invoices in cache
        // Todo: this should be a function of the Breeze EntityManager itself
        cachedInvoices.forEach(function (entity) { manager.detachEntity(entity); });
    }
    

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

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

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

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

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

    如果您总是刷新与发票有关的所有内容,则无需担心丢失的引用。

    这些限制应该很容易让您实现。它们几乎与你原来说的一致:你真的不想缓存。所以这应该是一块蛋糕......只需使用上面显示的代码,您就可以轻松获得缓存的好处。

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

    我也有她的食谱。

    function refreshAllInvoices(removed) {
            // removed is the caller's array that should be filled with the entities 
            // that we remove from cache; it's populated in the success method below.
        var cached = manager.getEntities('Invoice'); // get all invoices in cache
        return breeze.EntityQuery.from('Invoices')
                     .using(manager).execute(success, _queryFailed);
    
        function success() {  
            removed.length = 0; // clear the array  
            var results = data.results; // results from query
            // remove each result from the 'cached' array
            results.forEach(function (entity) {
                var ix = cached.indexof(entity);
                if (ix > -1) { cached[ix] = null; }
            });
            // what's left must have been deleted on the server
            // or is a new entity we haven't saved yet
            // Loop through, detaching the ones we think have been deleted.
            cached.forEach(function (entity) {
                if (entity !== null &&
                    !entity.entityAspect.entityState.isAdded()) {
                    removed.push(entity); // let caller know about this one
                    manager.detachEntity(entity);
                }
            });
    
            return results;
        }
    }