愚者の経験

「また今度」はほとんどこない

月別アーカイブ: 1月 2012

ADODB.Commandの「Prepared」プロパティ

ADODB.Commandオブジェクトのプロパティの中で
「Prepared」プロパティというものがあるのをご存知ですか?

参考URL:http://msdn.microsoft.com/ja-jp/library/cc426744.aspx

「CommandText」プロパティに設定したSQL文を準備済み(コンパイル済み)にして
初回の実行速度を犠牲にその後のパフォーマンスを改善するものらしいです。

SQL ServerのProfilerで見ると

    cmd.CommandType = adCmdText
    cmd.CommandText = “insert t_test(testid) values(1)”
    cmd.Execute
    cmd.Execute

上記のようにそのまま実行した場合は

sp_executesql SQL文

で実行され、2回目以降の実行も「sp_executesql」です。

    cmd.CommandType = adCmdText
    cmd.CommandText = “insert t_test(testid) values(1)”
    cmd.Prepared = True
    cmd.Execute
    cmd.Execute

「Prepared」プロパティをTrueにしてコマンドを実行すると
1回目の実行が

sp_prepexec

2回目以降の実行が

sp_execute

になります。実行速度については資料が無いのでわかりませんでしたが、
その後の実行で結果的にパフォーマンスで有利と書かれていますので、
使う価値はあるのではないかなと思って最近良く使っています。

特にユーザーに検索条件を入力させるような画面がある場合、
where句のパラメータの値を変更して実行するだけなので、「Prepared」プロパティを使えば
毎回似たようなSQL文を発行しなくて済みます。

Silverlight 5 リリース!?

SDK(JPN)もあります。
参考URL:http://msdn.microsoft.com/ja-jp/silverlight/bb187452
こんなページあったかな?

でもVisual Studio 2010のアドオンはSP1用しかないです。
この前SP1入れて色々してたらいつの間にか「Silverlight 4」の開発ができなくなって入れなおした経緯があるのでSP1は避けてたんですが…
自分は「Silverlight 4 Tools for Visual Studio 2010」が内蔵されているあたりが怪しいと思いました。
SP1入れる前にこれ入れてた人はどうするのが正しいのかわかりません。

とりあえず今のところ安定してるのは「Silverlight X SDK」だけを入れることです。
これだけ入れていればなんとなくVS2010でも変な動きはありません。

とにかく「Silverlight 5」出ました!期待大です(?)。
個人的にはDataGridが使いやすくなっていると有りがたいです。
バインドの仕方や更新、データの追加用の行などわからないことだらけです。

SilverlightのADODB.Recordsetラッパー

引き続きADODB.Recordsetオブジェクト等。

完全に自信なし。

using System;
using System.Runtime.InteropServices.Automation;

namespace SLADO.Data
{
    public class Recordset : IDisposable
    {
        internal dynamic rs;
        //コンストラクタ
        public Recordset(Connection connection)
        {
            rs = AutomationFactory.CreateObject(“ADODB.Recordset”);
            connection.Open();
            rs.ActiveConnection = connection.cn;
            Fields = new Fields(this);
        }
        //コンストラクタ(オーバーロード)
        internal Recordset(dynamic recordset)
        {
            rs = recordset;
            Fields = new Fields(this);
            Fieldset();
        }

        //フィールドコレクション
        public Fields Fields { get; private set; }
     
        //フィールドのセット
        private void Fieldset()
        {
            if (!EOF && rs.Fields != null && rs.Fields.Count > 0)
            {
                int FieldsCount = rs.Fields.Count;
                for (int i = 0; i < FieldsCount; i++)
                {
                    Fields.Append(i);
                }
            }
        }

        //レコード数の取得
        public int RecordCount
        {
            get { return rs.RecordCount; }
        }

        //BOFの取得
        public bool BOF
        {
            get { return (bool)rs.BOF; }
        }

        //EOFの取得
        public bool EOF
        {
            get { return (bool)rs.EOF; }
        }

        //Accessと同じくrecordset()でフィールドにアクセスする。
        public Field this[int index]
        {
            get { return Fields[index]; }
        }
        //Accessと同じくrecordset()でフィールドにアクセスする。(オーバーロード)
        public Field this[string name]
        {
            get { return Fields[name]; }
        }

        //SQLを実行する(結果を返すSQLステートメント)
        public void ExecuteQuery(string source)
        {
            rs.Open(source);
            Fieldset();
        }

        //SQLを実行する(結果を返すSQLステートメント)(Commandオブジェクトのオーバーロード)
        public void ExecuteQuery(Command source)
        {
            rs.Open();
            Fieldset();
        }

        public void Dispose()
        {
            rs.Close();
            rs = null;
        }
    }
}

