2017年12月16日土曜日

C# で SQL Server 接続時に "SqlCommand.Prepare requires all variable length parameters to have an explicitly set non-zero Size" が発生した場合の対処

オフィス狛 技術部です。

とあるプロジェクト(C#)で、今まで Oracle だけに接続していたのが、SQL Server への接続も追加になりました。
Oracle への接続は「Oracle.ManagedDataAccess.Client」を使っていましたが、
SQL Server への接続は、「System.Data.SqlClient」を使う事になりました。

※本当ならデータベースに依存しないように「System.Data.Common」を使って、
プロバイダファクトリ的に作るべきなのでしょうが、途中から変更するのが大人の事情で難しかったです・・・。

と言う事で、Oracleへの接続クラスをコピーして、使用するクラスなどを変えていざ実行してみると、

    "Message": "An error has occurred.",
    "ExceptionMessage": "SqlCommand.Prepare requires all variable length parameters to have an explicitly set non-zero Size",
    "ExceptionType": "System.ApplicationException",

「可変長パラメータには、必ずゼロ以外のサイズを指定しろ」と言っていますね。
確かに、プログラム的には、
string sql =
        "SELECT login_id" 
  + ",user_name "
  + "FROM authentication_info "
                + "WHERE user_identifier = :user_identifier "
                + "AND hoge_code = :hoge_code ";
〜中略〜
SqlParameter parameter1 = this._cmd.CreateParameter();
parameter1.Value = "123";
parameter1.ParameterName = ":user_identifier";
parameter1.DbType = DbType.String;
SqlParameter parameter2 = this._cmd.CreateParameter();
parameter2.Value = "123";
parameter2.ParameterName = ":hoge_code";
parameter2.DbType = DbType.String;
this._cmd.Parameters.Add(parameter1);
this._cmd.Parameters.Add(parameter2);
となっていて、サイズを指定していません。

「Oracle.ManagedDataAccess.Client」では不要でも、
「System.Data.SqlClient」では必要なんですね。

と言うわけで、プログラムも以下の様に変更します。
string sql =
        "SELECT login_id" 
  + ",user_name "
  + "FROM authentication_info "
                + "WHERE user_identifier = @user_identifier "
                + "AND hoge_code = @hoge_code ";
〜中略〜
SqlParameter parameter1 = this._cmd.CreateParameter();
parameter1.Value = "123";
parameter1.ParameterName = ":user_identifier";
parameter1.DbType = DbType.String;
parameter1.Size = parameter1.Value.ToString().Length;   // 追加
SqlParameter parameter2 = this._cmd.CreateParameter();
parameter2.Value = "123";
parameter2.ParameterName = ":hoge_code";
parameter2.DbType = DbType.String;
parameter2.Size = parameter2.Value.ToString().Length;  // 追加
this._cmd.Parameters.Add(parameter1);
this._cmd.Parameters.Add(parameter2);
これで問題なく実行出来ました。

ちなみに、「Oracle.ManagedDataAccess.Client」でのプリペアドステートメントには「:(コロン)」を使いますが、「System.Data.SqlClient」では「@(アットマーク)」を使います。

今回の話は大した話ではないのですが、
「Oracle.ManagedDataAccess.Client」では問題無かった、と言う部分がハマりどころなので、
わざわざ記事にしてみました。

,

2017年12月9日土曜日

Android Studio で DexArchiveMergerException: Unable to merge dex が発生した場合の対処

オフィス狛 技術部です。

あるAndroidアプリの改修を行う事になり、Android Studio で gradleの設定を変更しました。
(対応のsdkversionを変更したり、ライブラリの追加など)

いざ、ビルドをすると・・・
Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.
> java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

これだけではちょっと分からないですね・・・。
まずは、エラーの詳細を知る為にビルドの設定を変えましょう。

Preferences...」->「Build, Execution, Deployment」->「Compiler」を選択して、
Command-line Options:」に「--stacktrace」と入力します。

この状態で再度ビルドしてみます。すると・・・
先程より情報が多く表示されました。その中でも注目すべきは、下記の部分です。
Error:com.android.dex.DexException: Multiple dex files define Lorg/apache/http/conn/HttpClientConnectionManager;
これは、Gradleにおける依存関係のエラーで、
dependenciesで指定しているライブラリが別のライブラリから使用されている場合などに出てきます。

結論から言ってしまうと、この時は単純なミスで、dependenciesに同じライブラリを2つ指定していた事がエラーの原因でした。
(記述が離れた場所にあったから気付かなかった・・・)

ただ、実際はそんな単純に解決できない場合もあります。
その場合は、各ライブラリの依存関係を調べてみましょう。

Android Studio のターミナルから下記のコマンドで依存関係を見ます。
./gradlew app:dependencies
この時、
./gradlew: Permission denied
となった場合、「gradlew」に実行権限が付いて可能性があるので、
下記コマンドで実行権限を付けます。
chmod +x gradlew

gradlewの出力は以下の様になります。(抜粋)
+--- com.android.support:appcompat-v7:26.1.0 (*)
+--- com.google.android.gms:play-services-gcm:11.6.2
|    +--- com.google.android.gms:play-services-base:11.6.2
|    |    +--- com.google.android.gms:play-services-basement:11.6.2
|    |    |    +--- com.android.support:support-v4:25.2.0 -> 26.1.0 (*)
|    |    |    \--- com.google.android.gms:play-services-basement-license:11.6.2
|    |    +--- com.google.android.gms:play-services-tasks:11.6.2
|    |    |    +--- com.google.android.gms:play-services-basement:11.6.2 (*)
|    |    |    \--- com.google.android.gms:play-services-tasks-license:11.6.2
|    |    \--- com.google.android.gms:play-services-base-license:11.6.2
|    +--- com.google.android.gms:play-services-basement:11.6.2 (*)
|    +--- com.google.android.gms:play-services-iid:11.6.2
|    |    +--- com.google.android.gms:play-services-base:11.6.2 (*)
|    |    +--- com.google.android.gms:play-services-basement:11.6.2 (*)
|    |    \--- com.google.android.gms:play-services-iid-license:11.6.2
|    \--- com.google.android.gms:play-services-gcm-license:11.6.2
+--- com.android.support:cardview-v7:26.1.0
|    \--- com.android.support:support-annotations:26.1.0
この情報から、各ライブラリの依存関係を確認します。

私の感覚だと、
しばらくメンテしてなかったアプリなどの、
ライブラリのバージョン上げる作業をしている時によく発生する気がします。

メンテは計画的に、ですね。


, ,