認知的プログラミング - LINQ to SQL on SQLCLR

LINQ to SQL を SQLCLR で使えない理由の一つとして、O/R デザイナで生成された DataContext 派生クラスが、static メンバを生成してしまうからと前回書きましたが、static メンバは、ちょっとした工夫で使えることがわかりました。

  • これをしたからといって、LINQ to SQL を SQLCLR ですぐ使えるようになるわけではないのですが、あくまで副産物ということで。

static メンバを使えるようにする

SQLCLR 上に配置される CLR アセンブリは、safe アセンブリである限り、動的 static プロパティへの代入を行うコードが存在すると配置時にエラーになりますが、readonly キーワードを追加すれば配置が可能となります。念のため説明しておくと、readonly キーワードは、コンストラクタでのみ代入可能で、それ以降は参照しかできないというものです。

そもそも、なぜそこまで static メンバにこだわるのかという話もありますが、static メンバが使えることによって、SQLCLR アセンブリで、データキャッシュなどアセンブリ内における共有オブジェクトを実現したかったからです。また、デザインパターンにおけるシングルトンパターンも、static メンバを利用して実装されるので、static メンバが使えることは、設計上非常に大きな意味を持つと考えています。

話が横道にそれてしまいましたが、動的 static メンバを実現することは実に簡単で、自前で用意したクラスオブジェクトを readonly static メンバにするというものです。



using System;
using System.Data.Sql;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

namespace SqlClrEvaluation
{
    public class SharedObject
    {
        public int _n;

        public SharedObject()
        {
            _n = 0;
        }
    }

    public class SharedObjectTest
    {
        private static readonly SharedObject _so = new SharedObject();

        [SqlFunction]
        public static SqlInt32 SqlFunction1()
        {
            int n = _so._n;
            _so._n++;
            return n;
        }

        [SqlFunction]
        public static SqlInt32 SqlFunction2()
        {
            int n = _so._n;
            _so._n++;
            return n;
        }
    }
}


非常にシンプルな SqlFunction のコードですが、クエリから SELECT SqlFunction1() などとすれば、実行毎に出力が1づつインクリメントされることが判ります。


また、SqlFunction2() を実行しても同様にインクリメントされ、その値は SqlFunction1() と SqlFunction2() で共有されていることが確認できます。


一見すると、どうというコードではないのですが、この機能を応用することで、複数のトランザクション間で様々な値やオブジェクトを共有することができ、関数(もしくはストアドプロシージャ)単位で完結しなければならなかった制限を回避することが可能になります。


実際に、この static メンバ内部に保持されたデータは、アセンブリの再配置を行うまで保持されるので、オブジェクトをデータベースにシリアライズ/デシリアライズする機能を付け加えれば、再配置時にオブジェクトの内容をデータベースに待避/復旧することで、永続化することが可能になるでしょう。

さらなる課題

SQLCLR で LINQ to SQL を使うには、さらに問題が存在します。それは、LINQ to SQL を使うための System.Data.Linq アセンブリが SQLCLR 環境上には存在しないことです。

LINQ to Object を実現するための System.Linq は存在するので、自前で LINQ to Object に対応した O/R マッピングクラスを定義するしかないのでしょうか。


safe 環境での取り組みはまだまだ続きそうです。