💡

LINQ to SharePoint の仕組みを理解する

に公開

はじめに

SharePoint 2010 から LINQ to SharePoint という機能が追加されました。これまでの SPQuery によるクエリ検索をより簡単にする仕組みで、Entity Framework を使ったことのある開発者であれば、ほとんど違和感なく利用できます。SPQuery で使われる CAML は開発者にとって直観的ではありませんでしたが、LINQ to SharePoint により SharePoint 開発が大幅に容易になります。

LINQ to SharePoint を利用するには、SPMetal というツールでコードを自動生成します。日本語環境の場合、コードに日本語が混ざったり、不要なリストのエンティティ クラスまで作成されてしまうことがありますが、通常の利用であれば十分に活用できます。今回は、SPMetal で自動生成されるコードを参考に、ゼロからコードを記述する方法をご紹介します。

サンプル コード

https://github.com/karamem0/samples/tree/main/sharepoint-linq-to-sharepoint

実行手順

事前準備

サンプルとして テスト という名前で空のカスタム リストを作成します。

コンテキスト クラス

Entity Framework に DBContext クラスがあるように、LINQ to SharePoint にも DataContext クラスが存在します。コンストラクターでは接続文字列の代わりにサイトの URL を受け取ります。リストの取得は GetList メソッドで行うため、これをプロパティとしてラップします。[1]

public class SharePointDataContext : DataContext
{

    public EntityList<Test> Tests
    {
        get { return this.GetList<Test>("テスト"); }
    }

    public SharePointDataContext(string url)
        : base(url) { }

}

エンティティ クラス

SharePoint にはコンテンツ タイプという概念があり、LINQ to SharePoint ではこれをクラスの継承で表現します。すべてのコンテンツ タイプはアイテムから派生するため、まずは Item クラスを作成します。ContentTypeAttribute 属性でコンテンツ タイプの名前と ID を指定します。ここで指定する名前がサイトの定義と一致していれば、クラス名は任意で問題ありません。

[ContentType(Name = "アイテム", Id = "0x01")]
public class Item : ITrackEntityState, ITrackOriginalValues, INotifyPropertyChanged, INotifyPropertyChanging { ... }

アイテムを取得するだけであれば上記のみで十分ですが、アイテムを更新する場合は、4 つのインターフェイスを継承する必要があります。それぞれのインターフェイスの役割は以下のとおりです。

  • ITrackEntityState インターフェイスはエンティティの状態管理に関するプロパティを定義する
  • ITrackOriginalValues インターフェイスは変更前の値を保持するディクショナリを定義する
  • INotifyPropertyChanged インターフェイスと INotifyPropertyChanging インターフェイスはプロパティの変更を通知するイベントを定義する

リストの列は ColumnAttribute 属性でマッピングします。Storage にはプロパティ名ではなくフィールド名を指定する必要があります。プロパティの変更通知も忘れずに実装してください。以下は Title 列の例ですが、ID や Version も同様に定義できます。

private string title;

[Column(Name = "Title", Storage = "title", Required = true, FieldType = "Text")]
public virtual string Title
{
    get { return this.title; }
    set {
        if (this.title != value)
        {
            this.OnPropertyChanging("Title", this.Title);
            this.title = value;
            this.OnPropertyChanged("Title");
        }
    }
}

OnPropertyChanged は一般的な実装で問題ありませんが、OnPropertyChanging ではイベントの発生前に変更前の値を保持する処理を追加します。

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)
{
    var handler = this.PropertyChanged;
    if (handler != null)
    {
        handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public event PropertyChangingEventHandler PropertyChanging;

protected void OnPropertyChanging(string propertyName, object value)
{
    if (this.OriginalValues.ContainsKey(propertyName) != true)
    {
        this.OriginalValues.Add(propertyName, value);
    }
    var handler = this.PropertyChanging;
    if (handler != null)
    {
        handler.Invoke(this, new PropertyChangingEventArgs(propertyName));
    }
}

最後に Item クラスを継承した Test クラスを作成します。これが実際のリストの定義となります。今回は列を追加していませんが、列を追加する場合は Item クラスの例と同様に設定できます。

[ContentTypeAttribute(Name = "テスト", Id = "0x01")]
public class Test : Item { ... }

実行結果

サンプル プログラムを作成します。

private static void Main(string[] args)
{
    using (var context = new SharePointDataContext("http://sharepoint.examle.com"))
    {
        var item1 = new Test() { Title = "タイトル 1" };
        var item2 = new Test() { Title = "タイトル 2" };
        var item3 = new Test() { Title = "タイトル 3" };
        context.Tests.InsertOnSubmit(item1);
        context.Tests.InsertOnSubmit(item2);
        context.Tests.InsertOnSubmit(item3);
        context.SubmitChanges();
        foreach (var item in context.Tests)
        {
            Console.WriteLine("{0}:{1}", item.ID, item.Title);
        }
        Console.ReadLine();
    }
}

実行すると、次のように表示されます。

1:タイトル 1
2:タイトル 2
3:タイトル 3
脚注
  1. リスト名の指定は表示名のみで、URL や内部名は指定できません。 ↩︎

Discussion