在IClientMessageInspector中获取当前的IpluginContext或IorganizationService对象

时间:2017-03-15 11:37:59

标签: c# wcf dynamics-crm crm microsoft-dynamics

我正在开发一个调用另一个应用程序WCF服务并解析响应的插件。一切都按预期工作,但在记录xml消息时我遇到了很多问题。

我使用了一个Interceptor,带有以下签名:

public class SFACMessageInspector : IEndpointBehavior, IClientMessageInspector

必须实施以下方法:

public object BeforeSendRequest(ref Message request,IClientChannel channel)
public void AfterReceiveReply(ref Message reply,object correlationState)

在方法中我检索xml消息没有问题,但是现在我需要记录它们,因此我被要求创建一个新的Logger实体,它在两个字段上同时包含请求和响应。

最大的问题是我没有以下任何对象:

IOrganizationService service //this is the object I could use to create the entity
IPluginExecutionContext context  //the context of the plugin, from this I can create a new IorganizationService

作为一个拦截器,我无法将它们作为参数传递,所以我想到了两个选择,它们都没有给我预期的结果:

  • 将服务或上下文声明为静态类变量(例如,来自插件调用类),因此我可以从Inspector访问它。我不太喜欢这种方法,因为上下文是我能想到的不那么静态的对象,并且它可能会在CRM的多用户环境中产生问题。

  • 在给定字符串连接参数的情况下创建新的IOrganizationService对象。我找到的唯一工作对象是Microsoft.Xrm.Client库,遗憾的是不推荐使用,我可以创建一个新对象,如:

var _service = new OrganizationService(CrmConnection.Parse(CRMConnectionString));

我在一个月左右的时间内没有遇到任何问题,但是将我的Dynamics CRM迁移到2016版本,这不再适用了。当代码是LogIntegrationMessage方法(调用服务的位置)被调用时,会引发以下异常,并出现以下一般错误消息:

Inner Exception: Microsoft.Xrm.Sdk.InvalidPluginExecutionException: The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.

我该怎么办?有没有其他方法可以在Interceptor类中创建一个实例,我没有PluginContext或OrganizationService?

提前致谢

2 个答案:

答案 0 :(得分:0)

CRM 2016中的事件您应该能够使用普通的OrganizationServiceProxy类手动连接到CRM(事实上,您使用的OrganizationService类只是此类的包装器)。回到CRM 2011,我们都像这样连接到CRM,网上有很多例子。 CRM SDK(2011年以来的所有版本,2016年都有相同的)在&#34; \ SDK \ SampleCode \ CS \ GeneralProgramming \ Authentication \ AuthenticateWithNoHelp \ AuthenticateWithNoHelp.cs&#34;提供了一个很好的示例,说明如何进行身份验证:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Reflection;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Crm.Sdk.Messages;

namespace Microsoft.Crm.Sdk.Samples
{
    /// <summary>
    /// Demonstrate how to do basic authentication using IServiceManagement and SecurityTokenResponse.
    /// </summary>
    class AuthenticateWithNoHelp
    {
        #region Class Level Members
        // To get discovery service address and organization unique name, 
        // Sign in to your CRM org and click Settings, Customization, Developer Resources.
        // On Developer Resource page, find the discovery service address under Service Endpoints and organization unique name under Your Organization Information.
        private String _discoveryServiceAddress = "https://dev.crm.dynamics.com/XRMServices/2011/Discovery.svc";
        private String _organizationUniqueName = "OrganizationUniqueName";
        // Provide your user name and password.
        private String _userName = "username@mydomain.com";
        private String _password = "password"
    #endregion Class Level Members

