14.02.2008, 01:33 | #1 |
Злыдни
|
crmForm.IsDirty зохватит всех!
Оказывается чтение crmForm.IsDirty в crm 3 занимает кучу времени (правда эта "куча" хорошо заметна только на PIII и ниже). Долго же я "оптимизировал" вызов веб метода, перед тем как допёр, что причина тормозов в чтении IsDirty
|
|
15.02.2008, 07:32 | #2 |
CRM
|
Не могу это проверить, т.к. по-близости PIII и тем более ниже нет :-) Но этот факт кажется мне очень странным... У меня есть эта проверка кое-где, но коиенты еще не разу не жаловались на тормаза, хотя у них и PIII и ниже встречается. Хотя может быть я не прав, надо поспрашивать. Если удасться выяснить, то обязательно отпишусь.
Вдруг, это только единичный случай :-) Т.к. я не представляю, на что куча времени может тратиться. Максимум что там может происходить - проход по всем полям формы и проверка на IsDirty. Ну это JS. Хотя компы и не быстрые, но эта процедура на JS должна выполниться достаточно быстро. |
|
15.02.2008, 08:28 | #3 |
Участник
|
Извините за любопытсвто - а что это за свойство?
|
|
15.02.2008, 09:01 | #4 |
Moderator
|
2 tatra: Это проверка на то, что значение поля изменилось. Аналогичное свойство есть и у формы.
2 Черничкин Станислав: Расскажу вам одну историю. Я как-то раз писал программу, выполнение которой занимало кучу времени. Так вот чтобы убедится, что она не повисла, на длительный обработчик я повесил окошко с прогресс баром - это такой контрол, который показывает процент завершенности. Так вот, я долго оптимизировал код, а скорость работы совершенно не увеличивалась, что меня ужасно бесило. В конце концов я начал отключать модули один за другим, чтобы определить узкое место, пока не остался чистый цикл for вообще без всяких операций. Я сперва тоже долго негодовал, что это ОН , оказывается, тормозит мою программу!!! Но потом дошло, что я слишком часто перерисовываю прогресс бар и это он хавает все ресурсы. После того как я от него избавился программа стала работать мгновенно. Маловероятно, что причина в том о чем вы говорите. Контролы мелкомягких помимо текущего хранят и исходное значение (original value). Вы можите не использовать IsDirty, а проводить проверку самостоятельно.
__________________
http://fixrm.wordpress.com, снятие/наведение порчи. Быстро, дорого, гарантия. MS Certified Dirty Magic Professional Последний раз редактировалось Артем Enot Грунин; 15.02.2008 в 09:03. |
|
18.02.2008, 11:00 | #5 |
Злыдни
|
2: enot poloskun:
Если убрать SerializeIsDirty запросы на PIII начинают выполняться аж по пол секунды. Развлекайся: function _GetErrorMessage(exception) { return (exception != null) && (exception.description != null) ? exception.description : exception; } function _IsNullOrEmpty(str) { return (str == null) || (str == ""); } function _ParseBool(str) { return !_IsNullOrEmpty(str) && ((str.toLowerCase() == "true") || (str == "1")); } function _ThrowArgumentNullException(paramName) { var error = new Object(); error.description = "Значение не может быть неопределенным."; if (paramName != null) error.description += "\nИмя параметра: " + paramName; throw error; } function _ThrowInvalidOperationException(message) { var error = new Object(); error.description = message; throw error; } function ExtenderMethodInvoker(extender, methodInfo) { if (extender == null) _ThrowArgumentNullException("extender"); return (function() { try { extender.InvokeExtenderMethod(methodInfo); } catch (e) { alert(_GetErrorMessage(e)); if (event != null) event.returnValue = false; } }); } function Extender() { this.ExtensionSiteAddress = "http://crm3.local:7010/" this.ExtensionServiceNamespace = "urn:schemas-infosyst-biz:mscrm-extend"; this.CrmServiceNamespace = "http://schemas.microsoft.com/crm/2006/WebServices"; this.FormExtensionsFolder = "FormExtenders/" this.ExtensionService = this.ExtensionSiteAddress + this.FormExtensionsFolder + crmForm.ObjectTypeName + ".asmx"; this.AttributeValueNull = function(attributeNode) { if (attributeNode == null) _ThrowArgumentNullException("attributeNode"); return attributeNode.getAttribute("IsNull") == 'true'; } this.DeserializeAttribute = function(attributeNode, attributeInfo) { if (attributeNode == null) _ThrowArgumentNullException("attributeNode"); if (attributeInfo == null) _ThrowArgumentNullException("attributeInfo"); switch (attributeInfo.Type) { case "String": crmForm.all[attributeInfo.Name].DataValue = attributeNode.text; break; case "Float": case "Currency": if (this.AttributeValueNull(attributeNode)) crmForm.all[attributeInfo.Name].DataValue = null; else crmForm.all[attributeInfo.Name].DataValue = parseFloat(attributeNode.text); break; case "Integer": case "Duration": case "Picklist": case "StatusReason": case "State": if (this.AttributeValueNull(attributeNode)) crmForm.all[attributeInfo.Name].DataValue = null; else crmForm.all[attributeInfo.Name].DataValue = parseInt(attributeNode.text); break; case "Customer": case "Regarding": case "Lookup": case "PartyList": if (this.AttributeValueNull(attributeNode)) crmForm.all[attributeInfo.Name].DataValue = null; else { var lookup = new Object(); lookup.id = attributeNode.text; var typeName = attributeNode.getAttribute("type"); lookup.typename = typeName != null ? typeName : attributeInfo.ReferencedType; lookup.name = attributeNode.getAttribute("name"); var bullShit = new Array(); bullShit[0] = lookup; crmForm.all[attributeInfo.Name].DataValue = bullShit; } break; default: _ThrowInvalidOperationException("Десериализация аттрибутов типа " + attributeInfo.Type + " не поддерживается."); break; } } this.DeserializeAttributeInfos = function(node) { if (node == null) _ThrowArgumentNullException("node"); var attributeNodeList = node.selectNodes("attribute"); var attributeInfos = new Array(); for (var i = 0; i < attributeNodeList.length; i++) { var attributeNode = attributeNodeList.item(i); var attributeInfo = new Object(); attributeInfo.Name = attributeNode.getAttribute("name"); attributeInfo.ReferencedType = attributeNode.getAttribute("referencedType"); attributeInfo.Type = attributeNode.getAttribute("type"); attributeInfos[i] = attributeInfo; } return attributeInfos; } this.DeserializeEntity = function(node, attributeInfos) { if (attributeInfos == null) _ThrowArgumentNullException("attributeInfos"); if (node != null) { for (var i = 0; i < attributeInfos.length; i++) { var attributeInfo = attributeInfos[i]; try { var attributeNode = node.selectSingleNode(attributeInfo.Name); if (attributeNode != null) this.DeserializeAttribute(attributeNode, attributeInfo); } catch (e) { alert("Ошибка десериализации атрибута " + attributeInfo.Name + ": " + _GetErrorMessage(e) + "\nДесериализация будет продолжена."); } } } } this.InvokeExtenderMethod = function(methodInfo) { if (methodInfo == null) _ThrowArgumentNullException("methodInfo"); var request = '<' + methodInfo.ExtenderMethod + ' xmlns="' + this.ExtensionServiceNamespace + '" >' + '<formType>' + crmForm.FormType + '</formType>' + '<entity xmlns="' + this.CrmServiceNamespace + '">' + this.SerializeEntity(methodInfo.InAttributes) + '</entity>'; if (methodInfo.SerializeIsDirty) request += '<isDirty>' + crmForm.IsDirty + '</isDirty>'; request += '</' + methodInfo.ExtenderMethod + '>'; var message = this.WrapSoapMessage(request); var messageResponse = this.InvokeSoapMethod(this.ExtensionService, message); var response = this.ParseSoapResult(messageResponse, methodInfo.ExtenderMethod + "Response", this.ExtensionServiceNamespace); this.DeserializeEntity(response.selectSingleNode(methodInfo.ResultNodeName), methodInfo.OutAttributes); } this.InvokeSoapMethod = function(service, message) { if (service == null) _ThrowArgumentNullException("service"); if (message == null) _ThrowArgumentNullException("message"); var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP"); xmlHttpRequest.Open("POST", service, false); xmlHttpRequest.setRequestHeader("Content-Type", "application/soap+xml; charset=utf-8"); xmlHttpRequest.setRequestHeader("Content-Length", message.length); xmlHttpRequest.send(message); var responseText = xmlHttpRequest.responseText; var responseDocument = new ActiveXObject("MSXML2.DOMDocument"); if (!responseDocument.loadXML(responseText)) { var errorString = (xmlHttpRequest.status != 200 ? "Ошибка выполнения WEB запроса " + xmlHttpRequest.status + ": " + xmlHttpRequest.statusText + "\n" : "Ошибка разбора ответного сообщения.\n") + (_IsNullOrEmpty(responseText) ? "Ответное сообщение пусто." : "Ответ сервера:\n" + "---------------------------\n" + responseText + "\n---------------------------"); _ThrowInvalidOperationException(errorString); } return responseDocument; } this.ParseSoapResult = function(resultXml, exceptedElementName, exceptedElementNamespace) { if (resultXml == null) _ThrowArgumentNullException("resultXml"); var namespaces = 'xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:extend="' + this.ExtensionServiceNamespace + '"'; if (exceptedElementNamespace != null) namespaces += ' xmlns:result="' + exceptedElementNamespace + '"'; resultXml.setProperty("SelectionNamespaces", namespaces); var faultNode = resultXml.selectSingleNode("/soap:Envelope/soap:Body/soap:Fault"); if (faultNode != null) { var friendlyMessageNode = faultNode.selectSingleNode('soapetail/extend:friendlyMessage'); if (friendlyMessageNode != null) _ThrowInvalidOperationException(friendlyMessageNode.text); else { var faultCodeNode = faultNode.selectSingleNode('soap:Code'); var faultStringNode = faultNode.selectSingleNode('soap:Reason'); var faultDetailNode = faultNode.selectSingleNode('soapetail'); var errorString = "Ошибка выполнения WEB запроса.\n" + (faultCodeNode != null ? "Код ошибки: " + faultCodeNode.text + "\n" : "") + (faultStringNode != null ? "Описание: " + faultStringNode.text + "\n" : "") + (faultDetailNode != null ? "Детальная информация: \n" + "---------------------------\n" + faultDetailNode.xml + "\n---------------------------\n" : ""); _ThrowInvalidOperationException(errorString); } } if (exceptedElementName != null) { var exceptedElement = resultXml.selectSingleNode((exceptedElementNamespace != null ? "/soap:Envelope/soap:Body/result:" : "/soap:Envelope/soap:Body/") + exceptedElementName); if (exceptedElement == null) { var errorString1 = "Ошибка выполнения WEB запроса.\n" + "Корневой элемент сообщения не найден либо не соответсвует ожидаемому значению.\n" + "Ожидаемое имя корневого элемента: " + exceptedElementName + "\n" + (exceptedElementNamespace != null ? "Ожидаемое пространство имен корневого элемента: " + exceptedElementNamespace + "\n" : "") + "Текст сообщения: \n" + "---------------------------\n" + resultXml.xml + "\n---------------------------\n"; _ThrowInvalidOperationException(errorString1); } return exceptedElement; } else return resultXml.selectSingleNode("/soap:Envelope/soap:Body/*"); } this.SerializeEntity = function(attributeInfos) { if (attributeInfos == null) _ThrowArgumentNullException("attributeInfos"); var entityData = ""; for (var i = 0; i < attributeInfos.length; i++) { var attributeInfo = attributeInfos[i]; if (attributeInfo.Type == "Key") { if (!_IsNullOrEmpty(crmForm.ObjectId)) entityData += '<' + attributeInfo.Name + '>' + crmForm.ObjectId + '</' + attributeInfo.Name + '>'; } else { var dataValue = crmForm.all[attributeInfo.Name].DataValue; switch (attributeInfo.Type) { case "String": if (dataValue != null) entityData += '<' + attributeInfo.Name + '>' + dataValue + '</' + attributeInfo.Name + '>'; break; case "Integer": case "Duration": case "DateTime": case "Float": case "Currency": case "Boolean": entityData += '<' + attributeInfo.Name; if (dataValue == null) entityData += ' IsNull="true" />'; else entityData += '>' + dataValue + '</' + attributeInfo.Name + '>'; break; case "Picklist": case "StatusReason": case "State": entityData += '<' + attributeInfo.Name; if (dataValue == null) entityData += ' IsNull="true" />'; else entityData += ' name="' + crmForm.all[attributeInfo.Name].SelectedText + '">' + dataValue + '</' + attributeInfo.Name + '>'; break; case "Customer": case "Regarding": case "Lookup": case "PartyList": entityData += '<' + attributeInfo.Name; if (dataValue == null) entityData += ' IsNull="true" />'; else entityData += ' name="' + dataValue[0].name + '" type="' + dataValue[0].typename + '">' + dataValue[0].id + '</' + attributeInfo.Name + '>'; break; default: _ThrowInvalidOperationException("Сериализация аттрибутов типа " + attributeInfo.Type + " не поддерживается"); break; } } } return entityData; } this.WrapSoapMessage = function(message) { if (message == null) _ThrowArgumentNullException("message"); return '<?xml version="1.0" encoding="utf-8"?><soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"><soap12:Body>' + message + '</soap12:Body></soap12:Envelope>'; } var extenderInfoRequest = '<getExtenderInfo xmlns="' + this.ExtensionServiceNamespace + '" />'; extenderInfoRequestMessage = this.WrapSoapMessage(extenderInfoRequest); var extenderInfoResponseMessage = this.InvokeSoapMethod(this.ExtensionService, extenderInfoRequestMessage); var extenderInfoResponse = this.ParseSoapResult(extenderInfoResponseMessage, "getExtenderInfoResponse", this.ExtensionServiceNamespace); var methodNodeList = extenderInfoResponse.selectNodes("formExtenderInfo/methods/method"); for (var methodNode = methodNodeList.nextNode(); methodNode != null; methodNode = methodNodeList.nextNode()) { //TODO: добавить механизм со var methodInfo = new Object(); methodInfo.ExtenderMethod = methodNode.getAttribute("extenderMethod"); methodInfo.ResultNodeName = methodNode.getAttribute("resultNodeName"); methodInfo.SerializeIsDirty = _ParseBool(methodNode.getAttribute("serializeIsDirty")); methodInfo.TargetElement = methodNode.getAttribute("targetElement"); methodInfo.TargetEvent = methodNode.getAttribute("targetEvent"); methodInfo.TargetMethod = methodNode.getAttribute("targetMethod"); methodInfo.InAttributes = this.DeserializeAttributeInfos(methodNode.selectSingleNode("inAttributes")); methodInfo.OutAttributes = this.DeserializeAttributeInfos(methodNode.selectSingleNode("outAttributes")); var entenderMethodInvoker = new ExtenderMethodInvoker(this, methodInfo); if (!_IsNullOrEmpty(methodInfo.TargetElement)) { var targetElement = crmForm.all[methodInfo.TargetElement]; if (!_IsNullOrEmpty(methodInfo.TargetEvent)) targetElement.attachEvent(methodInfo.TargetEvent, entenderMethodInvoker); if (!_IsNullOrEmpty(methodInfo.TargetMethod)) targetElement[methodInfo.TargetMethod] = entenderMethodInvoker; } else { if (!_IsNullOrEmpty(methodInfo.TargetEvent)) if (methodInfo.TargetEvent == "onload") this.OnLoad = entenderMethodInvoker; else crmForm.attachEvent(methodInfo.TargetEvent, entenderMethodInvoker); if (!_IsNullOrEmpty(methodInfo.TargetMethod)) crmForm[methodInfo.TargetMethod] = entenderMethodInvoker; } } } var extender = new Extender(); if (extender.OnLoad != null) extender.OnLoad(); ШУТКА!!! На самом деле IsDirty вполне может подтормаживать, потому-что не факт, что он выполняется локально. Возможно он на каждой проверке лезет в базу и сравнивает поля на форме с полями в базе (такое поведение разумно, это один из паттернов оптимистической блокировки). Мне лень разбираться, я просто добавил if и скорость подскочила в несколько раз. |
|
18.02.2008, 12:27 | #6 |
Moderator
|
А я уже хотел возмущаться против таких наворотов на в скриптах формы.
Тоже думал об этом, но мне отчего-то сомнительно, что IsDirty лезет в базу. Нелогично это, что ли. Одним словом пофиг - проблема снята, все довольны.
__________________
http://fixrm.wordpress.com, снятие/наведение порчи. Быстро, дорого, гарантия. MS Certified Dirty Magic Professional |
|
18.02.2008, 14:57 | #7 |
Злыдни
|
Вот смотри: В 16.00 я открываю форму организации "Рога и Копыта" и начинаю в нее смотреть. В 16.05 ты открываешь ту же форму, меняешь значение поля "название" на "Копыта и Рога" и в 16.06 сохраняешь ее. В 16.10 я решаю, что с карточкой все в порядке и жму на кнопку "сохранить" (вот она, проверка IsDirty). Вопрос: если я снова открою форму в 16.30 и увижу там "Копыта и Рога" и дату последнего изменения 16.06, при том, что я точно буду знать, что в 16.10 там было "Рога и Копыта" и я нажал кнопку "сохранить", как внедренец мне объяснит "пачему название не сохраняецо?". Вывод: IsDirty должна сравнивать значение на форме со значением в базе.З.Ы. java скрипт с формы, только никому об этом не рассказывай
|
|
18.02.2008, 15:25 | #8 |
CRM
|
Провел с коллегой эксперимент
Ну что я могу сказать по этому поводу... Для чистоты экспериментов даже открыл Fiddler, чтобы отлавливать все запросы-ответы сервера. В общем, я убедился, что при сохранении нет запроса к БД как такового. Выяснил (ну как мне кажется), что при сохранении формы, идёт проверка на то какие поля менялись (у поля тоже есть свойство isDirty), если значение поля менялось, то это значение и записывается в БД (это всё на стороне сервера, это же АСП ;-) ). Поэтому ваш случай в рамках этого подхода Если бы оба меняли название, то в БД сохранилось бы последнее введеное А так при при нажатии на "Сохранить" в БД записываются только измененные значения полей. |
|
18.02.2008, 16:20 | #9 |
Moderator
|
Что и требовалось доказать! Осталось только понять фигли оно тормозит? Лично у меня нет - проверял.
__________________
http://fixrm.wordpress.com, снятие/наведение порчи. Быстро, дорого, гарантия. MS Certified Dirty Magic Professional |
|
18.02.2008, 17:53 | #10 |
Злыдни
|
2 Енот Полоскун
Запусти этот скрипт и почувствуй гнев праведных (не советую запускать на машине ниже Core 2, гнев быдет невыносимым var d = new Date(); var donkey = ""; for (var i = 0; i < 100; i++) { if (crmForm.IsDirty) donkey += "donkey"; } alert(new Date() - d); d = new Date(); var jennet = ""; for (var i = 0; i < 100; i++) { if (i == i) jennet += "jennet"; } alert(new Date() - d); честно говоря а очень ли оно интересно, почему тормозит? все равно ускорить не сможем. лично я не хочу тратить время на выяснение причины. |
|
19.02.2008, 08:10 | #11 |
CRM
|
Запустил. Выдало на:
1. Core 2 - 500 и 0 2. на Р-4 2ГГц - 1650 и 0 Тут задержка опять же ясна. Только зачем по 100 раз проверять форму? Да, на проверку тратится время, по моим меркам, не смертельное. Вот насчет ускорить не можем... Проблему надо локализовывать. У нас тоже были проблемы: пользователи жаловались что у них всё медленно работает. Пеняли на сервер и обработки на форме, оказалось что дело в сети :-) Сеть починили, всё залетало. "лично я не хочу тратить время на выяснение причины" - ваше право |
|
|
Похожие темы | ||||
Тема | Ответов | |||
Сохраняется ли форма когда IsDirty == false? | 2 | |||
Ошибка на всех страничках | 5 | |||
Поменял подразделение у всех пользователей в CRM | 2 |
Опции темы | Поиск в этой теме |
Опции просмотра | |
|