Наконец-то я смог сгенерировать класс с ранней привязкой с отфильтрованным набором сущностей и безошибочным набором опций.Я нашел большую часть своего ответа через эту ветку, так что, спасибо, ребята.Проблема, однако, в том, что сложно скомпилировать все различные предложения в нечто, что на самом деле ... компилируется.Поэтому я решил опубликовать свое окончательное решение на благо других, вот что сработало для меня.
Я использовал решение Эрика Пула, Мэнни Гривала и Питера Маджида для вывода только отдельных перечислений с правильными значениями, а затем объединил его с решением Гаурава Далала (обновлено JFK007 для исправления ошибки приведения) для перезаписи SetAttributeValue
, что вызвало ошибку (int)(value)
.И в качестве дополнительного бонуса я использовал то же решение для фильтрации отдельных наборов опций, чтобы также фильтровать различные значения набора опций (что было проблемой в моей организации).
В результате получается библиотека классов, содержащая CodeWriterFilter
и CustomizeCodeDomService
, командный файл cmd для запуска CrmSvcUtil.exe
и filter.xml
для фильтрации объектов.
В вашей библиотеке классов добавьте ссылки на CrmSvcUtil.exe
, Microsoft.Xrm.Sdk
и System.Runtime.Serialization
, затем скомпилируйте dll и скопируйте его в ту же папку, что и ваша CrmSvcUtil.exe
.Используйте команду, которую я включил, чтобы сослаться на вашу новую сборку и построить файл раннего связанного класса.
CodeWriterFilter
:
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
using System.Text.RegularExpressions;
using Microsoft.Xrm.Sdk;
namespace SvcUtilFilter
{
/// <summary>
/// CodeWriterFilter for CrmSvcUtil that reads list of entities from an xml file to
/// determine whether or not the entity class should be generated.
/// </summary>
public class CodeWriterFilter : ICodeWriterFilterService
{
//list of entity names to generate classes for.
private HashSet<string> _validEntities = new HashSet<string>();
//reference to the default service.
private ICodeWriterFilterService _defaultService = null;
//list of generated option sets, instantiated in the constructor
private List<string> GeneratedOptionSets;
//list of generated options, instantiated in the constructor
private List<string> GeneratedOptions;
/// <summary>
/// constructor
/// </summary>
/// <param name="defaultService">default implementation</param>
public CodeWriterFilter(ICodeWriterFilterService defaultService)
{
this._defaultService = defaultService;
this.GeneratedOptionSets = new List<string>();
this.GeneratedOptions = new List<string>();
LoadFilterData();
}
/// <summary>
/// loads the entity filter data from the filter.xml file
/// </summary>
private void LoadFilterData()
{
XElement xml = XElement.Load("filter.xml");
XElement entitiesElement = xml.Element("entities");
foreach (XElement entityElement in entitiesElement.Elements("entity")) {
_validEntities.Add(entityElement.Value.ToLowerInvariant());
}
}
/// <summary>
/// /Use filter entity list to determine if the entity class should be generated.
/// </summary>
public bool GenerateEntity(EntityMetadata entityMetadata, IServiceProvider services)
{
return (_validEntities.Contains(entityMetadata.LogicalName.ToLowerInvariant()));
}
//All other methods just use default implementation:
public bool GenerateAttribute(AttributeMetadata attributeMetadata, IServiceProvider services)
{
return _defaultService.GenerateAttribute(attributeMetadata, services);
}
public bool GenerateOption(OptionMetadata optionMetadata, IServiceProvider services)
{
//return _defaultService.GenerateOption(optionMetadata, services);
string label = optionMetadata.Label.UserLocalizedLabel.Label;
//remove spaces and special characters
label = Regex.Replace(label, @"[^a-zA-Z0-9]", string.Empty);
if (label.Length > 0 && !char.IsLetter(label, 0)) {
label = "Number_" + label;
}
else if (label.Length == 0) {
label = "empty";
}
if (!GeneratedOptions.Exists(l=>l.Equals(label))) {
GeneratedOptions.Add(label);
optionMetadata.Label = new Label(label, 1033);
return _defaultService.GenerateOption(optionMetadata, services);
}
else { return false; }
}
public bool GenerateOptionSet(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
{
//return _defaultService.GenerateOptionSet(optionSetMetadata, services);
if (!GeneratedOptionSets.Contains(optionSetMetadata.Name)) {
GeneratedOptionSets.Add(optionSetMetadata.Name);
return true;
}
return _defaultService.GenerateOptionSet(optionSetMetadata, services);
}
public bool GenerateRelationship(RelationshipMetadataBase relationshipMetadata, EntityMetadata otherEntityMetadata, IServiceProvider services)
{
return _defaultService.GenerateRelationship(relationshipMetadata, otherEntityMetadata, services);
}
public bool GenerateServiceContext(IServiceProvider services)
{
return _defaultService.GenerateServiceContext(services);
}
}
}
CustomizeCodeDomService
:
using System;
using System.CodeDom;
using System.Diagnostics;
using System.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
namespace SvcUtilFilter
{
/// <summary>
/// The customize code dom service.
/// </summary>
public sealed class CustomizeCodeDomService : ICustomizeCodeDomService
{
#region Constants and Fields
/// <summary>
/// The metadata.
/// </summary>
private IOrganizationMetadata metadata;
#endregion
#region Properties
#endregion
#region Public Methods
/// <summary>
/// The customize code dom.
/// </summary>
/// <param name="codeCompileUnit">
/// The code compile unit.
/// </param>
/// <param name="services">
/// The services.
/// </param>
public void CustomizeCodeDom(CodeCompileUnit codeCompileUnit, IServiceProvider services)
{
// Locate the namespace to use
CodeNamespace codeNamespace = codeCompileUnit.Namespaces[0];
var metadataProviderService = (IMetadataProviderService)services.GetService(typeof(IMetadataProviderService));
var filterService = (ICodeWriterFilterService)services.GetService(typeof(ICodeWriterFilterService));
this.metadata = metadataProviderService.LoadMetadata();
foreach (EntityMetadata entityMetadata in this.metadata.Entities) {
if (filterService.GenerateEntity(entityMetadata, services)) {
CodeTypeDeclaration entityClass =
codeNamespace.Types.Cast<CodeTypeDeclaration>().First(codeType => codeType.Name.ToUpper() == entityMetadata.SchemaName.ToUpper());
UpdateEnumSetter(entityClass, entityMetadata);
}
}
}
#endregion
#region Private Methods
private static void UpdateEnumSetter(CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf))) {
AttributeMetadata currentMetadata = attributeMetadata;
foreach (CodeTypeMember codeMembers in entityClass.Members.Cast<CodeTypeMember>().Where(codeMembers => codeMembers.Name == currentMetadata.SchemaName)) {
CodeMemberProperty codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet) {
if (attributeMetadata.AttributeType != null && (attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist || attributeMetadata.AttributeType.Value == AttributeTypeCode.Status)) {
if (codeProperty.SetStatements[1].GetType() == typeof(CodeConditionStatement)) {
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] = new CodeSnippetStatement {
Value = String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName)
};
}
else {
codeProperty.SetStatements[1] = new CodeSnippetStatement(String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName));
}
}
}
}
}
}
#endregion
}
}
CrmSvcUtil_run.cmd
Пакетный командный файл:
@echo off
set url=https://[organization].api.crm.dynamics.com/XRMServices/2011/Organization.svc
echo.
echo Generating CrmSvcUtil Proxy class in output folder
echo.
CrmSvcUtil.exe /metadataproviderservice:"MetadataProvider.IfdMetadataProviderService,
MetadataProvider"
/url:https://[organization].api.crm.dynamics.com/XRMServices/2011/Organization.svc /out:Xrm.cs
/namespace:Xrm /serviceContextName:XrmServiceContext /serviceContextPrefix:Xrm
/u:[username] /p:[password]
/codewriterfilter:SvcUtilFilter.CodeWriterFilter,SvcUtilFilter
/codecustomization:SvcUtilFilter.CustomizeCodeDomService,SvcUtilFilter
echo.
pause
filter.xml
<filter>
<entities>
<entity>systemuser</entity>
<entity>team</entity>
<entity>role</entity>
<entity>businessunit</entity>
<entity>account</entity>
<entity>product</entity>
<entity>transactioncurrency</entity>
</entities>
</filter>