    #region How To Sample Code
    /// <summary>
    /// 
    /// </summary>
    public void Run()
    {
        //<snippetAuthenticateWithNoHelp1>
        IServiceManagement<IDiscoveryService> serviceManagement =
                    ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(
                    new Uri(_discoveryServiceAddress));
        AuthenticationProviderType endpointType = serviceManagement.AuthenticationType;

        // Set the credentials.
        AuthenticationCredentials authCredentials = GetCredentials(serviceManagement, endpointType);


        String organizationUri = String.Empty;
        // Get the discovery service proxy.
        using (DiscoveryServiceProxy discoveryProxy =
            GetProxy<IDiscoveryService, DiscoveryServiceProxy>(serviceManagement, authCredentials))
        {
            // Obtain organization information from the Discovery service. 
            if (discoveryProxy != null)
            {
                // Obtain information about the organizations that the system user belongs to.
                OrganizationDetailCollection orgs = DiscoverOrganizations(discoveryProxy);
                // Obtains the Web address (Uri) of the target organization.
                organizationUri = FindOrganization(_organizationUniqueName,
                    orgs.ToArray()).Endpoints[EndpointType.OrganizationService];

            }
        }
        //</snippetAuthenticateWithNoHelp1>


        if (!String.IsNullOrWhiteSpace(organizationUri))
        {
            //<snippetAuthenticateWithNoHelp3>
            IServiceManagement<IOrganizationService> orgServiceManagement =
                ServiceConfigurationFactory.CreateManagement<IOrganizationService>(
                new Uri(organizationUri));

            // Set the credentials.
            AuthenticationCredentials credentials = GetCredentials(orgServiceManagement, endpointType);

            // Get the organization service proxy.
            using (OrganizationServiceProxy organizationProxy =
                GetProxy<IOrganizationService, OrganizationServiceProxy>(orgServiceManagement, credentials))
            {
                // This statement is required to enable early-bound type support.
                organizationProxy.EnableProxyTypes();

                // Now make an SDK call with the organization service proxy.
                // Display information about the logged on user.
                Guid userid = ((WhoAmIResponse)organizationProxy.Execute(
                    new WhoAmIRequest())).UserId;
                SystemUser systemUser = organizationProxy.Retrieve("systemuser", userid,
                    new ColumnSet(new string[] { "firstname", "lastname" })).ToEntity<SystemUser>();
                Console.WriteLine("Logged on user is {0} {1}.",
                    systemUser.FirstName, systemUser.LastName);
            }
            //</snippetAuthenticateWithNoHelp3>
        }

    }

    //<snippetAuthenticateWithNoHelp2>
    /// <summary>
    /// Obtain the AuthenticationCredentials based on AuthenticationProviderType.
    /// </summary>
    /// <param name="service">A service management object.</param>
    /// <param name="endpointType">An AuthenticationProviderType of the CRM environment.</param>
    /// <returns>Get filled credentials.</returns>
    private AuthenticationCredentials GetCredentials<TService>(IServiceManagement<TService> service, AuthenticationProviderType endpointType)
    {
        AuthenticationCredentials authCredentials = new AuthenticationCredentials();

        switch (endpointType)
        {
            case AuthenticationProviderType.ActiveDirectory:
                authCredentials.ClientCredentials.Windows.ClientCredential =
                    new System.Net.NetworkCredential(_userName,
                        _password,
                        _domain);
                break;
            case AuthenticationProviderType.LiveId:
                authCredentials.ClientCredentials.UserName.UserName = _userName;
                authCredentials.ClientCredentials.UserName.Password = _password;
                authCredentials.SupportingCredentials = new AuthenticationCredentials();
                authCredentials.SupportingCredentials.ClientCredentials =
                    Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
                break;
            default: // For Federated and OnlineFederated environments.                    
                authCredentials.ClientCredentials.UserName.UserName = _userName;
                authCredentials.ClientCredentials.UserName.Password = _password;
                // For OnlineFederated single-sign on, you could just use current UserPrincipalName instead of passing user name and password.
                // authCredentials.UserPrincipalName = UserPrincipal.Current.UserPrincipalName;  // Windows Kerberos

                // The service is configured for User Id authentication, but the user might provide Microsoft
                // account credentials. If so, the supporting credentials must contain the device credentials.
                if (endpointType == AuthenticationProviderType.OnlineFederation)
                {
                    IdentityProvider provider = service.GetIdentityProvider(authCredentials.ClientCredentials.UserName.UserName);
                    if (provider != null && provider.IdentityProviderType == IdentityProviderType.LiveId)
                    {
                        authCredentials.SupportingCredentials = new AuthenticationCredentials();
                        authCredentials.SupportingCredentials.ClientCredentials =
                            Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
                    }
                }

                break;
        }

        return authCredentials;
    }
    //</snippetAuthenticateWithNoHelp2>

