読者です 読者をやめる 読者になる 読者になる

【Dynamics CRM 2011】CrmConnectionを使ってDynamicsへの認証・接続を行う

MSDNを読んでいると「Dynamicsへの認証はSDKに含まれているサンプルコードのcrmservicehelpers.cssdk->samplecode->cs->helpercode内)を使うと便利だよ!」みたいな記述をちらほら見かけるのですが、どうにもコンソールアプリケーション以外に適用し辛く、コードを参考にするにはあまりにも複雑な仕組みになっています。

そこでCrmConnectionと言うクラスを使ってもっと簡単にDynamicsへの接続を行いたいと思います。

CrmConnectionへ渡すConnectionString

CrmConnectionはParseメソッドにConectionStringを直接渡すか、コンストラクタConnectionStringSettingsを渡すことで取得できます。

OrganizationServiceCrmOrganizationServiceContextコンストラクタにこのCrmConnectionを受け取るオーバーロードが存在するので、実質ConnectionStringだけで接続と認証を済ませることが出来るわけです。

実際にどんなConnectionStringを渡すかですが、このドキュメントに全部書いてあります。

CrmConnectionを使ったいつものクソみたいなサンプルコードもあるので、これらのドキュメントを参考にしつつ、実際にやってみましょう。

コード

ActiveDirectoryの場合はURLに加えてドメイン名、ユーザ名、パスワードを渡してあげればいいみたいです。

using System;
using System.Linq.Expressions;
using System.Text;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;

namespace CrmConnectionTest
{
    public class CrmConnecter
    {
        public static OrganizationService Connect(string url, string domain, string userName, string password)
        {
            var connectionString = CreateConnectionString
                (
                    Url => url,
                    Domain => domain,
                    Username => userName,
                    Password => password
                );

            var con = CrmConnection.Parse(connectionString);
            return new OrganizationService(con);
        }

        private static string CreateConnectionString(params Expression<Func<object, object>>[] exprs)
        {
            var sb = new StringBuilder();

            foreach (var expr in exprs)
            {
                var obj = expr.Compile().Invoke(null);

                if (obj == null) continue;

                var name = expr.Parameters[0].Name;
                var val = obj.ToString();

                sb.Append(name).Append('=').Append(val).Append(';');
            }

            return sb.ToString();
        }
    }
}

これだけでOKです。

他の認証方法を使ったConnectionStringの作り方はさっきのドキュメントの下の方に書いてあります。

まとめ

汎用的なものを作る場合はともかく、ちょっと実験したいな、ってぐらいなら、これで十分です。

[2014/04/03追記]

OrganizationServiceProxyを作成する場合

OrganizationServiceではなくOrganizationServiceProxyが欲しいケースもあると思います。

その場合もCrmConnectionを使うと簡単です。

public static OrganizationServiceProxy Connect(string server, string organization, string domain, string userName, string password)
{
    var url = "http://" + server + "/" + organization + "/XRMServices/2011/Organization.svc";
    
    var connectionString = CreateConnectionString
        (
            Url => url,
            Domain => domain,
            Username => userName,
            Password => password
        );

    var con = CrmConnection.Parse(connectionString);
    
    return new OrganizationServiceProxy(con.ServiceUri, con.HomeRealmUri, con.ClientCredentials, con.DeviceCredentials);
}

OrganizationServiceProxyを作成する場合(マルチスレッド編)

さらにマルチスレッドでやりたいとなると、もう一工夫必要です。

と言っても

IServiceManagement<IOrganizationService> orgServiceManagement
    = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(organizationUrl));

AuthenticationCredentials authCredentials
    = orgServiceManagement.Authenticate(credentials);

の部分を

var con = CrmConnection.Parse(connectionString);

var orgServiceManagement =
    ServiceConfigurationFactory.CreateManagement<IOrganizationService>(con.ServiceUri);

var authCredentials =
    orgServiceManagement.Authenticate(new AuthenticationCredentials{
        HomeRealm = con.HomeRealmUri,
        ClientCredentials = con.ClientCredentials,
    });

に書き換えるだけです。

メタデータを保持する必要があるため、流石にstaticなメソッドだけでは厳しいのでこんな感じにしました。

using System;
using System.Linq.Expressions;
using System.Text;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;

namespace CrmConnectionTest
{
    public class CrmConnecter
    {
        private readonly IServiceManagement<IOrganizationService> _manager;
        private readonly AuthenticationCredentials _credential;

        public CrmConnecter(string server, string organization, string domain, string userName, string password)
        {
            var url = "http://" + server + "/" + organization + "/XRMServices/2011/Organization.svc";
            var connectionString = CreateConnectionString
                (
                    Url => url,
                    Domain => domain,
                    Username => userName,
                    Password => password
                );

            var connection = CrmConnection.Parse(connectionString);

            _manager = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(connection.ServiceUri);

            _credential = _manager.Authenticate(new AuthenticationCredentials
            {
                HomeRealm = connection.HomeRealmUri,
                ClientCredentials = connection.ClientCredentials,
            });
        }

        public OrganizationServiceProxy GetProxy()
        {
            var proxy = new OrganizationServiceProxy(_manager, _credential.ClientCredentials);

            proxy.EnableProxyTypes();
            return proxy;
        }

        private static string CreateConnectionString(params Expression<Func<object, object>>[] exprs)
        {
            var sb = new StringBuilder();

            foreach (var expr in exprs)
            {
                var obj = expr.Compile().Invoke(null);

                if (obj == null) continue;

                var name = expr.Parameters[0].Name;
                var val = obj.ToString();

                sb.Append(name).Append('=').Append(val).Append(';');
            }

            return sb.ToString();
        }
    }
}

こんな感じに使います。

var connecter = new CrmConnecter(server, organization, domain, userName, password);

Parallel.For<OrganizationServiceProxy>(0, count
    , () => connecter.GetProxy()
    , (i, state, proxy) =>
    {
       // TODO: OrganizationServiceProxyをつかったいろいろ
       // ex.) proxy.Update(entity);
    }
    , proxy => proxy.Dispose());

[2014/04/03追記ここまで]

参考