Javascript 从现有实体创建实体

Javascript 从现有实体创建实体,javascript,breeze,Javascript,Breeze,几天来我一直在头痛 假设您有一个汽车销售管理应用程序。你们卖不同的型号。你的车型有50个属性。举个例子,假设你想卖掉布加迪威龙。现在,你刚刚收到了其中的5辆车。所以,我登录到我的应用程序,创建第一个具有特定ID的布加迪威龙。然后我想添加第二个,但有一个问题-我必须再次写下所有这些属性!我想要一个复制按钮,我只需要更改序列号,breeze会更改ID,瞧,里面有两辆车 出于黑客的考虑,我首先创建了以下解决方案: newCar(datacontext.createCar()); newCar().pr

几天来我一直在头痛

假设您有一个汽车销售管理应用程序。你们卖不同的型号。你的车型有50个属性。举个例子,假设你想卖掉布加迪威龙。现在,你刚刚收到了其中的5辆车。所以,我登录到我的应用程序,创建第一个具有特定ID的布加迪威龙。然后我想添加第二个,但有一个问题-我必须再次写下所有这些属性!我想要一个复制按钮,我只需要更改序列号,breeze会更改ID,瞧,里面有两辆车

出于黑客的考虑,我首先创建了以下解决方案:

newCar(datacontext.createCar());
newCar().property1(oldCar().property1());
newCar().property2(oldCar().property2());
...
这是丑陋的,在我证明我能做到之后,当然,申请的要求是让所有的东西都可以复制——我不可能这么做!一定有副本。在挖掘了很多东西之后,甚至试图改变微风本身的一些东西,我都不能做如下事情:

manager.createEntity('Car', oldCar);
现在,最新的解决方案比第一个解决方案更可行,但仍然需要比我想要的更多的代码,并且不像第一个解决方案那样直观:

        var newObject = {};
        manager.metadataStore._structuralTypeMap[oldCar.entityType.name].dataProperties.forEach(function (singleProperty) {
                if (!singleProperty.isPartOfKey)
                newObject[singleProperty.name] = oldCar[singleProperty.name];
            });
        var newCar = manager.createEntity('Equipment', newObject);
有没有其他“更干净”的方法来创建一个属性完全相同但id不同的新实体


我应该提到,Car entity中有一些ICollections,但是这个黑客解决方案忽略了这些可以改进的ICollections,但目前我自己用一些.forEach循环来处理这些问题。

我们正在后面的房间里处理这样的事情。准备好了我们会通知你的。没有承诺,也没有时机

与此同时,我试了一下。我决定利用Breeze方法知道如何克隆实体这一事实。如果您阅读了该方法的breeze源代码,您就会知道这很棘手

这就是我的想法(作为一名平民,而不是微风开发人员):

与您的一样,它保留了外键属性,这意味着如果源具有这样的值,则父实体的引用导航属性将具有从缓存中提取的值

与您的一样,不会填充集合导航属性。这种方法不知道如何克隆子对象。这也不是不言而喻的。那是你的额外学分

2013年12月15日更新 由于您的要求,我已经实现了克隆子项(集合导航)的功能。我遵循了您建议的语法,因此用法如下:

cloneItem(something, ['collectionProp1', 'collectionProp2']); 
请注意,我再次依赖微风导出来完成繁重的工作

警告:此代码非常脆弱,无法推广到所有型号


