Sono finalmente in grado di generare una classe associata in anticipo con un set di entità filtrate e un set di opzioni prive di errori. Ho trovato la maggior parte della mia risposta attraverso questo thread, quindi grazie ragazzi. Il problema però è difficile compilare tutti i vari suggerimenti in qualcosa che in realtà ... compila. Quindi ho pensato di pubblicare la mia soluzione finale a beneficio degli altri, ecco cosa ha funzionato per me.
ho usato Erik piscina, Manny Grewal di, e la soluzione di Peter Majeed per emettere solo le enumerazioni distinti con valori corretti, poi combinato che, con la soluzione di Gaurav Dalal (aggiornato da JFK007 di correggere l'errore espressi) di riscrivere la SetAttributeValue
che ha causato l'errore (int)(value)
. E come bonus aggiuntivo, ho usato la stessa soluzione per filtrare i set di opzioni distinte per filtrare anche i valori di set di opzioni distinti (che era un problema nella mia org).
Il risultato è una libreria di classi che contiene CodeWriterFilter
e CustomizeCodeDomService
, il file batch cmd per eseguire CrmSvcUtil.exe
, e il filter.xml
per filtrare le entità.
Nella libreria di classi aggiungere riferimenti a CrmSvcUtil.exe
, Microsoft.Xrm.Sdk
, e quindi System.Runtime.Serialization
Compilare la DLL e copiarlo nella stessa cartella come il vostro CrmSvcUtil.exe
. Utilizzare il comando che ho incluso per fare riferimento al nuovo assembly e creare il file di classe associato all'inizio.
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
comando Batch File:
@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>
JFYI. È stata posta una domanda simile [qui] (http://stackoverflow.com/questions/8443981/crm-2011-generated-code-using-icodewriterfilterservice-fails-to-build). – paramosh
@paramosh: Sì, ho appena visto quella domanda ora per qualche motivo. Penso che l'avrei postata comunque perché non ero chiaro delle conseguenze relative alla personalizzazione del metodo 'GenerateOptionSet', ma comunque, si spera che la risposta di Guarav darà una risposta. –