Morning Girl

Web API, Windows, C#, .NET, Dynamics 365/CRM etc..

CData ADO.NET Provider が .NET Coreに対応したので、試しにXamarin サンプルアプリ を Dynamics 365 のタスク管理アプリにしてみる

この記事は「C# その2 Advent Calendar 2018」の記事です。

qiita.com

手前味噌なんですが、最近 CData ADO.NET Provider が .NET Standard に対応しました! やったね!

https://www.cdata.com/jp/ado/

f:id:sugimomoto:20181208172504p:plain

それじゃあ、Unityt でも Xamarin でも使えるではないか! ということで、個人的にあまり今まで触れてこなかったXamarin を体験しながら、試してみた! というのが本記事の趣旨になります。

CData ADO.NET Provider って何?

通常SQL ServerMySQLといったRDBにアクセスするために提供されているADO.NETを、Dynamics 365 や SalesforceTwitterFacebookといったWeb APIを提供しているサービスに接続できるようにしてしまうというDriver製品です。

例えばTwitter であれば、「Select * From Tweets」とクエリすれば、タイムラインの一覧が取得でき

「Insert into Tweets(Text)values('Hello World!')」とクエリすれば、ツイートができるという、摩訶不思議(?)なライブラリです。

ついでに Dynamics 365 って何?

Microsoft が提供する 次世代のビジネス アプリケーション!!

つまるところ、マイクロソフトが提供するCRMERPクラウドサービスです。(Office365と間違えないでね)

f:id:sugimomoto:20181208172532p:plain

用途に応じて様々なエディションがあるのですが、今回はDynamics 365 for Customer Engagement(旧Dynamics CRM)を使います。

Dynamics 365 for Customer Engagement では Web API(OData・SOAP)が提供されているので、これを使って外部連携のアプリケーションを開発することが可能です。

出来上がりはこんな感じ

せっかくなので作ったXamarin アプリをAmazon Fire HDで動かして、ムービーを撮ってみました。

Dynamics 365 のタスク情報を 参照・作成・更新・削除 できる Xamarin です。

面倒なことはせず、モバイル アプリテンプレートを使って、処理部分だけ拡張しています。

youtu.be

サンプルでは「MockDataStore」で生成されたメモリ上のアイテムですが、IDataStoreを継承したクラスを作成して、そこからDynamics 365 のデータソースへADO.NET Proivder 経由でアクセスしているだけといえばだけです。

大した実装はしていないですが、ソースコードは以下に置いています。

github.com

必要な環境

・Visual Stduio 2017 (Xamarin開発用のモジュールを入れておきます)

Android Studio(Emulater)、Java SDKなどXamarin Android開発用のモジュール一式

・Dynamics 365 トライアルアカウント

・CData ADO.NET Providr for Dynamics 365

CData ADO.NET Providr for Dynamics 365 のインストール

今回使うCData ADO.NET Providr for Dynamics 365 を予めインストールしておきます。(トライアルがあるので、それを使います。)

以下はDynamics CRMと書かれていますが、Dynamics 365 for Customer Engagement にも接続可能です。

https://www.cdata.com/jp/drivers/dynamicscrm/ado/

Xamarinテンプレートを使って雛形を作成

Visual Stduio を立ち上げてあらたらしく「モバイルアプリ(Xamarin.Forms)」を作成します。

f:id:sugimomoto:20181208172635p:plain

テンプレートは「Master-Detail」を選択し、これをベースに改良します。

f:id:sugimomoto:20181208172859p:plain

参照の追加

プロジェクトを作成したら、まずは先程インストールしたCData ADO.NET Providerの参照です。

以下のフォルダにある.NET Standard ライブラリ「System.Data.CData.DynamicsCRM.dll」を参照します。

C:\Program Files\CData\CData ADO.NET Provider for Dynamics CRM 2018J\lib\netstandard2.0

f:id:sugimomoto:20181208172906p:plain

実装

これで準備は整いました。あとは心置きなく改良します。

今回は「Master-Detail」テンプレートに最初から備わっている「MockDataStore.cs」を差し替える感じにします。

新しく「Dynamics365DataStore」というクラスを作成して、以下のように構成しました。

github.com

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.CData.DynamicsCRM;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CDataSampleApp.Models;

