Skip to content

Instantly share code, notes, and snippets.

@abelevtsov
Created July 11, 2016 08:54
Show Gist options
  • Save abelevtsov/26adc740a0018faa587a333ed90868e2 to your computer and use it in GitHub Desktop.
Save abelevtsov/26adc740a0018faa587a333ed90868e2 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Query;
using MoreLinq;
using NLog;
using Rosagroleasing.Crm.Core.Interfaces;
using Rosagroleasing.Crm.Core.Interfaces.Entities;
namespace Rosagroleasing.Crm.Core
{
public class CrmProvider
{
protected static readonly Logger Logger = LogManager.GetLogger("CRM");
public Guid UserId { get; protected set; }
public Guid OrganizationId { get; protected set; }
public IOrganizationService SystemService { get; set; }
public IOrganizationService UserService { get; set; }
/// <summary>
/// Gets id of current user
/// </summary>
public virtual Guid GetUserId()
{
var me = (WhoAmIResponse)SystemService.Execute(new WhoAmIRequest());
return me.UserId;
}
/// <summary>
/// Get settings per user
/// </summary>
public Entity GetUserSettings()
{
var userId = GetUserId();
return userId != Guid.Empty ? GetUserSettings(userId) : null;
}
/// <summary>
/// Get setting for specified user
/// </summary>
/// <param name="userId">User Id</param>
public Entity GetUserSettings(Guid userId)
{
if (userId != Guid.Empty)
{
var request =
new RetrieveUserSettingsSystemUserRequest
{
EntityId = userId,
ColumnSet = new ColumnSet(true)
};
try
{
var userSettings = SystemService.Execute(request) as RetrieveUserSettingsSystemUserResponse;
return userSettings == null || userSettings.Entity == null ? null : userSettings.Entity;
}
catch (FaultException<OrganizationServiceFault>)
{
return null;
}
}
return null;
}
/// <summary>
/// Get teams of user
/// </summary>
/// <param name="userId">User Id, current user Id by default</param>
public IEnumerable<Entity> GetUserTeams(Guid? userId = null)
{
var currentUserId = userId.HasValue && userId.Value != Guid.Empty ? userId.Value : GetUserId();
var query = new QueryExpression("team")
{
ColumnSet = new ColumnSet(true)
};
query.LinkEntities.Add(new LinkEntity
{
LinkFromEntityName = "team",
LinkToEntityName = "teammembership",
LinkFromAttributeName = "teamid",
LinkToAttributeName = "teamid",
LinkCriteria = new FilterExpression
{
Conditions =
{
new ConditionExpression("systemuserid", ConditionOperator.Equal, currentUserId)
}
}
});
return SystemService.RetrieveMultiple(query).Entities;
}
/// <summary>
/// Gets id of current organization
/// </summary>
public virtual Guid GetOrganizationId()
{
var me = (WhoAmIResponse)SystemService.Execute(new WhoAmIRequest());
return me.OrganizationId;
}
/// <summary>
/// Gets name of current organization
/// </summary>
public virtual string GetOrganizationName()
{
var orgId = GetOrganizationId();
var org = SystemService.Retrieve("organization", orgId, new ColumnSet("name"));
return org["name"] as string;
}
/// <summary>
/// Gets current language id
/// </summary>
public string GetCurrentLcid()
{
var userSettings = GetUserSettings();
if (userSettings == null)
{
return GetOrganizationBaseLanguageLcid().ToString();
}
int currentLcid;
int.TryParse(userSettings.Attributes["uilanguageid"].ToString(), out currentLcid);
return (currentLcid != 0 ? currentLcid : GetOrganizationBaseLanguageLcid()).ToString();
}
/// <summary>
/// Gets base currency id.
/// </summary>
/// <returns>Currency id of the organization.</returns>
public Guid GetBaseCurrencyId()
{
var orgId = GetOrganizationId();
var org = SystemService.Retrieve("organization", orgId, new ColumnSet("basecurrencyid"));
var baseCurrencyId = (EntityReference)org["basecurrencyid"];
return baseCurrencyId != null ? baseCurrencyId.Id : Guid.Empty;
}
/// <summary>
/// Gets base language code.
/// </summary>
/// <returns>Base language code of the organization.</returns>
public int? GetOrganizationBaseLanguageLcid()
{
var org = SystemService.Retrieve("organization", GetOrganizationId(), new ColumnSet("languagecode"));
var languageCode = (int?)org["languagecode"];
return languageCode ?? 1033;
}
/// <summary>
/// Creates link between two entity instances in a many-to-many relationship
/// </summary>
public void CreateManyToManyRelationship(string firstEntityName, Guid firstEntityId, string secondEntityName, Guid secondEntityId, string roleName, bool userOwned = false)
{
var request =
new AssociateEntitiesRequest
{
Moniker1 = new EntityReference(firstEntityName, firstEntityId),
Moniker2 = new EntityReference(secondEntityName, secondEntityId),
RelationshipName = roleName
};
var orgService = GetOrgService(userOwned);
orgService.Execute(request);
}
/// <summary>
/// Creates link between two entity instances in a many-to-many relationship
/// </summary>
public void CreateManyToManyRelationship(EntityReference firstEntity, EntityReference secondEntity, string roleName, bool userOwned = false)
{
var request =
new AssociateEntitiesRequest
{
Moniker1 = firstEntity,
Moniker2 = secondEntity,
RelationshipName = roleName
};
var orgService = GetOrgService(userOwned);
orgService.Execute(request);
}
/// <summary>
/// Initializes one entity based on another. Uses Crm Mapping to fill fields based on parent entity fields
/// </summary>
/// <returns>initialized entity</returns>
public Entity InitializeEntityFromAnother(string targetEntityName, string sourceEntityName, Guid sourceId, bool userOwned = false)
{
var request =
new InitializeFromRequest
{
EntityMoniker = new EntityReference(sourceEntityName, sourceId),
TargetEntityName = targetEntityName,
TargetFieldType = TargetFieldType.All
};
var orgService = GetOrgService(userOwned);
var response = (InitializeFromResponse)orgService.Execute(request);
return response.Entity;
}
/// <summary>
/// Change state of an entity
/// </summary>
public void SetState(string entityName, Guid entityId, int statecode, int statuscode, bool userOwned = false)
{
var request =
new SetStateRequest
{
EntityMoniker = new EntityReference(entityName, entityId),
State = new OptionSetValue(statecode),
Status = new OptionSetValue(statuscode)
};
var orgService = GetOrgService(userOwned);
orgService.Execute(request);
}
/// <summary>
/// Get current ObjectTypeCode of entity
/// </summary>
/// <param name="entityLogicalName">Entity logical name</param>
/// <returns>ObjectTypeCode value if found, otherwise int.MinValue</returns>
public int? GetObjectTypeCode(string entityLogicalName)
{
var request = new RetrieveEntityRequest
{
LogicalName = entityLogicalName
};
var response = (RetrieveEntityResponse)SystemService.Execute(request);
return response.EntityMetadata.ObjectTypeCode;
}
/// <summary>
/// Add annotation to entity
/// </summary>
/// <param name="entity">Entity to add annotation</param>
/// <param name="description">Annotation description</param>
/// <param name="fileName">Attachment file name</param>
/// <param name="fileBytes">File bytes</param>
/// <returns>Id of added annotation</returns>
public Guid AddAnnotation(EntityReference entity, string description, string fileName, byte[] fileBytes)
{
var annotation = new Entity("annotation");
if (!string.IsNullOrWhiteSpace(description))
{
annotation["subject"] = description;
}
if (!string.IsNullOrWhiteSpace(fileName) && fileBytes != null && fileBytes.Length > 0)
{
annotation["filename"] = fileName;
annotation["documentbody"] = Convert.ToBase64String(fileBytes);
}
if (annotation.Attributes.Count == 0)
{
return Guid.Empty;
}
annotation["objecttypecode"] = entity.LogicalName;
annotation["objectid"] = entity;
return SystemService.Create(annotation);
}
/// <summary>
/// Retrieve all entities from CRM (instead first 5000 by default)
/// </summary>
public IEnumerable<T> RetrieveMultipleAllPages<T>(QueryExpression query, int batchSize = 5000, bool userOwned = false) where T : Entity
{
if (query == null)
{
throw new ArgumentNullException("query");
}
query.PageInfo =
new PagingInfo
{
PageNumber = 1,
Count = batchSize,
ReturnTotalRecordCount = false
};
var orgService = GetOrgService(userOwned);
var entitiesCollection = orgService.RetrieveMultiple(query);
var entities = entitiesCollection.Entities;
while (entitiesCollection.MoreRecords)
{
query.PageInfo.PageNumber++;
query.PageInfo.PagingCookie = entitiesCollection.PagingCookie;
entitiesCollection = orgService.RetrieveMultiple(query);
entities.AddRange(entitiesCollection.Entities);
}
return entities.Cast<T>();
}
public IEnumerable<T> RetrieveMultipleAllPages<T>(QueryByAttribute query, int batchSize = 5000, bool userOwned = false) where T : Entity
{
if (query == null)
{
throw new ArgumentNullException("query");
}
if (query.PageInfo == null)
{
query.PageInfo =
new PagingInfo
{
PageNumber = 1,
Count = batchSize,
ReturnTotalRecordCount = false
};
}
var orgService = GetOrgService(userOwned);
var entitiesCollection = orgService.RetrieveMultiple(query);
var entities = entitiesCollection.Entities;
while (entitiesCollection.MoreRecords)
{
query.PageInfo.PageNumber++;
query.PageInfo.PagingCookie = entitiesCollection.PagingCookie;
entitiesCollection = orgService.RetrieveMultiple(query);
entities.AddRange(entitiesCollection.Entities);
}
return entities.Cast<T>();
}
public IEnumerable<T> ExecuteMultiple<T>(IEnumerable<T> entities, Func<T, OrganizationRequest> requestBuilder, int batchSize = 1000, bool userOwned = false) where T : Entity
{
var records = entities as IList<T> ?? entities.ToList();
var faultedRequests = new List<OrganizationRequest>();
var orgService = GetOrgService(userOwned);
foreach (var batch in records.Batch(batchSize))
{
var request =
new ExecuteMultipleRequest
{
Settings =
new ExecuteMultipleSettings
{
ContinueOnError = true,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
foreach (var entity in batch)
{
request.Requests.Add(requestBuilder(entity));
}
var response = (ExecuteMultipleResponse)orgService.Execute(request);
var faultedResponses = response.Responses.Where(responseItem => responseItem.Fault != null).ToList();
foreach (var faultedResponse in faultedResponses)
{
LogExecuteMultipleFault(faultedResponse.Fault);
}
faultedRequests.AddRange(faultedResponses.Select(responseItem => request.Requests[responseItem.RequestIndex]).ToList());
}
var unprocessed = faultedRequests.Select(GetRequestTarget<T>);
return records.Except(unprocessed);
}
public void Associate(Entity current, IEnumerable<Entity> entitiesToAssociate, string relationshipName, bool userOwned = false)
{
var relatedEntities = new EntityReferenceCollection();
relatedEntities.AddRange(entitiesToAssociate.Where(e => e != null).Select(e => e.ToEntityReference()));
var relationship = new Relationship(relationshipName);
var orgService = GetOrgService(userOwned);
orgService.Associate(current.LogicalName, current.Id, relationship, relatedEntities);
}
public void Disassociate(Entity current, IEnumerable<Entity> entitiesToDisassociate, string relationshipName, bool deleteEntities, bool userOwned = false)
{
var entities = new EntityReferenceCollection();
var enumerable = entitiesToDisassociate as IList<Entity> ?? entitiesToDisassociate.ToList();
entities.AddRange(enumerable.Where(e => e != null).Select(e => e.ToEntityReference()));
var relationship = new Relationship(relationshipName);
var orgService = GetOrgService(userOwned);
orgService.Disassociate(current.LogicalName, current.Id, relationship, entities);
if (deleteEntities)
{
foreach (var entityToDelete in enumerable)
{
orgService.Delete(entityToDelete.LogicalName, entityToDelete.Id);
}
}
}
public void RestoreRecordFromAudit(string entityLogicalName, Guid entityId)
{
var changeRequest = new RetrieveRecordChangeHistoryRequest
{
Target = new EntityReference(entityLogicalName, entityId)
};
var changeResponse = (RetrieveRecordChangeHistoryResponse)SystemService.Execute(changeRequest);
var details = changeResponse.AuditDetailCollection;
for (var count = 0; count < details.Count; count++)
{
var detail = details[count] as AttributeAuditDetail;
if (detail == null)
{
continue;
}
if (detail.NewValue != null || detail.OldValue == null)
{
continue;
}
// Восстанавливаем запись
var entity = detail.OldValue;
SystemService.Create(entity);
break;
}
}
/// <summary>
/// Get timezone of current user
/// </summary>
public int GetCurrentUserTimeZone()
{
var userSettings = GetUserSettings();
return userSettings == null ? 0 : userSettings.GetAttributeValue<int>("timezonecode");
}
/// <summary>
/// Get local time
/// </summary>
/// <param name="utcTime">Current UTC time</param>
/// <param name="timeZoneCode">Code of time zone</param>
/// <returns>Local time</returns>
public DateTime? RetrieveLocalTimeFromUTCTime(DateTime? utcTime, int? timeZoneCode)
{
if (!utcTime.HasValue)
{
return null;
}
if (!timeZoneCode.HasValue)
{
return DateTime.UtcNow;
}
return RetrieveLocalTimeFromUTCTime(utcTime.Value, timeZoneCode.Value);
}
/// <summary>
/// Returns user local datetime
/// </summary>
/// <param name="date">Date probably UTC</param>
/// <param name="userId">User identifier</param>
/// <param name="userdate">Conversion result</param>
public bool TryGetUserDateTime(DateTime? date, Guid userId, out DateTime? userdate)
{
try
{
var timezonecode = (int?)GetUserSettings(userId)["timezonecode"];
userdate = RetrieveLocalTimeFromUTCTime(date, timezonecode);
return true;
}
catch
{
Logger.Warn("Systemuser (Id = '{0}') doesn't have timezone settings", userId);
userdate = null;
return false;
}
}
/// <summary>
/// Get UTC time
/// </summary>
/// <param name="localTime">Current local time</param>
/// <param name="timeZoneCode">Code of time zone</param>
/// <returns>UTC time</returns>
public DateTime RetrieveUTCTimeFromLocalTime(DateTime localTime, int timeZoneCode)
{
var request = new UtcTimeFromLocalTimeRequest
{
TimeZoneCode = timeZoneCode,
LocalTime = localTime
};
var response = (UtcTimeFromLocalTimeResponse)SystemService.Execute(request);
return response.UtcTime;
}
/// <summary>
/// Назначить ответственного
/// </summary>
public void AssignOwner(EntityReference target, EntityReference owner, bool userOwned = false)
{
if (owner == null)
{
return;
}
var orgService = GetOrgService(userOwned);
try
{
var request =
new AssignRequest
{
Assignee = owner,
Target = target
};
orgService.Execute(request);
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException(string.Format("Не удалось назначить на сущности {0} ответственного. Ошибка: {1}", target.LogicalName, ex.Message));
}
}
/// <summary>
/// Grant shared access on target entity with principal
/// </summary>
public void GrantAccess(EntityReference target, EntityReference principal, AccessRights accessRights)
{
var grantAccessRequest = new GrantAccessRequest
{
PrincipalAccess = new PrincipalAccess
{
AccessMask = accessRights,
Principal = principal
},
Target = target
};
SystemService.Execute(grantAccessRequest);
}
/// <summary>
/// Modify shared access on target entity with principal
/// </summary>
public void ModifyAccess(EntityReference target, EntityReference principal, AccessRights accessRights)
{
var modifyAccessRequest = new ModifyAccessRequest
{
PrincipalAccess = new PrincipalAccess
{
AccessMask = accessRights,
Principal = principal
},
Target = target
};
SystemService.Execute(modifyAccessRequest);
}
/// <summary>
/// Revoke shared access on target entity with revokee
/// </summary>
public void RevokeAccess(EntityReference target, EntityReference revokee)
{
var revokeAccessRequest = new RevokeAccessRequest
{
Revokee = revokee,
Target = target
};
SystemService.Execute(revokeAccessRequest);
}
/// <summary>
/// Get attachments maximum file size
/// </summary>
public int GetMaxUploadFileSize()
{
var query = new QueryExpression("organization")
{
ColumnSet = new ColumnSet("maxuploadfilesize")
};
query.Criteria.AddCondition("organizationid", ConditionOperator.Equal, GetOrganizationId());
var org = SystemService.RetrieveMultiple(query).Entities.FirstOrDefault();
if (org != null)
{
return (int)org["maxuploadfilesize"];
}
return 0;
}
/// <summary>
/// Execute workflow on entity
/// </summary>
/// <returns>AsyncOperation Id</returns>
public Guid ExecuteWorkflow(Guid workflowId, Guid entityId, bool userOwned = false)
{
if (workflowId == Guid.Empty || entityId == Guid.Empty)
{
return Guid.Empty;
}
var request =
new ExecuteWorkflowRequest
{
WorkflowId = workflowId,
EntityId = entityId
};
var orgService = GetOrgService(userOwned);
var response = (ExecuteWorkflowResponse)orgService.Execute(request);
return response.Id;
}
public string GetOptionSetLabel(string optionSetName, OptionSetValue selectedOptionSetValue)
{
var request = new RetrieveOptionSetRequest
{
Name = optionSetName
};
var baseLanguage = GetOrganizationBaseLanguageLcid();
return (from option in ((OptionSetMetadata)((RetrieveOptionSetResponse)SystemService.Execute(request)).OptionSetMetadata).Options
where (option.Value != selectedOptionSetValue.Value ? 0 : option.Value) != 0
let localizedLabel =
(from ll in option.Label.LocalizedLabels
where ll.LanguageCode == baseLanguage
select ll).FirstOrDefault()
select localizedLabel == null
? option.Label.UserLocalizedLabel.Label
: localizedLabel.Label).FirstOrDefault();
}
private static T GetRequestTarget<T>(OrganizationRequest request) where T : Entity
{
var createRequest = request as CreateRequest;
if (createRequest != null)
{
return (T)createRequest.Target;
}
var updateRequest = request as UpdateRequest;
if (updateRequest != null)
{
return (T)updateRequest.Target;
}
var setstateRequest = request as SetStateRequest;
if (setstateRequest != null)
{
return new Entity(setstateRequest.EntityMoniker.LogicalName)
{
Id = setstateRequest.EntityMoniker.Id
}.ToEntity<T>();
}
return default(T);
}
private DateTime RetrieveLocalTimeFromUTCTime(DateTime utcTime, int timeZoneCode)
{
var request = new LocalTimeFromUtcTimeRequest
{
TimeZoneCode = timeZoneCode,
UtcTime = utcTime.ToUniversalTime()
};
var response = (LocalTimeFromUtcTimeResponse)SystemService.Execute(request);
return response.LocalTime;
}
private IEnumerable<T> GetEntities<T>(
string entityName,
IEnumerable<string> attributesNames = null,
FilterExpression criteria = null,
IEnumerable<LinkEntity> linkedEntities = null,
IEnumerable<OrderExpression> orders = null,
bool noLock = false,
bool distinct = false,
PagingInfo pagingInfo = null) where T : Entity
{
var query = new QueryExpression(entityName)
{
ColumnSet = TranslateColumnSet(attributesNames)
};
if (criteria != null)
{
query.Criteria = criteria;
}
if (linkedEntities != null)
{
query.LinkEntities.AddRange(linkedEntities);
}
query.Distinct = distinct;
if (orders != null)
{
query.Orders.AddRange(orders);
}
if (pagingInfo != null)
{
query.PageInfo = pagingInfo;
}
query.NoLock = noLock;
return SystemService.RetrieveMultiple(query).Entities.Cast<T>();
}
private ColumnSet TranslateColumnSet(IEnumerable<string> columnSet)
{
var result = new ColumnSet();
if (columnSet != null)
{
var enumerable = columnSet as IList<string> ?? columnSet.ToList();
if (enumerable.Any())
{
result.AllColumns = false;
result.Columns.AddRange(enumerable);
return result;
}
}
result.AllColumns = true;
return result;
}
private void LogExecuteMultipleFault(OrganizationServiceFault fault)
{
Logger.Error("Произошла ошибка выполнения запроса.\nErrorCode: {0},\nMessage: {1},\nErrorDetails: {2},\nTraceText: {3}",
fault.ErrorCode,
fault.Message,
fault.ErrorDetails,
fault.TraceText);
if (fault.InnerFault != null)
{
LogExecuteMultipleFault(fault.InnerFault);
}
}
private IOrganizationService GetOrgService(bool userOwned)
{
return userOwned ? UserService : SystemService;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment