いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

DevExpress奮闘記-121 (XPO, Session.ExecuteQueryWithMetadata, 結果と列情報を一度に取得)


XPOのSQL直接実行機能 (Direct SQL Queries) を利用する場合、以下のメソッドがあります。

  • ExecuteQuery
    • SQLを実行して結果をSelectedDataで取得
  • ExecuteQueryWithMetadata
    • SQLを実行して結果をSelectedDataで取得。尚、結果とともに列情報も取得する
  • ExecuteNonQuery
    • 更新系クエリ発行
  • GetObjectsFromQuery
    • SQLを実行して、結果を指定したT型にマッピングして返す


上記のメソッドの内、ExecuteQueryWithMetadataがちょっと特殊です。
このメソッドには、ExecuteQueryと同様のパラメータを渡して実行できます。
結果も、同じSelectedDataです。ですが、このメソッドの場合は
ついでに列情報も返してくれます。


列情報は、SelectedData.ResultSet[]の先頭に格納されています。
なので、ExecuteQueryWithMetadataを実行した場合、結果データは[1]の方に
含まれています。

var dbData = uow.ExecuteQueryWithMetadata(sql);

var columnsInfo = dbData.ResultSet[0];
var queryResult = dbData.ResultSet[1];


列情報は以下のように設定されています。

foreach (var row in columnsInfo.Rows)
{
  var columnName   = row.Values[0]; // 列名
  var dbColumnType = row.Values[1]; // DB側の列型名 (nvarcharとか)
  var columnType   = row.Values[2]; // .NET側の列型名 (stringとか)
}

この列情報ですが、XPDataViewを構築する場合に便利です。
通常、XPDataViewを構築する際、事前にマッピングクラスを定義していると
楽なのですがクラスも定義したくない場合、以下のようにします。

var view = new XPDataView();

view.AddProperty("Id", typeof(int));
view.AddProperty("Name", typeof(string));

view.LoadData(dbData);

上記を見ると分かるように、表示列定義を指定していっています。
これを、ExecuteWithMetadataから取得した列情報で設定すると
以下のようにできます。

var view = new XPDataView();

foreach (var row in columnsInfo.Rows)
{
  view.AddProperty(row.Values[0] as string, DBColumn.GetType((DBColumnType) Enum.Parse(typeof(DBColumnType), row.Values[2] as string)));
}

view.LoadData(new SelectedData(queryResult));


XPDataViewに設定する部分に関しては、若干冗長なので、拡張メソッドにしてみました。
(Gistにアップしています: https://gist.github.com/4181320)

using DevExpress.Xpo;

namespace DevExpress.Xpo
{
  public static class XPDataViewExtensions
  {
    //
    // [usage]
    //   var view       = new XPDataView();
    //   var selectData = uow.ExecuteQueryWithMetaData(sql);
    //   
    //   view.FillProperties(selectData.ResultSet[0])
    //       .LoadData(new SelectedData(selectData.ResultSet[1]);
    //
    public static XPDataView FillProperties(this XPDataView self, SelectStatementResult schemaResult)
    {
      foreach (var row in schemaResult.Rows)
      {
        var propName = row.Values[0] as string;
        var propType = DBColumn.GetType((DBColumnType)Enum.Parse(typeof(DBColumnType), row.Values[2] as string));

        self.AddProperty(propName, propType);
      }

      return self;
    }

    //
    // [usage]
    //   new XPDataView().FillAndLoad(uow.ExecuteQueryWithMetaData(sql, names, values).ResultSet);
    //
    public static XPDataView FillAndLoad(this XPDataView self, SelectStatementResult[] results)
    {
      self.FillProperties(results[0])
          .LoadData(new SelectedData(results[1]));

      return self;
    }
    
    //
    // [usage]
    //   new XPDataView().FillAndLoad(sess, sql);
    //
    public static XPDataView FillAndLoad(this XPDataView self, Session sess, string sql)
    {
      return self.FillAndLoad(sess.ExecuteQueryWithMetadata(sql).ResultSet);
    }
  }
}


以下、サンプルです。
(Gistにアップしています: https://gist.github.com/4181499)

namespace WpfApplication1
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Windows;

  using DevExpress.Xpo;
  using DevExpress.Xpo.DB;

  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      DataContext = new DataSource();
    }
  }

  public class DataSource
  {
    public DataSource()
    {
      Setup();
    }

    public XPDataView Data { get; private set; }

    private void Setup()
    {
      var connectionString = MSSqlConnectionProvider.GetConnectionString("sever", "user", "passwd", "database");
      var dataLayer        = XpoDefault.GetDataLayer(connectionString, AutoCreateOption.SchemaAlreadyExists);

      using (var uow = new UnitOfWork(dataLayer))
      {
        var sql    = "SELECT Id, Name FROM BookGenre WHERE Name = @Name";
        var dbData = uow.ExecuteQueryWithMetadata(sql, new[] { "Name" }, new[] { "Computer" });

        Data = new XPDataView().FillAndLoad(dbData.ResultSet);
      }
    }
  }

  public static class XPDataViewExtensions
  {
    public static XPDataView FillProperties(this XPDataView self, SelectStatementResult schemaResult)
    {
      foreach (var row in schemaResult.Rows)
      {
        var propName = row.Values[0] as string;
        var propType = DBColumn.GetType((DBColumnType)Enum.Parse(typeof(DBColumnType), row.Values[2] as string));

        self.AddProperty(propName, propType);
      }

      return self;
    }

    public static XPDataView FillAndLoad(this XPDataView self, SelectStatementResult[] results)
    {
      self.FillProperties(results[0])
          .LoadData(new SelectedData(results[1]));

      return self;
    }

    public static XPDataView FillAndLoad(this XPDataView self, Session sess, string sql)
    {
      return self.FillAndLoad(sess.ExecuteQueryWithMetadata(sql).ResultSet);
    }
  }
}

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。