/// <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> /// 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> /// </summary> /// <param name="container"></param> /// <param name="entity"></param> public static void Delete(this IExecutionContainer container, Entity entity) { if (entity.Id.Equals(Guid.Empty)) { container.Log("Cannot delete - guid is empty"); return; } container.Service.Delete(entity.LogicalName, entity.Id); container.Log($"Deleted {entity.LogicalName}:{entity.Id}"); }
/// <summary>Update state and status of current record</summary> /// <remarks> /// http://msdynamicscrmblog.wordpress.com/2013/10/26/status-and-status-reason-values-in-dynamics-crm-2013/comment-page-1/ /// ToStringWithEntityName() is replaced with entity.LogicalName /// </remarks> /// <param name="container"></param> /// <param name="entity"></param> /// <param name="state">Active=0 and Inactive=1</param> /// <param name="status">Active=1 and Inactive=2</param> public static SetStateResponse SetState(this IExecutionContainer container, Entity entity, int state, int status) { container.Log($"Setting state {state} {status} on {entity.LogicalName}"); var response = container.Service.Execute(new SetStateRequest() { EntityMoniker = entity.ToEntityReference(), State = new OptionSetValue(state), Status = new OptionSetValue(status) }) as SetStateResponse; container.Log("SetState completed"); return(response); }
/// <summary> /// </summary> /// <param name="container"></param> /// <param name="entity"></param> /// <returns>The Guid of the created entity.</returns> public static Entity Create(this IExecutionContainer container, Entity entity) { entity.Id = container.Service.Create(entity); container.Log($"Created {entity.LogicalName}:{entity.Id}"); return(entity); }
/// <summary>Validates given ShuffleDefinition with XSD.</summary> /// <param name="container"></param> /// <param name="def"></param> /// <returns></returns> public static void ValidateDefinitionXml(IExecutionContainer container, XmlDocument def) { try { def.Schemas = Schemas; if (def.Schemas.Count >= 2) { def.Validate(null); container?.Log("ShuffleDefinition validated"); } } catch (XmlSchemaValidationException ex) { container?.Log(ex); throw; } }
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> /// </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 void SendText(IExecutionContainer container, string msg, int totalBlocks, int currentBlock, int blockRecords, int currentRecord, bool replacelast, params object[] args) { if (msg != null) { msg = string.Format(msg, args); if (msg.Length > 1) { container.Log(msg, args); } } OnRaiseShuffleEvent(new ShuffleEventArgs(msg, totalBlocks, currentBlock, blockRecords, currentRecord, replacelast)); }
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 static void AddRelationFilter(IExecutionContainer container, ShuffleBlocks blocks, DataBlockRelation relation, XmlNode xEntity) { if (blocks != null && blocks.Count > 0) { var block = relation.Block; var attribute = relation.Attribute; var pkattribute = relation.PKAttribute; var includenull = relation.IncludeNull; var ids = new List <string>(); 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)) { ids.Add(parent.Id.ToString()); } else { ids.Add(parent.GetAttribute(pkattribute, new EntityReference()).Id.ToString()); } } } else { // Adding temp guid to indicate "no matches", as ConditionOperator.In will fail if no values are given ids.Add(new Guid().ToString()); } if (!includenull) { FetchXML.AppendFilter(xEntity, "and", attribute, "in", ids.ToArray()); } else { var xFilter = FetchXML.AppendFilter(xEntity, "or"); FetchXML.AppendCondition(xFilter, attribute, "null"); FetchXML.AppendCondition(xFilter, attribute, "in", ids.ToArray()); } container.Log($"Adding relation condition for {attribute} in {ids.Count} values in {block}.{pkattribute}"); } }
/// <summary>Associates current record with relatedentities, using specified intersect relationship</summary> /// <param name="entity"></param> /// <param name="container"></param> /// <param name="relatedEntities">Collection of the entities to be related to current entity</param> /// <param name="intersect">Name of the intersect relationship/entity</param> /// <param name="batchSize">Optional. Determines the max number of entities to associate per request</param> /// <remarks>To be used with N:N-relationships.</remarks> /// <exception cref="FaultException{TDetail}"> /// <strong>TDetail</strong> may be typed as: /// <para> /// <see cref="OrganizationServiceFault" />: Thrown when any of the associations already exists. /// </para> /// </exception> public static void Associate(this IExecutionContainer container, Entity entity, EntityCollection relatedEntities, string intersect, int batchSize) { if (entity == null) { throw new ArgumentNullException(nameof(entity)); } EntityRole?role = null; if (relatedEntities.Entities.Count > 0 && relatedEntities[0].LogicalName == entity.LogicalName) { // N:N-relation till samma entitet, då måste man ange en roll, tydligen. role = EntityRole.Referencing; } if (batchSize < 1) { throw new ArgumentException("batchSize must be larger than zero."); } var entRefCollection = relatedEntities.ToEntityReferenceCollection(); var processed = 0; while (processed < relatedEntities.Entities.Count) { var batch = new EntityReferenceCollection(entRefCollection.Skip(processed).Take(batchSize).ToList()); processed += batch.Count(); var req = new AssociateRequest { Target = entity.ToEntityReference(), Relationship = new Relationship(intersect) { PrimaryEntityRole = role }, RelatedEntities = batch }; container.Service.Execute(req); container.Log("Associated {0} {1} with {2}", batch.Count, relatedEntities.Entities.Count > 0 ? relatedEntities[0].LogicalName : "", entity.LogicalName); } }
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); }
private void ReplaceGuids(IExecutionContainer container, Entity cdEntity, bool includeid) { foreach (var prop in cdEntity.Attributes) { if (prop.Value is Guid && guidmap.ContainsKey((Guid)prop.Value)) { if (includeid) { throw new NotImplementedException("Cannot handle replacement of Guid type attributes"); } else { container.Log("No action, we don't care about the guid of the object"); } } if (prop.Value is EntityReference && guidmap.ContainsKey(((EntityReference)prop.Value).Id)) { ((EntityReference)prop.Value).Id = guidmap[((EntityReference)prop.Value).Id]; } } }
/// <summary> /// Write message and parameter values to the log file. /// </summary> /// <param name="message"></param> /// <param name="args"></param> public static void Log(this IExecutionContainer container, string message, params object[] args) => container.Log(string.Format(message, args));
/// <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 AddFilter(IExecutionContainer container, QueryExpression qExport, DataBlockExportFilter filter) { var valuestring = filter.Value; if (valuestring != null && valuestring.Contains("{0}")) { throw new ArgumentOutOfRangeException("Name", "Filter", "Parameterized Filters not supported in embedded Shuffle Utils"); } var operstr = filter.Operator.ToString(); if (string.IsNullOrEmpty(operstr)) { operstr = "Equal"; } var oper = (Microsoft.Xrm.Sdk.Query.ConditionOperator)Enum.Parse(typeof(Microsoft.Xrm.Sdk.Query.ConditionOperator), operstr, true); object value = null; if (oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.EqualBusinessId && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.EqualUserId && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.EqualUserLanguage && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.NotEqualBusinessId && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.NotEqualUserId && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.NotNull && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.Null && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.ThisMonth && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.ThisWeek && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.ThisYear && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.Today && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.Tomorrow && oper != Microsoft.Xrm.Sdk.Query.ConditionOperator.Yesterday) { if (filter.TypeSpecified) { switch (filter.Type) { case FilterTypes.@string: value = valuestring; break; case FilterTypes.@int: value = int.Parse(valuestring); break; case FilterTypes.@bool: value = bool.Parse(valuestring); break; case FilterTypes.datetime: value = DateTime.Parse(valuestring); break; case FilterTypes.guid: value = new Guid(valuestring); break; default: throw new ArgumentOutOfRangeException("Type", filter.Type, "Invalid filter attribute type"); } } } var attribute = filter.Attribute; container.Log($"Adding filter: {attribute} {oper} {value}"); Query.AppendCondition(qExport.Criteria, LogicalOperator.And, attribute, oper, value); }
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(); }
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 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(); }
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(); }
/// <summary> /// </summary> /// <param name="container"></param> /// <param name="entity"></param> public static void Update(this IExecutionContainer container, Entity entity) { container.Service.Update(entity); container.Log($"Updated {entity.LogicalName} {entity.Id} with {entity.Attributes.Count} attributes"); }
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 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); }