当Breeze团队进行此项工作时,如果有人在完成此项工作之前需要此项工作,下面是我为复制对象及其导航属性而编写的代码:

    function createSimpleObject(heavyObject) {
        if (heavyObject === undefined) return {};
        var simpleObject = {};
        manager.metadataStore._structuralTypeMap[heavyObject.entityType.name].dataProperties.forEach(function (singleProperty) {
            if (!singleProperty.isPartOfKey)
                simpleObject[singleProperty.name] = heavyObject[singleProperty.name]();
        });
        return simpleObject;
    }

    function makeNavigationProperties(newObject, oldObject, navigationProperties) {
        if (oldObject === undefined || navigationProperties === undefined) return {};
        navigationProperties.forEach(function (singleNavigationProperty) {
            var selectedArray = [];
            if (ko.isObservable(oldObject[singleNavigationProperty])) {
                selectedArray = oldObject[singleNavigationProperty]();
            }
            else selectedArray = oldObject[singleNavigationProperty];
            if (selectedArray) {
                selectedArray.forEach(function (singleObject) {
                    var simpleObject = {};
                    manager.metadataStore._structuralTypeMap[singleObject.entityType.name].dataProperties.forEach(function (singleProperty) {
                        if (!singleProperty.isPartOfKey) {
                            if (singleProperty.relatedNavigationProperty) {
                                if (singleProperty.relatedNavigationProperty.entityTypeName === oldObject.entityType.name) {
                                    simpleObject[singleProperty.name] = newObject.id();
                                }
                                else {
                                    if (ko.isObservable(singleObject[singleProperty.name]))
                                        simpleObject[singleProperty.name] = singleObject[singleProperty.name]();
                                    else simpleObject[singleProperty.name] = singleObject[singleProperty.name];
                                }
                            }
                            else {
                                if (ko.isObservable(singleObject[singleProperty.name]))
                                    simpleObject[singleProperty.name] = singleObject[singleProperty.name]();
                                else simpleObject[singleProperty.name] = singleObject[singleProperty.name];
                            }
                        }
                    });
                    manager.createEntity(singleObject.entityType.shortName, simpleObject);
                });
            }
        });
    }
CloneEntity: function (originalEntity) {
    // NoRyb's change
    function recursiveFixEntity(entity) {
        if (entity && (typeof entity === 'object')) {
            delete entity.complexAspect;
            for (var propertyName in entity) {
                recursiveFixEntity(entity[propertyName]);
            }
        }
    };

    var manager = originalEntity.entityAspect.entityManager;
    // export w/o metadata and then parse the exported string.
    var exported = manager.exportEntities([originalEntity], { asString: false, includeMetadata: false });
    // extract the entity from the export
    var type = originalEntity.entityType;
    var copy = exported.entityGroupMap[type.name].entities[0];
    // remove the entityAspect
    delete copy.entityAspect;
    // remove the key properties
    type.keyProperties.forEach(function (p) { delete copy[p.name]; });

    // NoRyb's change:
    recursiveFixEntity(copy);

    // the "copy" provides the initial values for the create
    return manager.createEntity(type, copy);
}
下面是创建对象的方法:

function createMyObject(originalObject, navigationProperties){
    var newMyObject = manager.createEntity('MyObject', createSimpleObject(originalObject));
    makeNavigationProperties(newMyObject, originalObject, navigationProperties);
    return newMyObject;
}
最后是调用创建新对象的代码:

copiedObject(datacontext.createMyNewObject(originalObject(), ['navigationProperty1', 'navigationProperty2', 'navigationProperty3']));
其中copiedObject是可观察的,包含新对象,originalObject是我要复制的对象,第二个参数包含我要复制的属性。它只适用于原始对象的直接子对象,我不需要子对象的子对象,因此缺少。它可以与参数一起使用,也可以不与参数一起使用,因此我使用完全相同的函数来创建空对象或复制没有子对象的实体

编辑

沃兹代码工作完美!我唯一要更改的是他的
fk
检测,并使用以下命令来启用复制连接表:

var fk = false;
navProp.entityType.foreignKeyProperties.forEach(function (singleProperty) {
if (singleProperty.relatedNavigationProperty.entityTypeName == newItem.entityType.name)
    fk = singleProperty.name;
});
if (!fk) return;

接受答案并扩展它以允许深入的属性链接。 示例用法是

cloneEntity(someEntity, ['collectionProp1.subCollection.another', 'collectionProp2']); 
注意,这仍然没有经过测试,仅适用于某些型号

