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.