Fieldsコレクション。敷居が高すぎた…orz

using System;
using System.Collections.ObjectModel;

namespace SLADO.Data
{
    public class Fields : KeyedCollection
    {
        internal Recordset rs;

        internal Fields(Recordset recordset)
        {
            rs = recordset;
        }

        public void Append(int index)
        {
            this.Add(new Field(rs.rs.Fields(index)));
        }

        protected override string GetKeyForItem(Field item)
        {
            return item.Name;
        }
    }
}

Fieldオブジェクト。必要そうなプロパティのみ実装しました。

using System;
using System.Runtime.InteropServices.Automation;

namespace SLADO.Data
{
    public class Field : IDisposable
    {
        internal dynamic fld;

        //コンストラクタ
        internal Field(dynamic field)
        {
            fld = field;
        }

        //フィールド名の取得
        public string Name
        {
            get { return fld.Name; }
        }

        //値の取得と設定
        public object Value
        {
            get { return fld.Value; }
            set { fld.Value = value; }
        }

        //前回値の取得
        public object OriginalValue
        {
            get { return fld.OriginalValue; }
        }

        //競合時の値の取得
        public object UnderlyingValue
        {
            get { return fld.UnderlyingValue; }
        }

        //データ型の取得
        public AdoDataType Type
        {
            get { return (AdoDataType)fld.Type; }
        }
        public void Dispose()
        {
            fld = null;
        }

    }
}

いよいよ使ってみます。ドキドキです。

SilverlightのADODB.Commandラッパー

動くかどうかはわからないのでとりあえず思いつく限り
書いていきます。C#の練習も兼ねて。動作確認はあとで(大丈夫な気がしない。)

using System;
using System.Runtime.InteropServices.Automation;

namespace SLADO.Data
{
    public class Command : IDisposable
    {
        internal dynamic cmd;
        private int recordsaffected;
        //コンストラクタ
        internal Command(Connection connection)
        {
            cmd = AutomationFactory.CreateObject(“ADODB.Command”);
            cmd.ActiveConnection = connection.cn;
            CommandTimeout = 0;
            Parameters = new Parameters(this);
        }
        //パラメータコレクション
        public Parameters Parameters { get; private set; }
        //コマンドタイプの設定と取得
        public AdoCommandType CommandType
        {
            get { return (AdoCommandType)cmd.CommandType; }
            set { cmd.CommandType = (int)value; }
        }
        //コマンドテキストの設定と取得
        public string CommandText
        {
            get { return (string)cmd.CommantText; }
            set { cmd.CommandText = value; }
        }
        //タイムアウトの設定と取得
        public int CommandTimeout
        {
            get { return (int)cmd.CommandTimeout; }
            set { cmd.CommandTimeout = value; }
        }
        //影響を受けたレコード数の取得
        public int RecordsAffected
        {
            get { return recordsaffected; }
        }
        //SQLを実行する(結果を返さないSQLステートメント)
        public void ExecuteNonQuery()
        {
            cmd.Execute( recordsaffected, null,(int)AdoExecuteOption.adExecuteNoRecords);
        }
        public void Dispose()
        {
            cmd = null;
        }
    }
}

Parametersコレクション。参考のものとかなり違うけど…

using System;
using System.Collections.ObjectModel;

namespace SLADO.Data
{
    public class Parameters : KeyedCollection
    {
        internal Command cmd;
        //コンストラクタ
        internal Parameters(Command command)
        {
            cmd = command;
        }
        //コレクションにパラメータ追加
        public void Append(string name,object value)
        {
            this.Add(new Parameter(cmd.cmd.CreateParameter(), name, value));
        }
        //パラメータ名でアクセスできるようにキーを指定
        protected override string GetKeyForItem(Parameter item)
        {
            return item.Name;
        }
    }
}

Parameterオブジェクト。これも参考と違う…動くかな?

using System;
using System.Runtime.InteropServices.Automation;

namespace SLADO.Data
{
    public class Parameter : IDisposable
    {
        internal dynamic prm;
        //コンストラクタ
        internal Parameter(dynamic parameter,string name,object value)
        {
            prm = parameter;
            Name = name;
            Value = value;
            Type = ResolveDataType(value);
            Size = DefaultSizeAttribute.GetSize(Type);
        }
        //名前の取得と設定
        public string Name
        {
            get { return prm.Name; }
            set { prm.Name = value; }
        }
        //値の取得と設定
        public object Value
        {
            get { return prm.Value; }
            set { prm.Value = value; }
        }
        //サイズの取得と設定
        public int Size
        {
            get { return prm.Size; }
            set { prm.Size = value; }
        }
        //データタイプの取得と設定
        public AdoDataType Type
        {
            get { return (AdoDataType)prm.Type; }
            set { prm.Type = (int)value; }
        }
        //パラメータの方向の取得と設定
        public AdoParameterDirection Direction
        {
            get { return (AdoParameterDirection)prm.Direction; }
            set { prm.Direction = (int)value; }
        }
        //パラメータの値からデータタイプの解決
        private AdoDataType ResolveDataType(object value)
        {
            return 0;
        }
        public void Dispose()
        {
            prm = null;
        }
    }
}