namespace CDataSampleApp.Services
{
    class Dynamics365DataStore : IDataStore<Item>
    {
        private string connectionString = "User=XXXXX@XXXXX.onmicrosoft.com;Password=XXXXXX;URL=https://XXXXXXX.crm7.dynamics.com/;CRM Version=CRM Online;RTK=XXXXXXX;";

        public async Task<bool> AddItemAsync(Item item)
        {
            var sql = $"insert into Task(Subject,Description)values('{item.Text}','{item.Description}')";
            var result = ExecuteForDynamics365(sql);

            return await Task.FromResult(result);
        }

        public async Task<bool> UpdateItemAsync(Item item)
        {
            var sql = $"Update Task set Subject = '{item.Text}', Description = '{item.Description}' where Id = '{item.Id}'";
            var result = ExecuteForDynamics365(sql);

            return await Task.FromResult(result);
        }

        public async Task<bool> DeleteItemAsync(string id)
        {
            var sql = $"Delete from Task where Id = '{id}'";
            var result = ExecuteForDynamics365(sql);

            return await Task.FromResult(result);
        }

        public async Task<Item> GetItemAsync(string id)
        {
            var resultItem = QueryForDynamics365ById(id);
            return await Task.FromResult(resultItem);
        }

        public async Task<IEnumerable<Item>> GetItemsAsync(bool forceRefresh = false)
        {
            var resultItems = QueryForDynamics365();
            return await Task.FromResult(resultItems);
        }

        private bool ExecuteForDynamics365(string sql)
        {
            var response = 0;

            using (var connection = new DynamicsCRMConnection(connectionString))
            {
                var cmd = new DynamicsCRMCommand(sql, connection);
                response = cmd.ExecuteNonQuery();
            }

            return response == 0 ? false : true;
        }

        private Item QueryForDynamics365ById(string id)
        {
            return QueryForDynamics365(id).FirstOrDefault();
        }

        private List<Item> QueryForDynamics365()
        {
            return QueryForDynamics365(null);
        }

        private List<Item> QueryForDynamics365(string id)
        {
            List<Item> items = new List<Item>();

            using (var connection = new DynamicsCRMConnection(connectionString))
            {
                var sql = "SELECT Id, Subject, Description FROM Task";
                sql += id == null ? "" : $" where Id = '{id}'";

                var dataAdapter = new DynamicsCRMDataAdapter(sql, connection);
                var table = new DataTable();
                dataAdapter.Fill(table);

                foreach (DataRow row in table.Rows)
                    items.Add(new Item()
                    {
                        Id = row["Id"].ToString(),
                        Text = row["Subject"].ToString(),
                        Description = row["Description"].ToString()
                    });
            }

            return items;
        }


    }
}

見てもらえば分かる通り、通常であればDynamics 365 Web APIをHTTPリクエストして、JSONをパースなりなんなりするところですが、全部ADO.NET Provider経由で「DataAdapter」を利用しながら実装しています。

(本当はEntityFramework Coreを使えればよかったんですが、まだCData ADO.NET Provider が対応していないので、、、。)

「SELECT Id, Subject, Description FROM Task」でレコードの一覧が

接続文字列は今回わかりやすいようにソースコードに加えていますが、実際にはユーザーからの入力を受け取るなり、Settingで保持するなり対処する必要があるかと思います。

あとは、もともとMockDataStoreをインスタンス化していた、「BaseViewModel」を差し替えるだけです。

namespace CDataSampleApp.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        // ここの new MockDataStore を new Dynamics365DataStore に変更
        public IDataStore<Item> DataStore => DependencyService.Get<IDataStore<Item>>() ?? new Dynamics365DataStore();

あと、初期状態では、Insertしかテンプレートが対応していないので、UpdateとDeleteのボタンとかも、GitHubには追加しています。(もうちょっと調整が必要ですが、、、。)

最後に

Xamarin は Xamarin でおそらくモバイルアプリ的データ操作のお作法がいろいろとあるかと思うのですが、今回は単純にクラウドサービスにつないで見るということを主眼にやっちゃってます。

CData ADO.NET ProviderにはローカルのSQL Liteに取得したテーブルをキャッシュ用としてレプリケートしてしまう機能があるので、それと組み合わせられると、パフォーマンス的な観点やオフライン動作なども対応できないなかーと考えたりです。