Javascript 一旦实例引用丢失,请编辑或删除BreezeJS EntityManager

Javascript 一旦实例引用丢失,请编辑或删除BreezeJS EntityManager,javascript,angularjs,breeze,Javascript,Angularjs,Breeze,我正在使用BreezeJS和AngularJS构建一个具有SPA结构的CRM应用程序,并且我正在使用一个动态生成的选项卡式环境来显示我在这里称为模块的内容。当用户单击侧菜单项时,将创建一个新选项卡,并将HTML模板(又称模块)加载到新内容区域。虽然某些模块类型一次只能打开一次(帐户网格),但其他模块(如帐户编辑器模块)可以打开多次。这样,用户可以在一天中的任何给定时间修改多个帐户,但总是从单个帐户网格实例进行修改 到目前为止,我已经为帐户编辑器模块建立了一个工作系统,我使用适当的配置参数(服务名

我正在使用BreezeJSAngularJS构建一个具有SPA结构的CRM应用程序,并且我正在使用一个动态生成的选项卡式环境来显示我在这里称为模块的内容。当用户单击侧菜单项时,将创建一个新选项卡,并将HTML模板(又称模块)加载到新内容区域。虽然某些模块类型一次只能打开一次(帐户网格),但其他模块(如帐户编辑器模块)可以打开多次。这样,用户可以在一天中的任何给定时间修改多个帐户,但总是从单个帐户网格实例进行修改

到目前为止,我已经为帐户编辑器模块建立了一个工作系统,我使用适当的配置参数(服务名称、保存选项等)创建了一个master
Breeze EntityManager
,然后在创建新的帐户编辑器时,使用masterManager.createEmptyCopy()
(根据中概述的代码):

然后,我调用一个
EntityQuery
,通过传入复制的
EntityManager
和entity
Id(key)
,以获得适当的帐户并使用Breeze的所有优点填充每个打开的编辑器

function queryAccountByKey(mgr, key) {
    var keyPredicate = breeze.Predicate.create('id', 'eq', key);
    var query = new breeze.EntityQuery('AccountsBase')
                          .expand('ContactsBase')
                          .where(keyPredicate);

    var promise = mgr.executeQuery(query)
                     .catch(queryFailed);

    return promise;

    function queryFailed(error) {
        console.log('Query Account by Id Failed', error);
        return $q.reject(error); // so downstream promise users know it failed
    }
}
似乎与其他打开的编辑器及其各自的实体管理器没有任何冲突,只要我将复制的
EntityManager
保存在模块的角度
范围内
。以这种方式编辑和保存是轻而易举的事!:p(抱歉)

我遇到的问题是当我切换到另一个角度的
路由
,例如登录屏幕,然后返回主屏幕。由于每个选项卡式模块的复杂性,在路由发生之前打开的任何帐户编辑器模块都必须从存储设置中重新加载(即使布局存储在缓存中)。但是,有两个Breeze实体管理器处理该帐户。其效果是,为单个帐户编辑器保存更改现在提交两次(或者无论您离开主布局多少次)

我试图弄清楚的是,一旦导航回主布局和原始实例引用(在我的示例中,在范围内),如何从客户端访问特定的
EntityManager
实例丢失。换句话说,我是否可以查询
EntityManager
集合以重用或删除实例?如果我可以按名称或其他Id获取manager实例,我可以简单地将其重新分配到我的范围,而无需删除和重新创建它

如果我确实需要删除
EntityManager
,我在任何地方都找不到任何东西来描述如何实现
destroy()
方法……只是一个
clear()
方法,该方法仅清除实体,不会从客户端删除
EntityManager
。当然,如果我在这方面做得完全错误,请建议更好的方法。希望我已经解释得足够清楚,有人可以提供可能的解决方案

解决方案

因此,感谢PW Kad的回答,我能够重用实体管理器,而不是删除和重新创建它们,方法是在初始化时将它们添加到$rootScope上的空对象集合中(仍然使用上面概述的
createEmptyCopy()
方法)。这允许在整个Angular应用程序中进行访问,而不会污染全局名称空间。我已经实现了一个唯一的
ID
,与每个选项卡关联,从而与内容区域中的模块关联。因此,我附加了该ID以创建一个名称,例如
'EM\'+instanceId
,用于存储在
$rootScope.entityManag>中ers
对象。稍后,我可以使用此ID在
$rootScope
中检索
EntityManager
实例,该ID可在每个帐户编辑器的角度
控制器中找到

以下是新代码:

// NEW: Add an 'entityManagers' object to the $rootScope of my main app module
angular.module('app', [])
.run(['$rootScope', function($rootScope) {
    $rootScope.entityManagers = {};
    $rootScope.entityManagers.count = 0;
}]);

// In the dataServices factory for the main app module
var serviceName = "/breeze/accounts";

var ds = new breeze.DataService({
    serviceName: serviceName,
    hasServerMetadata: true
});

var masterManager = new breeze.EntityManager({
    dataService: ds,
    saveOptions: new breeze.SaveOptions({ allowConcurrentSaves: false })
});

function createManager(instanceId) {
    // make a copy of the above EntityManager (with no cached entities)
    var sandboxManager = masterManager.createEmptyCopy();

    // NEW: Save the EntityManager instance to the $rootScope
    $rootScope.entityManagers['EM_' + instanceId] = sandboxManager;
    $rootScope.entityManagers.count++;

    return sandboxManager;
}

// In the event that you want to delete the EntityManager from the $rootScope
function deleteManager(instanceId) {
    var manager = $rootScope.entityManagers['EM_' + instanceId];
    manager.clear();
    delete $rootScope.entityManagers['EM_' + instanceId];
    $rootScope.entityManagers.count--;
}

// And lastly, inside any Angular controller
$scope.instanceId = '1234'; // Dynamically-assigned at runtime

$scope.init = function() {
    var manager = $rootScope.entityManagers['EM_' + $scope.instanceId];
    if (manager === undefined) {
       $scope.entityManager = 
              entityManagerService.createManager($scope.instanceId);
    } else {
       $scope.entityManager = manager;
    }
}

$scope.getEntityById = function(id){
    //use $scope.entityManager here to query database via Breeze
}

虽然我仍然想知道BreezeJS将其
EntityManager
集合保存在何处,但我有一个有效的解决方案。我希望它能帮助某些人!

每次更改页面时重新启动一个新的实体管理器似乎是一个糟糕的设计选择,因为实际上,您正在失去缓存机制并在页面之间共享实体,但是如果你必须这样做,你可以用一些非常简单的东西,比如-

var manager = {};

manager = manager ? manager.clear() : new breeze.EntityManager();

或者其他很多方式

但我建议不要这样做,而只是这样做-

var manager = {};

// Some route activation logic
if (!manager) {
    manager = new breeze.EntityManager();
}
编辑

简单的回答是,我不能100%确定全局命名空间中的breeze对象是如何引用实体管理器的。我认为breeze对象没有保留实体管理器的集合,但我可能错了。我不明白为什么在实体管理器上调用delete不起作用,但这应该可以完成您正在尝试的操作-

在一个闭包或全局命名空间中的某个位置创建名为EntityManager的对象。例如-

window.entityManagers = {};
window.entityManagers.count = 0;
然后在管理器被创建到该名称空间时将其固定。可能有一种更动态的方法可以做到这一点,只需提供一些伪代码-

window.entityManagers.createNewManager = function (name) {
    window.entityManagers[name] = new breeze.EntityManager();
}


window.entityManagers.createNewManager('ManagerNumber1');
然后,当您想处置某个特定实例时,只需清除它,然后将其删除。通过变量引用获取您想要的实例,或者如果由于某些疯狂的原因无法这样做,只需从受影响的实体中获取它-

window.entityManagers.deleteManager = function (name) {
    window.entityManagers[name].clear();
    delete window.entityManagers[name];
}

window.entityManagers.deleteManager('ManagerNumber1');
出于所有意图和目的,只要在其他模块/控制器中没有对该管理器实例的其他引用,那么就应该从世界上删除entityManager。我仍然不完全理解该用例,因此请使用gr
window.entityManagers.createNewManager = function (name) {
    window.entityManagers[name] = new breeze.EntityManager();
}


window.entityManagers.createNewManager('ManagerNumber1');
window.entityManagers.deleteManager = function (name) {
    window.entityManagers[name].clear();
    delete window.entityManagers[name];
}

window.entityManagers.deleteManager('ManagerNumber1');