function cloneEntity(item, collectionNames) {
    var manager = item.entityAspect.entityManager;

    // export w/o metadata and then parse the exported string.
    var exported = JSON.parse(manager.exportEntities([item], false));

    // extract the entity from the export
    var type = item.entityType;
    var copy = exported.entityGroupMap[type.name].entities[0];

    // remove the entityAspect (todo: remove complexAspect from nested complex types)
    delete copy.entityAspect;

    // remove the key properties (assumes key is store-generated)
    type.keyProperties.forEach(function (p) { delete copy[p.name]; });

    // the "copy" provides the initial values for the create
    var newItem = manager.createEntity(type, copy);

    if (collectionNames && collectionNames.length) {
        // can only handle parent w/ single PK values
        var keyValue = newItem.entityAspect.getKey().values[0];
        collectionNames.forEach(function (propertyString) { copyChildren(item, propertyString, keyValue); });
    }
    return newItem;

    function copyChildren(parentItem, navPropString, parentKeyValue) {

        var navPropName;
        // todo: add much more error handling
        var parentType = parentItem.entityType;

        //parse deep properties
        if (navPropString.indexOf('.') >= 0) {
            navPropName = navPropString.substr(0, navPropString.indexOf('.'));
            navPropString = navPropString.substr(navPropString.indexOf('.') + 1);
        } else {
            navPropName = navPropString;
            navPropString = "";
        }

        var navProp = parentType.getNavigationProperty(navPropName);

        if (navProp.isScalar) return; // only copies collection navigations. Todo: should it throw?

        // This method only copies children (dependent entities), not a related parent
        // Child (dependent) navigations have inverse FK names, not FK names
        var fk = navProp.invForeignKeyNames[0]; // can only handle child w/ single FK value
        if (!fk) return;

        // Breeze `getProperty` gets values for all model libraries, e.g. both KO and Angular
        var children = parentItem.getProperty(navPropName);
        if (children.length === 0) return;

        // Copy all children
        var childType = navProp.entityType;
        var copies = JSON.parse(manager.exportEntities(children, false)).entityGroupMap[childType.name].entities;

        copies.forEach(function (c) {

            //Get the original childid for deeper copy
            var originalChildId = c.id;

            delete c.entityAspect;

            // remove key properties (assumes keys are store generated)
            childType.keyProperties.forEach(function (p) { delete c[p.name]; });

            // set the FK parent of the copy to the new item's PK               
            c[fk] = parentKeyValue;

            // merely creating them will cause Breeze to add them to the parent
            var childItem = manager.createEntity(childType, c);

            if (navPropString.length > 0) {
                //Copy children

                var originalChild = $.grep(children, function (a) {
                    return a.id() == originalChildId;
                })[0];

                var childKeyValue = childItem.entityAspect.getKey().values[0];
                copyChildren(originalChild, navPropString, childKeyValue);
            }
        });
    }
};

我的导航属性有子类型,在创建对象时手动创建。使用Ward的cloneItem()引发错误,因为

children.entityGroupMap没有childType.name条目

下面是我在copyChildren()的最后一部分中针对这种情况的解决方案:


我使用了Ward的原始代码(没有子克隆部分),并向其中添加了以下代码,以便它递归地删除ComplexSpect(我有一个地理属性,它由两个嵌套的复杂属性组成):


另外,我自己也没有解析exportEntities中的JSON,而是使用了这个选项(我想这是后来添加的)。