列挙型定数もたくさんになってます。
列挙型にも属性値を持たせられるのはかなり便利だと思った。[DefaultSize()]の部分。

using System;
namespace SLADO
{
    public enum AdoState
    {
        adStateClosed = 0,
        adStateOpen = 0x1,
        adStateConnecting = 0x2,
        adStateExecuting = 0x4,
        adStateFetching = 0x8
    }
    public enum AdoExecuteOption
    {
        adOptionUnspecified = -1,
        adAsyncExecute = 0x10,
        adAsyncFetch = 0x20,
        adAsyncFetchNonBlocking = 0x40,
        adExecuteNoRecords = 0x80,
        adExecuteStream = 0x400,
        adExecuteRecord = 0x800
    }
    public enum AdoCommandType
    {
        adCmdUnspecified = -1,
        adCmdText = 0x1,
        adCmdTable = 0x2,
        adCmdStoredProc = 0x4,
        adCmdUnknown = 0x8,
        adCmdFile = 0x100,
        adCmdTableDirect = 0x200
    }
    public enum AdoParameterDirection
    {
        adParamUnknown = 0,
        adParamInput = 0x1,
        adParamOutput = 0x2,
        adParamInputOutput = 0x3,
        adParamReturnValue = 0x4
    }
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public class DefaultSizeAttribute : Attribute
    {
        private int size;
        public DefaultSizeAttribute(int defaultsize)
        {
            this.size = defaultsize;
        }
        public static int GetSize(Enum value)
        {
            Type enumtype = value.GetType();
            string name = Enum.GetName(enumtype, value);
            DefaultSizeAttribute[] attrs = (DefaultSizeAttribute[])enumtype.GetField(name).GetCustomAttributes(typeof(DefaultSizeAttribute), false);
            return attrs[0].size;
        }
    }
    public enum AdoDataType
    {
        [DefaultSize(0)] adEmpty = 0,
        [DefaultSize(1)] adTinyInt = 16,
        [DefaultSize(2)] adSmallInt = 2,
        [DefaultSize(4)] adInteger = 3,
        [DefaultSize(8)] adBigInt = 20,
        [DefaultSize(8)] adSingle = 4,
        [DefaultSize(8)] adDouble = 5,
        [DefaultSize(8)] adCurrency = 6,
        [DefaultSize(0)] adDecimal = 14,
        [DefaultSize(0)] adNumeric = 131,
        [DefaultSize(1)] adBoolean = 11,
        [DefaultSize(0)] adError = 10,
        [DefaultSize(0)] adUserDefined = 132,
        [DefaultSize(8016)] adVariant = 12,
        [DefaultSize(0)] adIDispatch = 9,
        [DefaultSize(0)] adIUnknown = 13,
        [DefaultSize(32)] adGUID = 72,
        [DefaultSize(8)] adDate = 7,
        [DefaultSize(0)] adDBDate = 133,
        [DefaultSize(0)] adDBTime = 134,
        [DefaultSize(0)] adDBTimeStamp = 135,
        [DefaultSize(50)] adBSTR = 8,
        [DefaultSize(50)] adChar = 129,
        [DefaultSize(50)] adVarChar = 200,
        [DefaultSize(50)] adLongVarChar = 201,
        [DefaultSize(50)] adWChar = 130,
        [DefaultSize(50)] adVarWChar = 202,
        [DefaultSize(50)] adLongVarWChar = 203,
        [DefaultSize(50)] adBinary = 128,
        [DefaultSize(50)] adVarBinary = 204,
        [DefaultSize(50)] adLongVarBinary = 205,
        [DefaultSize(0)] adChapter = 136,
        [DefaultSize(0)] adFileTime = 64,
        [DefaultSize(0)] adPropVariant = 138,
        [DefaultSize(0)] adVarNumeric = 139,
        [DefaultSize(0)] adArray = 0x2000
    }
}

SilverlightのADODB.Connectionラッパー

もう「本当にやるのか?」という自問が始まってます(笑)

改良したADODB.Connectionのラッパーを改良版。

using System;
using System.Runtime.InteropServices.Automation;

