/// <summary>Reloads encapsulated Entity from database</summary> /// <param name="container"></param> /// <param name="entity"></param> /// <param name="columns">Set of colums with which entity should be reloaded</param> /// <remarks>ToStringWithEntityName() is replaced with entity.LogicalName</remarks> public static Entity Reload(this IExecutionContainer container, Entity entity, ColumnSet columns) { container.StartSection($@"{MethodBase.GetCurrentMethod().DeclaringType.Name}\{MethodBase.GetCurrentMethod().Name}"); container.StartSection($"Reloading {container.Entity(entity).ToString()}."); foreach (var attr in entity.Attributes.Keys) { if (attr.Contains('.')) { throw new InvalidPluginExecutionException($"Cannot reload entity {entity.LogicalName} with aliased attributes ({attr})"); } } if (columns == null) { columns = new ColumnSet(); } if (!columns.Columns.Any() && !columns.AllColumns) { foreach (var attr in entity.Attributes.Keys) { columns.AddColumn(attr); } } entity = container.Retrieve(entity.ToEntityReference(), columns); container.EndSection(); return(entity); }
/// <summary> /// Converts QueryExpression to FetchXml /// </summary> /// <param name="container"></param> /// <param name="query"></param> /// <returns></returns> public static string Convert(this IExecutionContainer container, QueryExpression query) { container.StartSection($@"{MethodBase.GetCurrentMethod().DeclaringType.Name}\{MethodBase.GetCurrentMethod().Name}"); try { var request = new QueryExpressionToFetchXmlRequest() { Query = query }; var response = (QueryExpressionToFetchXmlResponse)container?.Service?.Execute(request); if (response != null) { container.Log("Query was converted successfully."); } else { container.Log("It was an issue converting query."); } return(response?.FetchXml); } finally { container.EndSection(); } }
/// <summary> /// Export entities from CRM to dictionary of blocks with entities /// </summary> /// <returns>Blocks with exported entities</returns> public ShuffleBlocks ExportFromCRM(IExecutionContainer container) { container.StartSection("ExportFromCRM"); if (definition == null) { throw new ArgumentNullException("Definition", "Shuffle definition must be specified to export data"); } var blocks = new ShuffleBlocks(); ExistingSolutionVersions = null; if (ShuffleDefinition.Blocks.Items.Any(b => (b is DataBlock data && data.Export != null) || b is SolutionBlock sol && sol.Export != null)) { stoponerror = ShuffleDefinition.StopOnError; timeout = ShuffleDefinition.TimeoutSpecified ? ShuffleDefinition.Timeout : -1; //double savedtimeout = -1; //if (timeout > -1) //{ // savedtimeout = SetTimeout(); //} var totalBlocks = ShuffleDefinition.Blocks.Items.Length; var currentBlock = 0; foreach (var block in ShuffleDefinition.Blocks.Items) { currentBlock++; SendStatus(totalBlocks, currentBlock, -1, -1); if (block is DataBlock datablock) { var cExported = ExportDataBlock(container, blocks, datablock); var name = datablock.Name; if (cExported != null) { if (blocks.ContainsKey(name)) { SendLine(container, $"Block already added: {name}"); } else { blocks.Add(name, cExported); } } } else if (block is SolutionBlock solutionblock) { if (ExistingSolutionVersions == null) { GetCurrentVersions(container); } ExportSolutionBlock(container, solutionblock); } } SendStatus(0, 0, 0, 0); //if (savedtimeout > -1) //{ // ResetTimeout(container, savedtimeout); //} } container.EndSection(); return(blocks); }
/// <summary>Returns a list of file names that are required for the given ShuffleDefinition</summary> /// <param name="container"></param> /// <param name="shuffleDefinition">ShuffleDefinition file</param> /// <param name="definitionpath"></param> /// <returns>List of files</returns> public static List <string> GetReferencedFiles(IExecutionContainer container, string shuffleDefinition, string definitionpath) { container.StartSection(MethodBase.GetCurrentMethod().Name); var result = new List <string>(); if (File.Exists(shuffleDefinition)) { var definition = GetShuffleDefinition(shuffleDefinition, true); if (DataFileRequired(definition)) { var datafile = Path.ChangeExtension(shuffleDefinition, ".data.xml"); container.Log($"Adding data file: {datafile}"); result.Add(datafile); } foreach (var solBlock in definition.Blocks.Items.Where(b => b is SolutionBlock)) { var solFile = GetSolutionFilename((SolutionBlock)solBlock, definitionpath); if (!result.Contains(solFile)) { container.Log($"Adding solution file: {solFile}"); result.Add(solFile); } } } else { container.Log("Definition file not found"); } container.Log($"Returning {result.Count} files"); container.EndSection(); return(result); }
/// <summary> /// Serialize blocks with entities with given serialization type /// </summary> /// <param name="container"></param> /// <param name="blocks"></param> /// <param name="type"></param> /// <param name="delimeter">Optional, only required for SerializationType: Text</param> /// <returns></returns> public XmlDocument Serialize(IExecutionContainer container, ShuffleBlocks blocks, SerializationType type, char delimeter) { container.StartSection("Serialize"); XmlDocument xml = null; if (blocks.Count > 0) { SendLine(container, "Serializing {0} blocks with type {1}", blocks.Count, type); xml = new XmlDocument(); XmlNode root = xml.CreateElement("ShuffleData"); xml.AppendChild(root); XML.AppendAttribute(root, "Type", type.ToString()); XML.AppendAttribute(root, "ExportTime", DateTime.Now.ToString("s")); switch (type) { case SerializationType.Full: case SerializationType.Simple: case SerializationType.SimpleWithValue: case SerializationType.SimpleNoId: case SerializationType.Explicit: foreach (var block in blocks.Keys) { SendLine(container, $"Serializing {blocks[block].Count()} records in block {block}"); XmlNode xBlock = xml.CreateElement("Block"); root.AppendChild(xBlock); XML.AppendAttribute(xBlock, "Name", block); XML.AppendAttribute(xBlock, "Count", blocks[block].Count().ToString()); var xSerialized = blocks[block].Serialize(container, (SerializationStyle)type); xBlock.AppendChild(xml.ImportNode(xSerialized.ChildNodes[0], true)); } break; case SerializationType.Text: XML.AppendAttribute(root, "Delimeter", delimeter.ToString()); var text = new StringBuilder(); foreach (var block in blocks.Keys) { SendLine(container, $"Serializing {blocks[block].Count()} records in block {block}"); text.AppendLine("<<<" + block + ">>>"); var serializedblock = blocks[block].ToTextFile(container, delimeter); text.Append(serializedblock); } XML.AddCDATANode(root, "Text", text.ToString()); break; } } container.EndSection(); return(xml); }
private EntityCollection GetAllRecordsForMatching(IExecutionContainer container, List <string> allattributes, Entity cdEntity) { container.StartSection(MethodBase.GetCurrentMethod().Name); var qMatch = new QueryExpression(cdEntity.LogicalName) { ColumnSet = new ColumnSet(allattributes.ToArray()) }; #if DEBUG container.Log($"Retrieving all records for {cdEntity.LogicalName}:\n{container.ConvertToFetchXml(qMatch)}"); #endif var matches = container.RetrieveMultiple(qMatch); SendLine(container, $"Pre-retrieved {matches.Count()} records for matching"); container.EndSection(); return(matches); }
private EntityCollection GetMatchingRecordsFromPreRetrieved(IExecutionContainer container, List <string> matchattributes, Entity cdEntity, EntityCollection cAllRecordsToMatch) { container.StartSection(MethodBase.GetCurrentMethod().Name); container.Log($"Searching matches for: {cdEntity.Id} {cdEntity.LogicalName}"); var result = new EntityCollection(); foreach (var cdRecord in cAllRecordsToMatch.Entities) { if (EntityAttributesEqual(container, matchattributes, cdEntity, cdRecord)) { result.Add(cdRecord); container.Log($"Found match: {cdRecord.Id} {cdRecord.LogicalName}"); } } container.Log($"Returned matches: {result.Count()}"); container.EndSection(); return(result); }
/// <summary>Import data in Data according to shuffle definition in Definition</summary> /// <param name="container"></param> /// <param name="Definition">Shuffle Definition</param> /// <param name="Data">Exported data</param> /// <param name="ShuffleEventHandler">Event handler processing messages from the import. May be null.</param> /// <param name="defpath">Path to definition file, if not standard</param> /// <param name="clearRemainingShuffleVars"></param> /// <returns>Tuple with counters for: Created, Updated, Skipped and Failed records and a collection of entityreferences for the created/updated records</returns> public static Tuple <int, int, int, int, int, EntityReferenceCollection> QuickImport(IExecutionContainer container, XmlDocument Definition, XmlDocument Data, EventHandler <ShuffleEventArgs> ShuffleEventHandler, string defpath, bool clearRemainingShuffleVars) { container.StartSection("QuickImport"); var shuffle = new Shuffler(container); if (ShuffleEventHandler != null) { shuffle.RaiseShuffleEvent += ShuffleEventHandler; } ShuffleHelper.VerifyShuffleVars(Definition, clearRemainingShuffleVars); shuffle.Definition = Definition; shuffle.definitionpath = defpath; var blocks = shuffle.Deserialize(container, Data); var result = shuffle.ImportToCRM(container, blocks); container.EndSection(); return(result); }
/// <summary>Export data according to shuffle definition in Definition to format Type</summary> /// <param name="container"></param> /// <param name="Definition">Shuffle Definition</param> /// <param name="Type">Type of target file</param> /// <param name="Delimeter">Delimeter to use when exporting to Type: Text</param> /// <param name="ShuffleEventHandler">Event handler processing messages from the export. May be null.</param> /// <param name="defpath">Folder path for the shuffle definition file.</param> /// <param name="clearRemainingShuffleVars"></param> /// <returns>XmlDocument with exported data</returns> public static XmlDocument QuickExport(IExecutionContainer container, XmlDocument Definition, SerializationType Type, char Delimeter, EventHandler <ShuffleEventArgs> ShuffleEventHandler, string defpath, bool clearRemainingShuffleVars) { container.StartSection("QuickExport"); var shuffle = new Shuffler(container); if (ShuffleEventHandler != null) { shuffle.RaiseShuffleEvent += ShuffleEventHandler; } ShuffleHelper.VerifyShuffleVars(Definition, clearRemainingShuffleVars); shuffle.Definition = Definition; shuffle.definitionpath = defpath; var blocks = shuffle.ExportFromCRM(container); var result = shuffle.Serialize(container, blocks, Type, Delimeter); container.EndSection(); return(result); }
/// <summary> /// </summary> /// <param name="container"></param> /// <param name="entity1"></param> /// <param name="entity2"></param> /// <returns></returns> public static Entity Merge(this IExecutionContainer container, Entity entity1, Entity entity2) { container.StartSection($@"{MethodBase.GetCurrentMethod().DeclaringType.Name}\{MethodBase.GetCurrentMethod().Name}"); container.Log($"Merging {entity1.LogicalName} {container.Entity(entity1).ToString()} with {entity2.LogicalName} {container.Entity(entity2).ToString()}"); var merge = entity1.CloneAttributes(); foreach (var prop in entity2.Attributes) { if (!merge.Attributes.Contains(prop.Key)) { merge.Attributes.Add(prop); } } container.Log($"Base entity had {entity1.Attributes.Count} attributes. Second entity {entity2.Attributes.Count}. Merged entity has {merge.Attributes.Count}"); container.EndSection(); return(merge); }
private SolutionImportConditions CheckIfImportRequired(IExecutionContainer container, SolutionBlockImport import, string name, Version thisversion) { container.StartSection("CheckIfImportRequired"); var result = SolutionImportConditions.Create; var overwritesame = import.OverwriteSameVersion; var overwritenewer = import.OverwriteNewerVersion; var cSolutions = GetExistingSolutions(container); foreach (var cdSolution in cSolutions.Entities) { if (cdSolution.GetAttribute("uniquename", "") == name) { // Now we have found the same solution in target environment result = SolutionImportConditions.Update; var existingversion = new Version(cdSolution.GetAttribute("version", "1.0.0.0")); container.Log("Existing solution has version: {0}", existingversion); var comparison = thisversion.CompareTo(existingversion); if (!overwritesame && comparison == 0) { result = SolutionImportConditions.Skip; SendLine(container, "Solution {0} {1} already exists in target", name, thisversion); } else if (!overwritenewer && comparison < 0) { result = SolutionImportConditions.Skip; SendLine(container, "Existing solution {0} {1} is newer than {2}", name, existingversion, thisversion); } else if (existingversion == thisversion) { SendLine(container, "Updating version {0}", thisversion); } else { SendLine(container, "Replacing version {0} with {1}", existingversion, thisversion); } break; } } container.Log("Import Condition: {0}", result); container.EndSection(); return(result); }
/// <summary>Get the current versions for all solutions defined in the definition file</summary> /// <remarks>Results will be placed in the public dictionary <c ref="ExistingSolutionVersions">ExistingSolutionVersions</c></remarks> public void GetCurrentVersions(IExecutionContainer container) { container.StartSection("GetCurrentVersions"); ExistingSolutionVersions = new Dictionary <string, Version>(); var xRoot = XML.FindChild(definition, "ShuffleDefinition"); var xBlocks = XML.FindChild(xRoot, "Blocks"); if (xBlocks != null) { var solutions = GetExistingSolutions(container); foreach (XmlNode xBlock in xBlocks.ChildNodes) { if (xBlock.NodeType == XmlNodeType.Element) { switch (xBlock.Name) { case "DataBlock": break; case "SolutionBlock": var xmlNode = XML.FindChild(xBlock, "Export"); if (xmlNode != null) { var name = XML.GetAttribute(xBlock, "Name"); container.Log("Getting version for: {0}", name); foreach (var solution in solutions.Entities) { if (name.Equals(solution.GetAttribute("uniquename", ""), StringComparison.OrdinalIgnoreCase)) { ExistingSolutionVersions.Add(name, new Version(solution.GetAttribute("version", "1.0.0.0"))); container.Log($"Version found: {ExistingSolutionVersions[name]}"); } } } break; } } } } container.EndSection(); }
private AttributeTypeCode?GetAttributeType(IExecutionContainer container, string attribute, string entityName) { container.StartSection(MethodBase.GetCurrentMethod().Name + " " + entityName + "." + attribute); AttributeTypeCode?type = null; var eqe = new EntityQueryExpression { Properties = new MetadataPropertiesExpression() }; eqe.Properties.PropertyNames.Add("Attributes"); eqe.Criteria.Conditions.Add(new MetadataConditionExpression("LogicalName", MetadataConditionOperator.Equals, entityName)); var aqe = new AttributeQueryExpression { Properties = new MetadataPropertiesExpression("LogicalName", "AttributeType") }; eqe.AttributeQuery = aqe; var req = new RetrieveMetadataChangesRequest() { Query = eqe, ClientVersionStamp = null }; var resp = (RetrieveMetadataChangesResponse)container.Execute(req); if (resp.EntityMetadata.Count == 1) { foreach (var attr in resp.EntityMetadata[0].Attributes) { if (attr.LogicalName == attribute) { type = attr.AttributeType; break; } } } container.Log($"Type: {type}"); container.EndSection(); return(type); }
/// <summary> /// Deserialize xml/string to blocks with entities /// </summary> /// <param name="container"></param> /// <param name="serialized"></param> /// <returns>Optional, only required for SerializationType: Text</returns> public ShuffleBlocks Deserialize(IExecutionContainer container, XmlDocument serialized) { container.StartSection("Deserialize"); var result = new ShuffleBlocks(); if (serialized != null) { var root = XML.FindChild(serialized, "ShuffleData"); var sertype = XML.GetAttribute(root, "Type"); SendLine(container, $"Deserialize from {sertype}"); if (sertype == SerializationType.Full.ToString() || sertype == SerializationType.Simple.ToString() || sertype == SerializationType.SimpleNoId.ToString() || sertype == SerializationType.SimpleWithValue.ToString() || sertype == SerializationType.Explicit.ToString()) { foreach (XmlNode xBlock in root.ChildNodes) { if (xBlock.NodeType == XmlNodeType.Element && xBlock.Name == "Block" && xBlock.ChildNodes.Count == 1) { var name = XML.GetAttribute(xBlock, "Name"); var xml = new XmlDocument(); xml.AppendChild(xml.ImportNode(xBlock.ChildNodes[0], true)); var cEntities = container.CreateEntityCollection(xml); SendLine(container, $"Block {name}: {cEntities.Count()} records"); result.Add(name, cEntities); } } } else if (sertype == SerializationType.Text.ToString()) { var strdelimeter = XML.GetAttribute(root, "Delimeter"); var delimeter = strdelimeter.Length == 1 ? strdelimeter[0] : '\t'; var xText = XML.FindChild(root, "Text"); var reader = new StringReader(xText.InnerText); var line = 0; var name = ""; StringBuilder serializedblock = null; var current = reader.ReadLine(); while (current != null) { container.Log("Line {0:000}: {1}", line, current); if (current.StartsWith("<<<") && current.Contains(">>>")) { container.Log("Block start"); if (!string.IsNullOrWhiteSpace(name) && serializedblock != null) { var cEntities = container.CreateEntityCollection(serializedblock.ToString(), delimeter); result.Add(name, cEntities); SendLine(container, $"Block {name}: {cEntities.Count()} records"); } name = current.Substring(3); name = name.Substring(0, name.IndexOf(">>>", StringComparison.Ordinal)); serializedblock = new StringBuilder(); } else { serializedblock.AppendLine(current); } current = reader.ReadLine(); line++; } if (!string.IsNullOrWhiteSpace(serializedblock.ToString())) { var cEntities = container.CreateEntityCollection(serializedblock.ToString(), delimeter); result.Add(name, cEntities); SendLine(container, $"Block {name}: {cEntities.Count()} records"); } } } container.EndSection(); return(result); }
private void ValidatePreReqs(IExecutionContainer container, SolutionBlockImport import, Version thisversion) { if (import.PreRequisites == null) { container.Log("No prereqs for solution import"); return; } container.StartSection("ValidatePreReqs"); var cSolutions = GetExistingSolutions(container); foreach (var prereq in import.PreRequisites) { var valid = false; var name = prereq.Name; var comparer = prereq.Comparer; var version = new Version(); container.Log("Prereq: {0} {1} {2}", name, comparer, version); if (comparer == SolutionVersionComparers.eqthis || comparer == SolutionVersionComparers.gethis) { version = thisversion; comparer = comparer == SolutionVersionComparers.eqthis ? SolutionVersionComparers.eq : comparer == SolutionVersionComparers.gethis ? SolutionVersionComparers.ge : comparer; } else if (comparer != SolutionVersionComparers.any) { version = new Version(prereq.Version.Replace('*', '0')); } foreach (var cdSolution in cSolutions.Entities) { if (cdSolution.GetAttribute("uniquename", "") == name) { container.Log("Found matching solution"); switch (comparer) { case SolutionVersionComparers.any: valid = true; break; case SolutionVersionComparers.eq: valid = new Version(cdSolution.GetAttribute("version", "1.0.0.0")).Equals(version); break; case SolutionVersionComparers.ge: valid = new Version(cdSolution.GetAttribute("version", "<undefined>")) >= version; break; default: throw new ArgumentOutOfRangeException("Comparer", comparer, "Invalid comparer value"); } } if (valid) { break; } } if (valid) { SendLine(container, "Prerequisite {0} {1} {2} is satisfied", name, comparer, version); } else { SendLine(container, "Prerequisite {0} {1} {2} is NOT satisfied", name, comparer, version); throw new Exception("Prerequisite NOT satisfied (" + name + " " + comparer + " " + version + ")"); } } container.EndSection(); }
private void AddRelationFilter(IExecutionContainer container, ShuffleBlocks blocks, string entityName, DataBlockRelation relation, FilterExpression filter) { container.StartSection(MethodBase.GetCurrentMethod().Name); if (blocks != null && blocks.Count > 0) { var block = relation.Block; var attribute = relation.Attribute; var pkattribute = relation.PKAttribute; var includenull = relation.IncludeNull; var type = GetAttributeType(container, attribute, entityName); var cond = new ConditionExpression { AttributeName = attribute, Operator = Microsoft.Xrm.Sdk.Query.ConditionOperator.In }; var ids = new List <object>(); var parentcoll = blocks.ContainsKey(block) ? blocks[block] : null; if (parentcoll != null && parentcoll.Entities.Count > 0) { foreach (var parent in parentcoll.Entities) { if (string.IsNullOrEmpty(pkattribute)) { if (type == AttributeTypeCode.String) { ids.Add(parent.Id.ToString()); } else { ids.Add(parent.Id); } } else if (type == AttributeTypeCode.String) { ids.Add(parent.GetAttribute(pkattribute, new EntityReference()).Id.ToString()); } else { ids.Add(parent.GetAttribute(pkattribute, new EntityReference()).Id); } } } else { // Adding temp guid to indicate "no matches", as ConditionOperator.In will fail if no values are given ids.Add(new Guid()); } cond.Values.AddRange(ids); if (!includenull) { filter.AddCondition(cond); } else { var orfilter = new FilterExpression(LogicalOperator.Or); orfilter.AddCondition(attribute, Microsoft.Xrm.Sdk.Query.ConditionOperator.Null); orfilter.AddCondition(cond); filter.AddFilter(orfilter); } container.Log($"Adding relation condition for {attribute} in {ids.Count} values in {block}.{pkattribute}"); } container.EndSection(); }
/// <summary> /// Import entities to CRM from dictionary of blocks /// </summary> /// <param name="container"></param> /// <param name="blocks">Blocks with entities to import</param> /// <returns>Tuple with counters for: Created, Updated, Skipped and Failed records</returns> public Tuple <int, int, int, int, int, EntityReferenceCollection> ImportToCRM(IExecutionContainer container, ShuffleBlocks blocks) { container.StartSection("ImportToCRM"); if (definition == null) { throw new ArgumentNullException("Definition", "Shuffle definition must be specified to import data"); } var created = 0; var updated = 0; var skipped = 0; var deleted = 0; var failed = 0; var references = new EntityReferenceCollection(); if (ShuffleDefinition.Blocks.Items.Any(b => (b is DataBlock data && data.Import != null) || b is SolutionBlock sol && sol.Import != null)) { guidmap = new Dictionary <Guid, Guid>(); stoponerror = ShuffleDefinition.StopOnError; timeout = ShuffleDefinition.TimeoutSpecified ? ShuffleDefinition.Timeout : -1; //double savedtimeout = -1; //if (timeout > -1) //{ // savedtimeout = SetTimeout(); //} var totalBlocks = ShuffleDefinition.Blocks.Items.Length; var currentBlock = 0; foreach (var block in ShuffleDefinition.Blocks.Items) { currentBlock++; SendStatus(totalBlocks, currentBlock, -1, -1); if (block is DataBlock datablock) { var name = datablock.Name; if (!blocks.ContainsKey(name)) { blocks.Add(name, new EntityCollection()); } var dataresult = ImportDataBlock(container, datablock, blocks[name]); created += dataresult.Item1; updated += dataresult.Item2; skipped += dataresult.Item3; deleted += dataresult.Item4; failed += dataresult.Item5; references.AddRange(dataresult.Item6); } else if (block is SolutionBlock solutionblock) { var solutionresult = ImportSolutionBlock(solutionblock); switch (solutionresult) { case ItemImportResult.Created: created++; break; case ItemImportResult.Updated: updated++; break; case ItemImportResult.Skipped: skipped++; break; case ItemImportResult.Failed: failed++; break; } } } SendStatus(0, 0, 0, 0); //if (savedtimeout > -1) //{ // ResetTimeout(container, savedtimeout); //} } container.EndSection(); return(new Tuple <int, int, int, int, int, EntityReferenceCollection>(created, updated, skipped, deleted, failed, references)); }
private bool SaveEntity(IExecutionContainer container, Entity cdNewEntity, Entity cdMatchEntity, bool updateInactiveRecord, bool updateIdentical, int pos, string identifier) { container.StartSection("SaveEntity " + pos.ToString("000 ") + identifier); var recordSaved = false; if (string.IsNullOrWhiteSpace(identifier)) { identifier = cdNewEntity.ToString(); } var newOwner = cdNewEntity.GetAttribute <EntityReference>("ownerid", null); var newState = cdNewEntity.GetAttribute <OptionSetValue>("statecode", null); var newStatus = cdNewEntity.GetAttribute <OptionSetValue>("statuscode", null); var newActive = newState != null?container.GetActiveStates(cdNewEntity.LogicalName).Contains(newState.Value) : true; var nowActive = true; if ((newState == null) != (newStatus == null)) { throw new InvalidDataException("When setting status of the record, both statecode and statuscode must be present"); } if (!newActive) { container.Log("Removing state+status from entity to update"); cdNewEntity.RemoveAttribute("statecode"); cdNewEntity.RemoveAttribute("statuscode"); } if (cdMatchEntity == null) { container.Create(cdNewEntity); recordSaved = true; SendLine(container, "{0:000} Created: {1}", pos, identifier); } else { var oldState = cdMatchEntity.GetAttribute <OptionSetValue>("statecode", null); var oldActive = oldState != null?container.GetActiveStates(cdNewEntity.LogicalName).Contains(oldState.Value) : true; nowActive = oldActive; cdNewEntity.Id = cdMatchEntity.Id; if (!oldActive && (newActive || updateInactiveRecord)) { // Inaktiv post som ska aktiveras eller uppdateras container.SetState(cdNewEntity, 0, 1); SendLine(container, "{0:000} Activated: {1} for update", pos, identifier); nowActive = true; } if (nowActive) { var updateattributes = cdNewEntity.Attributes.Keys.ToList(); if (updateattributes.Contains(container.Entity(cdNewEntity.LogicalName).PrimaryIdAttribute)) { updateattributes.Remove(container.Entity(cdNewEntity.LogicalName).PrimaryIdAttribute); } if (updateIdentical || !EntityAttributesEqual(container, updateattributes, cdNewEntity, cdMatchEntity)) { try { container.Update(cdNewEntity); recordSaved = true; SendLine(container, "{0:000} Updated: {1}", pos, identifier); } catch (Exception) { recordSaved = false; SendLine(container, "{0:000} Update Failed: {1} {2} {3}", pos, identifier, cdNewEntity.LogicalName); } } else { SendLine(container, "{0:000} Skipped: {1} (Identical)", pos, identifier); } } else { SendLine(container, "{0:000} Inactive: {1}", pos, identifier); } if (newOwner != null && !newOwner.Equals(cdMatchEntity.GetAttribute("ownerid", new EntityReference()))) { container.Principal(cdNewEntity).On(newOwner).Assign(); // cdNewEntity.Assign(newOwner); SendLine(container, "{0:000} Assigned: {1} to {2} {3}", pos, identifier, newOwner.LogicalName, string.IsNullOrEmpty(newOwner.Name) ? newOwner.Id.ToString() : newOwner.Name); } } if (newActive != nowActive) { // Active should be changed on the record var newStatusValue = newStatus.Value; if (cdNewEntity.LogicalName == "savedquery" && newState.Value == 1 && newStatusValue == 1) { // Adjustment for inactive but unpublished view newStatusValue = 2; } if (cdNewEntity.LogicalName == "duplicaterule") { if (newStatusValue == 2) { container.PublishDuplicateRule(cdNewEntity); SendLine(container, "{0:000} Publish Duplicate Rule: {1}", pos, identifier); } else { container.UnpublishDuplicateRule(cdNewEntity); SendLine(container, "{0:000} Unpublish Duplicate Rule: {1}", pos, identifier); } } else { container.SetState(cdNewEntity, newState.Value, newStatusValue); SendLine(container, "{0:000} SetState: {1}: {2}/{3}", pos, identifier, newState.Value, newStatus.Value); } } container.EndSection(); return(recordSaved); }
private Tuple <int, int, int, int, int, EntityReferenceCollection> ImportDataBlock(IExecutionContainer container, DataBlock block, EntityCollection cEntities) { container.StartSection("ImportDataBlock"); var created = 0; var updated = 0; var skipped = 0; var deleted = 0; var failed = 0; var references = new EntityReferenceCollection(); var name = block.Name; container.Log($"Block: {name}"); SendStatus(name, null); SendLine(container); if (block.Import != null) { var includeid = block.Import.CreateWithId; var save = block.Import.Save; var delete = block.Import.Delete; var updateinactive = block.Import.UpdateInactive; var updateidentical = block.Import.UpdateIdentical; if (block.Import.OverwriteSpecified) { SendLine(container, "DEPRECATED use of attribute Overwrite!"); save = block.Import.Overwrite ? SaveTypes.CreateUpdate : SaveTypes.CreateOnly; } var matchattributes = GetMatchAttributes(block.Import.Match); var updateattributes = !updateidentical?GetUpdateAttributes(cEntities) : new List <string>(); var preretrieveall = block.Import.Match?.PreRetrieveAll == true; SendLine(container); SendLine(container, $"Importing block {name} - {cEntities.Count()} records "); var i = 1; if (delete == DeleteTypes.All && (matchattributes.Count == 0)) { // All records shall be deleted, no match attribute defined, so just get all and delete all var entity = block.Entity; var qDelete = new QueryExpression(entity); qDelete.ColumnSet.AddColumn(container.Entity(entity).PrimaryNameAttribute); var deleterecords = container.RetrieveMultiple(qDelete); //var deleterecords = Entity.RetrieveMultiple(crmsvc, qDelete, log); SendLine(container, $"Deleting ALL {entity} - {deleterecords.Count()} records"); foreach (var record in deleterecords.Entities) { SendLine(container, "{0:000} Deleting existing: {1}", i, record); try { container.Delete(record); deleted++; } catch (FaultException <OrganizationServiceFault> ex) { if (ex.Message.ToUpperInvariant().Contains("DOES NOT EXIST")) { // This may happen through delayed cascade delete in CRM SendLine(container, " ...already deleted"); } else { throw; } } i++; } } var totalRecords = cEntities.Count(); i = 1; EntityCollection cAllRecordsToMatch = null; foreach (var cdEntity in cEntities.Entities) { var unique = cdEntity.Id.ToString(); SendStatus(-1, -1, totalRecords, i); try { var oldid = cdEntity.Id; var newid = Guid.Empty; ReplaceGuids(container, cdEntity, includeid); ReplaceUpdateInfo(cdEntity); unique = GetEntityDisplayString(container, block.Import.Match, cdEntity); SendStatus(null, unique); if (!block.TypeSpecified || block.Type == EntityTypes.Entity) { #region Entity if (matchattributes.Count == 0) { if (save == SaveTypes.Never || save == SaveTypes.UpdateOnly) { skipped++; SendLine(container, "{0:000} Not saving: {1}", i, unique); } else { if (!includeid) { cdEntity.Id = Guid.Empty; } if (SaveEntity(container, cdEntity, null, updateinactive, updateidentical, i, unique)) { created++; newid = cdEntity.Id; references.Add(cdEntity.ToEntityReference()); } } } else { var matches = GetMatchingRecords(container, cdEntity, matchattributes, updateattributes, preretrieveall, ref cAllRecordsToMatch); if (delete == DeleteTypes.All || (matches.Count() == 1 && delete == DeleteTypes.Existing)) { foreach (var cdMatch in matches.Entities) { SendLine(container, "{0:000} Deleting existing: {1}", i, unique); try { container.Delete(cdMatch); deleted++; } catch (FaultException <OrganizationServiceFault> ex) { if (ex.Message.ToUpperInvariant().Contains("DOES NOT EXIST")) { // This may happen through cascade delete in CRM SendLine(container, " ...already deleted"); } else { throw; } } } matches.Entities.Clear(); } if (matches.Count() == 0) { if (save == SaveTypes.Never || save == SaveTypes.UpdateOnly) { skipped++; SendLine(container, "{0:000} Not creating: {1}", i, unique); } else { if (!includeid) { cdEntity.Id = Guid.Empty; } if (SaveEntity(container, cdEntity, null, updateinactive, updateidentical, i, unique)) { created++; newid = cdEntity.Id; references.Add(cdEntity.ToEntityReference()); } } } else if (matches.Count() == 1) { var match = matches[0]; newid = match.Id; if (save == SaveTypes.CreateUpdate || save == SaveTypes.UpdateOnly) { if (SaveEntity(container, cdEntity, match, updateinactive, updateidentical, i, unique)) { updated++; references.Add(cdEntity.ToEntityReference()); } else { skipped++; } } else { skipped++; SendLine(container, "{0:000} Exists: {1}", i, unique); } } else { failed++; SendLine(container, $"Import object matches {matches.Count()} records in target database!"); SendLine(container, unique); } } if (!oldid.Equals(Guid.Empty) && !newid.Equals(Guid.Empty) && !oldid.Equals(newid) && !guidmap.ContainsKey(oldid)) { container.Log("Mapping IDs: {0} ==> {1}", oldid, newid); guidmap.Add(oldid, newid); } #endregion Entity } else if (block.Type == EntityTypes.Intersect) { #region Intersect if (cdEntity.Attributes.Count != 2) { throw new ArgumentOutOfRangeException("Attributes", cdEntity.Attributes.Count, "Invalid Attribute count for intersect object"); } var intersect = block.IntersectName; if (string.IsNullOrEmpty(intersect)) { intersect = cdEntity.LogicalName; } var ref1 = (EntityReference)cdEntity.Attributes.ElementAt(0).Value; var ref2 = (EntityReference)cdEntity.Attributes.ElementAt(1).Value; var party1 = new Entity(ref1.LogicalName, ref1.Id); //Entity.InitFromNameAndId(ref1.LogicalName, ref1.Id, crmsvc, log); var party2 = new Entity(ref2.LogicalName, ref2.Id); //Entity.InitFromNameAndId(ref2.LogicalName, ref2.Id, crmsvc, log); try { container.Associate(party1, party2, intersect); //party1.Associate(party2, intersect); created++; SendLine(container, "{0} Associated: {1}", i.ToString().PadLeft(3, '0'), name); } catch (Exception ex) { if (ex.Message.Contains("duplicate")) { SendLine(container, "{0} Association exists: {1}", i.ToString().PadLeft(3, '0'), name); skipped++; } else { throw; } } #endregion Intersect } } catch (Exception ex) { failed++; SendLine(container, $"\n*** Error record: {unique} ***\n{ex.Message}"); container.Log(ex); if (stoponerror) { throw; } } i++; } SendLine(container, $"Created: {created} Updated: {updated} Skipped: {skipped} Deleted: {deleted} Failed: {failed}"); } container.EndSection(); return(new Tuple <int, int, int, int, int, EntityReferenceCollection>(created, updated, skipped, deleted, failed, references)); }
private EntityCollection ExportDataBlock(IExecutionContainer container, ShuffleBlocks blocks, DataBlock block) { container.StartSection("ExportDataBlock"); container.Log($"Block: {block.Name}"); EntityCollection cExportEntities = null; if (block.Export != null) { #region Define attributes var attributes = block.Export.Items.Where(i => i is DataBlockExportAttributes).FirstOrDefault() as DataBlockExportAttributes; var allcolumns = false; var lAttributes = new List <string>(); var lNullAttributes = new List <string>(); if (attributes != null) { foreach (var attribute in attributes.Attribute) { var attr = attribute.Name; container.Log($"Adding column: {attr}"); lAttributes.Add(attr.Replace("*", "%")); if (attr.Contains("*")) { allcolumns = true; container.Log("Found wildcard"); } else { if (attribute.IncludeNull) { lNullAttributes.Add(attr); } } } } else { allcolumns = true; lAttributes.Add("*"); container.Log("Attributes not specified, retrieving all"); } #endregion Define attributes var fetchxml = block.Export.Items.Where(i => i is string).FirstOrDefault() as string; if (!string.IsNullOrWhiteSpace(fetchxml)) { container.StartSection("Export entity using FetchXML"); #if DEBUG container.Log($"FetchXML:\n{fetchxml}"); #endif cExportEntities = container.RetrieveMultiple(new FetchExpression(fetchxml)); container.EndSection(); } else if (!block.TypeSpecified || block.Type == EntityTypes.Entity) { #region QueryExpression Entity container.StartSection($"Export entity {block.Entity}"); var qExport = new QueryExpression(block.Entity); if (block.Export.ActiveOnly) { Query.AppendConditionActive(qExport.Criteria); //CintQryExp.AppendConditionActive(qExport.Criteria); } if (block.Relation != null) { foreach (var relation in block.Relation) { AddRelationFilter(container, blocks, block.Entity, relation, qExport.Criteria); } } foreach (var filter in block.Export.Items.Where(i => i is DataBlockExportFilter).Cast <DataBlockExportFilter>()) { AddFilter(container, qExport, filter); } foreach (var sort in block.Export.Items.Where(i => i is DataBlockExportSort).Cast <DataBlockExportSort>()) { qExport.AddOrder(sort.Attribute, sort.Type == SortTypes.Desc ? OrderType.Descending : OrderType.Ascending); } if (allcolumns) { qExport.ColumnSet = new ColumnSet(true); } else { foreach (var attr in lAttributes) { qExport.ColumnSet.AddColumn(attr); } } #if DEBUG container.Log("Converting to FetchXML"); try { var fetch = container.ConvertToFetchXml(qExport); container.Log($"Exporting {block.Entity}:\n{fetch}"); } catch (Exception ex) { container.Log("Conversion error:"); container.Log(ex); } #endif cExportEntities = container.RetrieveMultiple(qExport); if (allcolumns) { SelectAttributes(container, cExportEntities, lAttributes, lNullAttributes); } SendLine(container, $"Block {block.Name} - {cExportEntities.Count()} records"); container.EndSection(); #endregion QueryExpression Entity } else if (block.Type == EntityTypes.Intersect) { #region FetchXML Intersect container.StartSection($"Export intersect {block.Entity}"); var xDoc = new XmlDocument(); var xEntity = FetchXML.Create(xDoc, block.Entity); FetchXML.AddAttribute(xEntity, lAttributes.ToArray()); if (block.Relation != null) { foreach (var relation in block.Relation) { AddRelationFilter(container, blocks, relation, xEntity); } } var fetch = xDoc.OuterXml; //Imran: Removed because this is causing invalid Xml errors. Could not see the point of having these placeholders. //fetch = fetch.Replace("<fetch ", "<fetch {0} {1} "); // Detta för att se till att CrmServiceProxy.RetrieveMultiple kan hantera paging #if DEBUG container.Log($"Exporting intersect entity {block.Entity}\n{fetch}"); #endif var qExport = new FetchExpression(fetch); cExportEntities = container.RetrieveMultiple(qExport); foreach (var entity in cExportEntities.Entities) { var newattributes = new List <KeyValuePair <string, object> >(); foreach (var attr in entity.Attributes) { if (attr.Value is Guid guid) { var attrname = attr.Key; var relatedentity = attrname.Substring(0, attrname.Length - (attrname.EndsWith("idone") || attrname.EndsWith("idtwo") ? 5 : 2)); if (!newattributes.Contains(new KeyValuePair <string, object>(attrname, new EntityReference(relatedentity, guid)))) { container.Log($"Adding Attribute {attrname} - Related entity {relatedentity}"); newattributes.Add(new KeyValuePair <string, object>(attrname, new EntityReference(relatedentity, guid))); #if DEBUG container.Log($"{attrname} added"); #endif } else { #if DEBUG container.Log($"{attrname} already exists."); #endif } } } foreach (var newattr in newattributes) { #if DEBUG container.Log($"Entity {entity.LogicalName} contains attribute {newattr.Key}: {entity.Attributes.Contains(newattr.Key)}"); #endif if (!entity.Attributes.Contains(newattr.Key)) { entity.Attributes.Add(newattr.Key, newattr.Value); } } } container.EndSection(); #endregion FetchXML Intersect } container.Log($"Returning {cExportEntities.Count()} records"); } container.EndSection(); return(cExportEntities); }
private EntityCollection GetMatchingRecords(IExecutionContainer container, Entity cdEntity, List <string> matchattributes, List <string> updateattributes, bool preretrieveall, ref EntityCollection cAllRecordsToMatch) { container.StartSection(MethodBase.GetCurrentMethod().Name); EntityCollection matches = null; var allattributes = new List <string> { container.Entity(cdEntity.LogicalName).PrimaryIdAttribute }; if (cdEntity.Contains("ownerid")) { allattributes.Add("ownerid"); } if (cdEntity.Contains("statecode") || cdEntity.Contains("statuscode")) { allattributes.Add("statecode"); allattributes.Add("statuscode"); } allattributes = allattributes.Union(matchattributes.Union(updateattributes)).ToList(); if (preretrieveall) { if (cAllRecordsToMatch == null) { cAllRecordsToMatch = GetAllRecordsForMatching(container, allattributes, cdEntity); } matches = GetMatchingRecordsFromPreRetrieved(container, matchattributes, cdEntity, cAllRecordsToMatch); } else { var qMatch = new QueryExpression(cdEntity.LogicalName) { // We need to be able to see if any attributes have changed, so lets make sure matching records have all the attributes that will be updated ColumnSet = new ColumnSet(allattributes.ToArray()) }; foreach (var matchattr in matchattributes) { object value = null; if (cdEntity.Contains(matchattr)) { value = container.AttributeAsBaseType(cdEntity, matchattr, null, false); } else if (matchattr == container.Entity(cdEntity.LogicalName).PrimaryIdAttribute) { value = cdEntity.Id; } if (value != null) { Query.AppendCondition(qMatch.Criteria, LogicalOperator.And, matchattr, Microsoft.Xrm.Sdk.Query.ConditionOperator.Equal, value); } else { Query.AppendCondition(qMatch.Criteria, LogicalOperator.And, matchattr, Microsoft.Xrm.Sdk.Query.ConditionOperator.Null, null); } } #if DEBUG container.Log($"Finding matches for {cdEntity.LogicalName}:\n{container.ConvertToFetchXml(qMatch)}"); #endif matches = container.RetrieveMultiple(qMatch); } container.EndSection(); return(matches); }
private void ExportSolutionBlock(IExecutionContainer container, SolutionBlock block) { container.StartSection("ExportSolutionBlock"); var name = block.Name; container.Log("Block: {0}", name); var path = block.Path; var file = block.File; if (string.IsNullOrWhiteSpace(path) && !string.IsNullOrWhiteSpace(definitionpath)) { path = definitionpath; path += path.EndsWith("\\") ? "" : "\\"; } if (string.IsNullOrWhiteSpace(file)) { file = name; } if (block.Export != null) { var type = block.Export.Type; var setversion = block.Export.SetVersion; var publish = block.Export.PublishBeforeExport; var targetversion = block.Export.TargetVersion; var cdSolution = GetAndVerifySolutionForExport(name); var currentversion = new Version(cdSolution.GetAttribute("version", "1.0.0.0")); SendLine(container, "Solution: {0} {1}", name, currentversion); if (!string.IsNullOrWhiteSpace(setversion)) { SetNewSolutionVersion(container, setversion, cdSolution, currentversion); } if (publish) { SendLine(container, "Publishing customizations"); container.Execute(new PublishAllXmlRequest()); } var req = new ExportSolutionRequest() { SolutionName = name }; #if Crm8 if (!string.IsNullOrWhiteSpace(targetversion)) { req.TargetVersion = targetversion; } #endif if (block.Export.Settings != null) { req.ExportAutoNumberingSettings = block.Export.Settings.AutoNumbering; req.ExportCalendarSettings = block.Export.Settings.Calendar; req.ExportCustomizationSettings = block.Export.Settings.Customization; req.ExportEmailTrackingSettings = block.Export.Settings.EmailTracking; req.ExportGeneralSettings = block.Export.Settings.General; req.ExportMarketingSettings = block.Export.Settings.Marketing; req.ExportOutlookSynchronizationSettings = block.Export.Settings.OutlookSync; req.ExportRelationshipRoles = block.Export.Settings.RelationshipRoles; req.ExportIsvConfig = block.Export.Settings.IsvConfig; } if (type == SolutionTypes.Managed || type == SolutionTypes.Both) { var filename = path + file + "_managed.zip"; SendLine(container, "Exporting solution to: {0}", filename); req.Managed = true; var exportSolutionResponse = (ExportSolutionResponse)container.Execute(req); var exportXml = exportSolutionResponse.ExportSolutionFile; File.WriteAllBytes(filename, exportXml); } if (type == SolutionTypes.Unmanaged || type == SolutionTypes.Both) { var filename = path + file + ".zip"; SendLine(container, $"Exporting solution to: {filename}"); req.Managed = false; var exportSolutionResponse = (ExportSolutionResponse)container.Execute(req); var exportXml = exportSolutionResponse.ExportSolutionFile; File.WriteAllBytes(filename, exportXml); } } container.EndSection(); }