private static void SelectAttributes(IExecutionContainer container, EntityCollection cExportEntities, List <string> lAttributes, List <string> lNullAttributes) { foreach (var entity in cExportEntities.Entities) { var i = 0; var x = new List <string>(entity.Attributes.Keys); while (i < entity.Attributes.Count) { var attr = x[i]; if (attr != container.Entity(entity.LogicalName).PrimaryIdAttribute&& !IncludeAttribute(attr, lAttributes)) { entity.Attributes.Remove(attr); x.Remove(attr); } else { i++; } } foreach (var nullattribute in lNullAttributes) { if (!entity.Contains(nullattribute)) { entity.Attributes.Add(nullattribute, null); } } } }
/// <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> /// </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 static bool EntityAttributesEqual(IExecutionContainer container, List <string> matchattributes, Entity entity1, Entity entity2) { var match = true; foreach (var attr in matchattributes) { var srcvalue = ""; if (attr == container.Entity(entity1.LogicalName).PrimaryIdAttribute) { srcvalue = entity1.Id.ToString(); } else { srcvalue = container.AttributeAsBaseType(entity1, attr, "<null>", true).ToString(); } var trgvalue = container.AttributeAsBaseType(entity2, attr, "<null>", true).ToString(); if (srcvalue != trgvalue) { match = false; break; } } return(match); }
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 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); }