namespace SLADO.Data
{
    public class Connection
    {
        internal dynamic cn;
        private int ra;
        //コンストラクタ(接続文字列)
        internal Connection(string connectionstring)
        {
            cn = AutomationFactory.CreateObject(“ADODB.Connection”);
            ConnectionString = connectionstring;
        }
        //接続文字列の設定と取得
        public string ConnectionString
        {
            get { return cn.ConnectionString; }
            set { cn.ConnectionString = value; }
        }
        //コネクションの状態の取得
        public AdoState State
        {
            get { return (AdoState)cn.State; }
        }
        //影響を受けたレコード数の取得
        public int RecordsAffected
        {
            get { return ra; }
        }
        //コネクションを開く
        public void Open()
        {
            if (State == AdoState.adStateClosed)
                cn.Open();
        }
        //コネクションを閉じる
        public void Close()
        {
            if (State != AdoState.adStateClosed)
                cn.Close();
        }
        //SQLを実行する(結果を返さないSQLステートメント)
        public void ExecuteNonQuery(string statement)
        {
            cn.Execute(statement, ra, (int)AdoExecuteOption.adExecuteNoRecords);
        }
        //SQLを実行する(結果を返すSQLステートメント)
        public Recordset ExecuteRecordset(string statement)
        {
            return new Recordset(cn.Execute(statement, ra));
        }
    }
}

ExecuteRecordsetに関しては、まだどうなるか未定です。

列挙型はこんな感じです。

    public enum AdoState
    {
        adStateClosed = 0,
        adStateOpen = 0x1,
        adStateConnecting = 0x2,
        adStateExecuting = 0x4,
        adStateFetching = 0x8
    }
    public enum AdoExecuteOption
    {
        adOptionUnspecified = -1,
        adAsyncExecute = 0x10,
        adAsyncFetch = 0x20,
        adAsyncFetchNonBlocking = 0x40,
        adExecuteNoRecords = 0x80,
        adExecuteStream = 0x400,
        adExecuteRecord = 0x800
    }
次はADODB.Commandも行ってみよう。

Silverlight用のADODBラッパークラスを作る

前回の記事でADODBが何とか使えることが分かったので
使いやすくするためのラッパークラスを作ります。

最近C#始めたばかりなのにクラスを作成するのは結構習得順番が飛んでいる気もしますが
Silverlight Com Toolkitにお手本があるので、参照しながら1から作ります。

まず、ADODB.CollecntionのCollectionクラス(作成途中)

using System;
using System.Runtime.InteropServices.Automation;

namespace SilverAdo.Data
{
    public class Connection : IDisposable
    {
        internal dynamic connection;

        public Connection(string connectionstring)
        {
            connection = AutomationFactory.CreateObject(“ADODB.Connection”);
            ConnectionString = connectionstring;
        }

        public string ConnectionString
        {
            get { return connection.ConnectionString; }
            set { connection.ConnectionString = value; }
        }

        public ObjectState State
        {
            get {return (ObjectState)connection.State; }
        }

        public void Open()
        {
            connection.Open();
        }

        public void Close()
        {
            if (this.State!=SilverAdo.ObjectState.adStateClosed)
            connection.Close();
        }

        public void Dispose()
        {
            Close();
            connection = null;
        }
    }
}

C#では戻り値のないメソッドをpublic(private) void ~と宣言するんですね。
プロパティの書き方もかなり簡単で行も減ります。
public(private) 型 プロパティ名
{
    get {}
    set {}
}
そもそもEnd ~を書かなくていいのが楽ですね。

ローカルデータベースを利用する

Silverlightでは基本ローカルファイルにアクセスできません。
「ブラウザー外実行」の設定をする必要があります。

「アプリケーションのブラウザー外実行を有効にする」にチェックする。

「ブラウザー外での実行時に昇格された信頼を要求する」にチェックする。

2項目にチェックしたらローカルアクセスの準備完了。

とりあえずおなじみのADODB.Connectionを使ってみます。

using System.Runtime.InteropServices.Automation;

if (AutomationFactory.IsAvailable == false)
 {
     //ブラウザー外実行されてない場合や信頼されていない場合
     MessageBox.Show(“インストールされていません。”, “未インストール”, MessageBoxButton.OK);
     return;
 }
 dynamic cn = AutomationFactory.CreateObject(“ADODB.Connection”);
 var ConnStr = @”Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source=D:\Silver1\Silver1\SL.accdb;”;
 var stmt = @”insert into tbl_test (TestName) values (“”test””)”;
 cn.Open(ConnStr);
 cn.Execute(stmt);
 cn.Close();
 MessageBox.Show(“実行完了!”, “完了”, MessageBoxButton.OK);

 
一応使えます。イベントはとれるのだろうか…
 
文字列を代入する際は、@””で括るとやりやすい気がします。(VBA上がりの私の感想です。)
「\」はエスケープ文字?っぽくなっていてやりにくい印象でした。(「\」は\\で表現します。)