ラベル Linq To XML の投稿を表示しています。 すべての投稿を表示
ラベル Linq To XML の投稿を表示しています。 すべての投稿を表示

2010年9月15日水曜日

Dynamic LinqをLinq To Xmlで使うときの注意点 in Silverlight

Linq To SQLでDynamic LINQを良く使っていたのだが、先日Silverlight上でLinq To Xmlを同じように使ったところ結構はまったので紹介しておく。

問題のコードは下記。

var list = doc.Descendants("Row").Select(x => new { 
  Original=x, 
  Text=x.Element("FirstName").Value + x.Element("LastName").Value + x.Element("Address").Value
})
.Where("Text.Contains(@0)", new string[]{ "何か" }).ToList();

上記は意味の無いDynamic Linqの使い方だがサンプル目的なので分かりやすく記述している。上記のコードを実行するとContainsの部分でMethodAccessExceptionがThrowされる。どうやらAnonymousクラスのプロパティに対してReflectionから実行しようとすると駄目なようだ。回避方法は下記の通り。

public class TemporaryDataHolder
{
 public XElement Original { get; set; }
 public string Text { get; set; }
}

var list = doc.Descendants("Row").Select(x => new TemporaryDataHolder { 
  Original=x, 
  Text=x.Element("FirstName").Value + x.Element("LastName").Value + x.Element("Address").Value
})
.Where("Text.Contains(@0)", new string[]{ "何か" }).ToList();

一時的にデータを保持するクラスを定義して、それをAnonymousクラスの代わりにインスタンス化すればよい。なんだかなぁ、という感じだがDynamic Linqが処理の重要な部分を占める場合は致し方ない。

Linq To XmlでXNamespaceを指定する

前回SqlMetalの使い方を説明したが、今回は前回出力したマッピングファイルの書き換えをLinq To XMLで行う方法を紹介する。

マッピングファイルを書き換える理由は次の通り。複数のDBをまたいだクエリをLinq To Sqlで行う場合、マッピングファイルのTable要素は下記のようにServerName、DBNameを含んだ記述にしなければならない。

dbo.users → ServerName.DBName.dbo.users

SQL Server 2008で複数のDBを単一クエリで扱うための設定は下記を参照してほしい。
Linked Serverの設定方法
sp_addlinkedserver (Transact-SQL)

で、Linq To Xmlを使用してのマッピングファイルの読み込みなのだが、下記のようにXNamespaceを指定しないと予期したようにTable要素は取得できない。

var doc = XDocument.Load(mappingFilePath);
XNamespace ns = "http://schemas.microsoft.com/linqtosql/mapping/2007";
var table = doc.Descendants(ns+"Table").Where(x => 条件).FirstOrDefault();
// 省略

Linq To Xmlに限らずXmlDocumentやXPathでもXmlファイルを操作する場合は、名前空間の指定のある要素にはそれぞれXNamespaceを明示的に指定しなければ予期したように動作しないので注意しよう。