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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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 :
1 2 3 4 5 | 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
1 2 | XmlSerializer xs = new XmlSerializer( typeof (BusinessRules), new Type[] { typeof (BRAttributeValueArgument), typeof (BRBlankArgument) , typeof (BRDomainBasedAttributeArgument)}); |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | // 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 :
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 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.