updated article (Oct 2011)
(code refactored)
Hi All,
One other missing feature of MDS is Import / Export / Copy Business rules from a model to another (or copy business rules to the same model)
my code is still in beta, but it is already (partially) working.
you can find it on my « MDS Manager » tool on codeplex:
http://mdsmanager.codeplex.com
Get business rules from modelId, quite easy with service client method « business rules get », returns a business rule set
public BusinessRules GetBusinessRules(string fileName) { BusinessRules brs = null; Stream str = null; try { if (File.Exists(fileName)) { str = new FileStream(@fileName, FileMode.Open, FileAccess.Read, FileShare.Read); if (str == null) { throw new BusinessRulesException("error while opening file!"); } XmlSerializer xs = new XmlSerializer(typeof(BusinessRules)); // Load the object saved above by using the Deserialize function brs = (BusinessRules)xs.Deserialize(str); } else { throw new BusinessRulesException("Cannot find specified file!"); } return brs; } catch (Exception ex) { throw ex; } finally { // Cleanup if (str != null) str.Close(); } }
then we export business rules to a XML file
note:
//this is needed to avoid a serialization error (more info on http://www.johnsoer.com/blog/?p=125 ) :
//XmlSerializer xs = new XmlSerializer(typeof(BusinessRules), new Type[] { typeof(BRAttributeValueArgument),typeof(BRBlankArgument) // ,typeof(BRDomainBasedAttributeArgument)});
here is the export method :
public void ExportBusinessRules(string exportFileName, BusinessRules brs) { TextWriter WriteFileStream = null; try {
//new type needed to avoir error because serialization of an inherited class XmlSerializer xs = new XmlSerializer(typeof(BusinessRules), new Type[] { typeof(Common.ServiceReference1.BRAttributeValueArgument) });
//update oct 2011!
//new type needed to avoir error because serialization of an inherited class
XmlSerializer xs = new XmlSerializer(typeof(BusinessRules), new Type[] { typeof(BRAttributeValueArgument),typeof(BRBlankArgument) ,typeof(BRDomainBasedAttributeArgument)});
// Create a new file stream to write the serialized object to a file WriteFileStream = new StreamWriter(@exportFileName); xs.Serialize(WriteFileStream, brs); //custom exception class , but not required throw new BusinessRulesException("Business Rules successfully exported to file " + exportFileName); } catch (Exception exc) { throw exc; } finally { // Cleanup if (WriteFileStream != null) WriteFileStream.Close(); } }
then we would like to import this XML file to another model:
here is the method to get business rules set from a previously exported XML file :
public BusinessRules GetBusinessRules(string fileName) { BusinessRules brs = null; Stream str = null; try { if (File.Exists(fileName)) { str = new FileStream(@fileName, FileMode.Open, FileAccess.Read, FileShare.Read); if (str == null) { throw new BusinessRulesException("error while opening file!"); } XmlSerializer xs = new XmlSerializer(typeof(BusinessRules)); // Load the object saved above by using the Deserialize function brs = (BusinessRules)xs.Deserialize(str); } else { throw new BusinessRulesException("Cannot find specified file!"); } return brs; } catch (Exception ex) { throw ex; } finally { // Cleanup if (str != null) str.Close(); } }
//then we can get the source modelID
sourceModelId =brs.BusinessRulesMember.First().Identifier.ModelId as Identifier;
// and then clone business rule set with updated modelId:
//brs is the business rule set
//sourceModelId is the source Model Identifier (taken above from the Rules set from the XML file)
//target ModelId is the model Identifier for the model we want to import back the B. rules to
CloneBusinessRules(brs,sourceModelId, targetModelId));
Here is the CloneBusinessRules Method:
public string CloneBusinessRules(BusinessRules brs, Identifier SourceModelId, Identifier targetModelId) { OperationResult or = null; //instanciate new BR members try { int cptMissingEntity = 0; StringBuilder sb = new StringBuilder("these business rule related entities does not exists in target model:"); sb.AppendLine(); BusinessRules newBRS = new BusinessRules(); foreach (BusinessRule br in brs.BusinessRulesMember) { Identifier EntityId = null; //this is used to get the target entity Id with matching its name using (ServiceClient c = new ServiceClientWrapper().CreateServiceClient()) { EntityId = GetEntityIdFromName(c, c.Endpoint.Address.Uri.OriginalString, targetModelId, br.Identifier.EntityId.Name); } if (EntityId != null) { newBRS.BusinessRulesMember = new Collection<BusinessRule>(); //create a new business rule based on the source one, and applying on it the target identifiers and names BusinessRule newBR = BusinessRuleInstanciate(Guid.NewGuid(), br.Identifier.Name, targetModelId.Name, br.Identifier.EntityId.Name, br.Identifier.MemberType, br.Priority, br.BRActions, br.Identifier.InternalId, br.RuleActionText, br.RuleConditionText); //some required parameters to instanciate below: //instanciate BRActions newBRS.BRActions = BRActionsInstanciate(brs.BRActions, newBR.Identifier); //instanciate BRConditions newBRS.BRConditions = BRConditionsInstanciate(brs.BRConditions, newBR.Identifier); //instanciate BRConditionTreeNodes newBRS.BRConditionTreeNodes = BRConditionTreeNodesInstanciate(brs.BRConditionTreeNodes, newBR.Identifier); newBRS.BusinessRulesMember.Add(newBR); } else { sb.AppendLine(br.Identifier.EntityId.Name); cptMissingEntity++; //these business rule related entities do not exist in target model } //then we clone modified BRSet using (ServiceClient c = new ServiceClientWrapper().CreateServiceClient()) { or = c.BusinessRulesClone(new International(), newBRS); string err = Tools.HandleErrors(or); if (!string.IsNullOrEmpty(err)) { throw new Exception(err); } } if (cptMissingEntity == 0) { sb = new StringBuilder(); } } return sb.ToString(); } catch (Exception exc) { throw exc; } }
update oct 2011 code for BRActionsInstanciate, BRConditionsInstanciate,BRConditionTreeNodesInstanciate
private Collection<BRConditionTreeNode> BRConditionTreeNodesInstanciate(Collection<BRConditionTreeNode> BRConditionTreeNodes, MemberTypeContextIdentifier BRId) { try { //Add BRConditionTreeNode to BRConditionTreeNode Collection Collection<BRConditionTreeNode> newBRConditionTreeNodes = new Collection<BRConditionTreeNode>(); foreach (BRConditionTreeNode brctn in BRConditionTreeNodes) { BRConditionTreeNode BRConditionTreeNodeItem = new BRConditionTreeNode(); //Add Attribute and Action type to the collection BRConditionTreeNodeItem.BRConditions = BRConditionsInstanciate(brctn.BRConditions, BRId); BRConditionTreeNodeItem.BusinessRuleId = BRId; BRConditionTreeNodeItem.ConditionTreeChildNodes = brctn.ConditionTreeChildNodes; BRConditionTreeNodeItem.ConditionTreeParentNode = brctn.ConditionTreeParentNode; BRConditionTreeNodeItem.LogicalOperator = brctn.LogicalOperator; BRConditionTreeNodeItem.Identifier = new Identifier() { Name = brctn.Identifier.Name, Id = Guid.NewGuid() }; //All Action and Condition Items must have a sequence greater that zero BRConditionTreeNodeItem.Sequence = brctn.Sequence; newBRConditionTreeNodes.Add(BRConditionTreeNodeItem); } return newBRConditionTreeNodes; } catch (Exception exc) { throw exc; } } private Collection<BRAction> BRActionsInstanciate(Collection<BRAction> BRActions, MemberTypeContextIdentifier BRId) { try { //Add Action to Action Collection Collection<BRAction> newBRActions = new Collection<BRAction>(); foreach (BRAction bra in BRActions) { BRAction BRActionItem = new BRAction() { Identifier = new Identifier() { Id = Guid.NewGuid(), Name = bra.Identifier.Name }, BusinessRuleId = BRId }; //Add Attribute and Action type to the collection BRActionItem.PrefixArgument = new BRAttributeArgument(); BRActionItem.PrefixArgument.PropertyName = bra.PrefixArgument.PropertyName; BRActionItem.PrefixArgument.AttributeId = new Identifier(); BRActionItem.PrefixArgument.AttributeId.Id = Guid.NewGuid(); BRActionItem.PrefixArgument.AttributeId.Name = bra.PrefixArgument.AttributeId.Name; BRActionItem.Operator = bra.Operator; var colBRffa = new Collection<object>(); foreach (var brffa in bra.PostfixArguments) { BRBlankArgument BRba = brffa as BRBlankArgument; BRFreeformArgument BRffa = brffa as BRFreeformArgument; if (BRba != null) { colBRffa.Add(new BRBlankArgument() { Identifier = new Identifier() { Id = Guid.NewGuid(), Name = BRba.Identifier.Name }, PropertyName = BRba.PropertyName }); } else { if (BRffa != null) { colBRffa.Add(new BRFreeformArgument() { Identifier = new Identifier() { Id = Guid.NewGuid(), Name = BRffa.Identifier.Name }, Value = BRffa.Value, PropertyName = BRffa.PropertyName }); } } } BRActionItem.PostfixArguments = colBRffa; BRActionItem.Text = bra.Text; //All Action and Condition Items must have a sequence greater that zero BRActionItem.Sequence = bra.Sequence; newBRActions.Add(BRActionItem); } return newBRActions; } catch (Exception exc) { throw exc; } } public Collection<BRCondition> BRConditionsInstanciate(Collection<BRCondition> BRconditions, MemberTypeContextIdentifier BRId) { try { //Add Action to Action Collection Collection<BRCondition> newBRconditions = new Collection<BRCondition>(); BRCondition BRConditionItem = new BRCondition(); foreach (BRCondition brc in BRconditions) { //Add Attribute and Action type to the collection BRConditionItem.BusinessRuleId = BRId; BRConditionItem.ConditionTreeNodeId = new Identifier() { Name = brc.ConditionTreeNodeId.Name }; BRConditionItem.Identifier = new Identifier() { Id = Guid.NewGuid(), Name = brc.Identifier.Name }; BRConditionItem.Operator = brc.Operator; BRConditionItem.PrefixArgument = brc.PrefixArgument; BRConditionItem.PostfixArguments = brc.PostfixArguments; //All Action and Condition Items must have a sequence greater that zero BRConditionItem.Sequence = brc.Sequence; BRConditionItem.Text = brc.Text; newBRconditions.Add(BRConditionItem); } return newBRconditions; } catch (Exception exc) { throw exc; } }
Posted a few weeks ago some methods that could help.
In my experience attempt to save a business rule with all properties and collections failed. I had to create business rule, could think of it as header. This is how you would create business rule in UI, too. And than add one section at a time actions and conditions. I have also added an initial call to get all entities interested in, cache those and lookup in collection using link, which speeds up creating business rules quite a bit. Please let me know how I can contact you, if you are interested. Thank you, Miron.
Great work, this will come in very useful for business rule changes that need to be migrated between environments.