    /// <summary>
    /// Discovers the organizations that the calling user belongs to.
    /// </summary>
    /// <param name="service">A Discovery service proxy instance.</param>
    /// <returns>Array containing detailed information on each organization that 
    /// the user belongs to.</returns>
    public OrganizationDetailCollection DiscoverOrganizations(
        IDiscoveryService service)
    {
        if (service == null) throw new ArgumentNullException("service");
        RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
        RetrieveOrganizationsResponse orgResponse =
            (RetrieveOrganizationsResponse)service.Execute(orgRequest);

        return orgResponse.Details;
    }

    /// <summary>
    /// Finds a specific organization detail in the array of organization details
    /// returned from the Discovery service.
    /// </summary>
    /// <param name="orgUniqueName">The unique name of the organization to find.</param>
    /// <param name="orgDetails">Array of organization detail object returned from the discovery service.</param>
    /// <returns>Organization details or null if the organization was not found.</returns>
    /// <seealso cref="DiscoveryOrganizations"/>
    public OrganizationDetail FindOrganization(string orgUniqueName,
        OrganizationDetail[] orgDetails)
    {
        if (String.IsNullOrWhiteSpace(orgUniqueName))
            throw new ArgumentNullException("orgUniqueName");
        if (orgDetails == null)
            throw new ArgumentNullException("orgDetails");
        OrganizationDetail orgDetail = null;

        foreach (OrganizationDetail detail in orgDetails)
        {
            if (String.Compare(detail.UniqueName, orgUniqueName,
                StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                orgDetail = detail;
                break;
            }
        }
        return orgDetail;
    }

    /// <summary>
    /// Generic method to obtain discovery/organization service proxy instance.
    /// </summary>
    /// <typeparam name="TService">
    /// Set IDiscoveryService or IOrganizationService type to request respective service proxy instance.
    /// </typeparam>
    /// <typeparam name="TProxy">
    /// Set the return type to either DiscoveryServiceProxy or OrganizationServiceProxy type based on TService type.
    /// </typeparam>
    /// <param name="serviceManagement">An instance of IServiceManagement</param>
    /// <param name="authCredentials">The user's Microsoft Dynamics CRM logon credentials.</param>
    /// <returns></returns>
    /// <snippetAuthenticateWithNoHelp4>
    private TProxy GetProxy<TService, TProxy>(
        IServiceManagement<TService> serviceManagement,
        AuthenticationCredentials authCredentials)
        where TService : class
        where TProxy : ServiceProxy<TService>
    {
        Type classType = typeof(TProxy);

        if (serviceManagement.AuthenticationType !=
            AuthenticationProviderType.ActiveDirectory)
        {
            AuthenticationCredentials tokenCredentials =
                serviceManagement.Authenticate(authCredentials);
            // Obtain discovery/organization service proxy for Federated, LiveId and OnlineFederated environments. 
            // Instantiate a new class of type using the 2 parameter constructor of type IServiceManagement and SecurityTokenResponse.
            return (TProxy)classType
                .GetConstructor(new Type[] { typeof(IServiceManagement<TService>), typeof(SecurityTokenResponse) })
                .Invoke(new object[] { serviceManagement, tokenCredentials.SecurityTokenResponse });
        }

        // Obtain discovery/organization service proxy for ActiveDirectory environment.
        // Instantiate a new class of type using the 2 parameter constructor of type IServiceManagement and ClientCredentials.
        return (TProxy)classType
            .GetConstructor(new Type[] { typeof(IServiceManagement<TService>), typeof(ClientCredentials) })
            .Invoke(new object[] { serviceManagement, authCredentials.ClientCredentials });
    }
    /// </snippetAuthenticateWithNoHelp4>

    #endregion How To Sample Code

    #region Main method

    /// <summary>
    /// Standard Main() method used by most SDK samples.
    /// </summary>
    /// <param name="args"></param>
    static public void Main(string[] args)
    {
        try
        {
            AuthenticateWithNoHelp app = new AuthenticateWithNoHelp();
            app.Run();
        }
        catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
        {
            Console.WriteLine("The application terminated with an error.");
            Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
            Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
            Console.WriteLine("Message: {0}", ex.Detail.Message);
            Console.WriteLine("Trace: {0}", ex.Detail.TraceText);
            Console.WriteLine("Inner Fault: {0}",
                null == ex.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
        }
        catch (System.TimeoutException ex)
        {
            Console.WriteLine("The application terminated with an error.");
            Console.WriteLine("Message: {0}", ex.Message);
            Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
            Console.WriteLine("Inner Fault: {0}",
                null == ex.InnerException.Message ? "No Inner Fault" : ex.InnerException.Message);
        }
        catch (System.Exception ex)
        {
            Console.WriteLine("The application terminated with an error.");
            Console.WriteLine(ex.Message);

            // Display the details of the inner exception.
            if (ex.InnerException != null)
            {
                Console.WriteLine(ex.InnerException.Message);

                FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
                    as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
                if (fe != null)
                {
                    Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
                    Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
                    Console.WriteLine("Message: {0}", fe.Detail.Message);
                    Console.WriteLine("Trace: {0}", fe.Detail.TraceText);
                    Console.WriteLine("Inner Fault: {0}",
                        null == fe.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
                }
            }
        }
        // Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
        // SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.

        finally
        {
            Console.WriteLine("Press <Enter> to exit.");
            Console.ReadLine();
        }
    }
    #endregion Main method
}

} //

正如您所看到的,它比从Microsoft.Xrm.Client获取IOrganizationService更复杂,但应该在您的情况下完成其工作。

当然这是通用代码,可以在大多数情况下使用,但是如果你有普通的Windows身份验证,你的代码将简化为:

ClientCredentials credentials = new ClientCredentials();

        credentials.Windows.ClientCredential = new NetworkCredential( "login", "password", "domain" );

        string orgUrl = "http://ServerName/OrganizationName/XRMServices/2011/Organization.svc";


        using(OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(new Uri(orgUrl), null, credentials, null))
        {
            //use serviceProxy as IOrganizationService
        }

答案 1 :(得分:0)

也许添加一个IOrganizationService字段和一个构造函数来在实例化时添加服务:

public class SFACMessageInspector : IEndpointBehavior, IClientMessageInspector
{
    private IOrganizationService service;

    public SFACMessageInspector(IOrganizationService svc)
    {
        service = svc;
    }
}

然后从插件中实例化:

public void Execute(IServiceProvider serviceProvider)
{
    var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    var crmService = factory.CreateOrganizationService(context.UserId);

    var interceptor = new SFACMessageInspector(crmService);
}

然后,您应该能够在serviceBeforeSendRequest方法中访问AfterReceiveReply,而无需将其作为参数传递。

如果您仍然希望使用连接字符串方法,那么新的方法是通过Xrm.Tooling.Connector程序集中的CrmServiceClient: https://msdn.microsoft.com/en-us/library/dn688177.aspx

例如:

private IOrganizationService createService(string connectionString)
{
    return new CrmServiceClient(connectionString);
}