我不确定克隆本身是否存在,谢谢。关于克隆子对象-我实际上成功地为第一层子对象获得了它(它不是递归的,因为我不需要它,所以甚至没有尝试过),但它足够灵活,所以它需要第二个参数,即表示我只想复制的子对象的字符串数组。我会在当天晚些时候在我的问题中发布一个编辑,如果能得到一个关于它的评论就好了,或者它可能会帮助其他人。只是一个快速更新-我最终使用建议的连接表修复方案(代码在我的答案底部)更改为您的解决方案。它工作得很好,并且没有内部变量,当breeze团队更改它们时,这些变量可能会中断。您应该更改fk的定义,
navProp.invForeignKeyNames[0]
为我提供导航属性的ID,而不是外键。再次感谢您!很高兴你取得了进步。我不理解您的评论“
navProp.invForeignKeyNames[0]
为我提供了导航属性的ID而不是外键”。我要找的是子实体上指向父实体的FK。考虑<代码>供应商。产品< /代码>。代码>产品处于从属角色,并将FK返回到供应商。该属性的名称可以是任何名称,但通常不是父实体的名称+“ID”。假设它是“SupID”。如果
navProp
Supplier.Products
属性的元数据,
navProp.invForeignKeyNames[0]
将是“SupID”。答对了。事实上,我从
navProp.invForeignKeyNames[0]
获得
id
。我应该得到类似于
supId
的东西,所以我有点惊讶我们得到了不同的东西。儿童财产确实有
function cloneEntity(item, collectionNames) {
    var manager = item.entityAspect.entityManager;

    // export w/o metadata and then parse the exported string.
    var exported = JSON.parse(manager.exportEntities([item], false));

    // extract the entity from the export
    var type = item.entityType;
    var copy = exported.entityGroupMap[type.name].entities[0];

    // remove the entityAspect (todo: remove complexAspect from nested complex types)
    delete copy.entityAspect;

    // remove the key properties (assumes key is store-generated)
    type.keyProperties.forEach(function (p) { delete copy[p.name]; });

    // the "copy" provides the initial values for the create
    var newItem = manager.createEntity(type, copy);

    if (collectionNames && collectionNames.length) {
        // can only handle parent w/ single PK values
        var keyValue = newItem.entityAspect.getKey().values[0];
        collectionNames.forEach(function (propertyString) { copyChildren(item, propertyString, keyValue); });
    }
    return newItem;

    function copyChildren(parentItem, navPropString, parentKeyValue) {

        var navPropName;
        // todo: add much more error handling
        var parentType = parentItem.entityType;

        //parse deep properties
        if (navPropString.indexOf('.') >= 0) {
            navPropName = navPropString.substr(0, navPropString.indexOf('.'));
            navPropString = navPropString.substr(navPropString.indexOf('.') + 1);
        } else {
            navPropName = navPropString;
            navPropString = "";
        }

        var navProp = parentType.getNavigationProperty(navPropName);

        if (navProp.isScalar) return; // only copies collection navigations. Todo: should it throw?

        // This method only copies children (dependent entities), not a related parent
        // Child (dependent) navigations have inverse FK names, not FK names
        var fk = navProp.invForeignKeyNames[0]; // can only handle child w/ single FK value
        if (!fk) return;

        // Breeze `getProperty` gets values for all model libraries, e.g. both KO and Angular
        var children = parentItem.getProperty(navPropName);
        if (children.length === 0) return;

        // Copy all children
        var childType = navProp.entityType;
        var copies = JSON.parse(manager.exportEntities(children, false)).entityGroupMap[childType.name].entities;

        copies.forEach(function (c) {

            //Get the original childid for deeper copy
            var originalChildId = c.id;

            delete c.entityAspect;

            // remove key properties (assumes keys are store generated)
            childType.keyProperties.forEach(function (p) { delete c[p.name]; });

            // set the FK parent of the copy to the new item's PK               
            c[fk] = parentKeyValue;

            // merely creating them will cause Breeze to add them to the parent
            var childItem = manager.createEntity(childType, c);

            if (navPropString.length > 0) {
                //Copy children

                var originalChild = $.grep(children, function (a) {
                    return a.id() == originalChildId;
                })[0];

                var childKeyValue = childItem.entityAspect.getKey().values[0];
                copyChildren(originalChild, navPropString, childKeyValue);
            }
        });
    }
};
            .....
        // Copy all children
        var childType = navProp.entityType;
        children = JSON.parse(manager.exportEntities(children, false));

        var copies;
        if (children.entityGroupMap.hasOwnProperty(childType.name)) {
            copies = children.entityGroupMap[childType.name].entities;
            copyChildrenOfType(copies, childType);
        }
        else {
            childType.subtypes.forEach(function (subtype) {
                if (children.entityGroupMap.hasOwnProperty(subtype.name)) {
                    copies = children.entityGroupMap[subtype.name].entities;
                    copyChildrenOfType(copies, subtype);
                }
            });
        }
        function copyChildrenOfType(copies, childType) {
            copies.forEach(function (c) {
                delete c.entityAspect;
                // remove key properties (assumes keys are store generated)
                childType.keyProperties.forEach(function (p) { delete c[p.name]; });
                // set the FK parent of the copy to the new item's PK               
                c[fk] = parentKeyValue;
                // merely creating them will cause Breeze to add them to the parent
                manager.createEntity(childType, c);
            });
        }
CloneEntity: function (originalEntity) {
    // NoRyb's change
    function recursiveFixEntity(entity) {
        if (entity && (typeof entity === 'object')) {
            delete entity.complexAspect;
            for (var propertyName in entity) {
                recursiveFixEntity(entity[propertyName]);
            }
        }
    };

    var manager = originalEntity.entityAspect.entityManager;
    // export w/o metadata and then parse the exported string.
    var exported = manager.exportEntities([originalEntity], { asString: false, includeMetadata: false });
    // extract the entity from the export
    var type = originalEntity.entityType;
    var copy = exported.entityGroupMap[type.name].entities[0];
    // remove the entityAspect
    delete copy.entityAspect;
    // remove the key properties
    type.keyProperties.forEach(function (p) { delete copy[p.name]; });

    // NoRyb's change:
    recursiveFixEntity(copy);

    // the "copy" provides the initial values for the create
    return manager.createEntity(type, copy);
}