脱皮化の問題:別のプログラムバージョンから脱出するときのエラー
-
28-10-2019 - |
質問
私はついに自分の問題を投稿することに決めました。数時間はインターネットを検索してソリューションを検索して試してみました。
問題のコンテキスト
2つの部分で展開されるアプリケーションを開発しています。
- XML Importerツール:その役割は、その後バイナリファイルにシリアル化されたいくつかのデータストラクチャを入力するためにXMLファイルをロード/読み取ることです。
- エンドユーザーアプリケーション:XMLインポーターによって生成されたバイナリファイルをロードし、回復したデータ構造でいくつかのことを行います。
今のところ、両方の目的でXMLインポーターのみを使用します(最初にXMLをロードしてバイナリファイルに保存し、XMLインポーターを再開してバイナリファイルをロードします)。
実際の問題
これは正常に機能し、XMLロード後に持っていたすべてのデータを回復することができます。 私がXMLインポーターの同じビルドでそれをする限り. 。これは実行可能ではありません。1つはXMLインポーター用、もう1つはエンドユーザーアプリケーション用に必要なため、これは実行可能ではありません。私がテストに使用しているXMLインポーターの2つのバージョンは ソースコード、したがってデータストラクチャとまったく同じ, 、唯一の違いはビルド数にあります(別のビルドを強制するために、どこかにスペースを追加して再びビルドするだけです)。
だから私がやろうとしているのは:
- XMLインポーターのバージョンを作成します
- XMLインポーターを開き、XMLファイルをロードし、結果のデータストラクチャをバイナリファイルに保存します
- XMLインポーターを再構築します
- 新しく構築されたXMLインポーターを開き、以前に作成したバイナリファイルをロードし、データストラクチャを回復します。
この時点で、例外が得られます。
SerializationException: Could not find type 'System.Collections.Generic.List`1[[Grid, 74b7fa2fcc11e47f8bc966e9110610a6, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadType (System.IO.BinaryReader reader, TypeTag code)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectInstance (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)
あなたの情報の場合(便利かどうかはわかりません)、それが脱皮に苦労している実際のタイプはリストであり、グリッドはカスタムクラスです(これは正しくシリアル化可能です。 XMLインポーター)。
潜在的な解決策
これについての多くの投稿や記事を読んだので、それはアセンブリの周りのどこかから来ていると思います。しかし、私はすでにアセンブリ名の違いを処理しているカスタムバインダーを持っていますが、次のように見えます。
public sealed class VersionDeserializationBinder : SerializationBinder
{
public override Type BindToType( string assemblyName, string typeName )
{
if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) )
{
Type typeToDeserialize = null;
assemblyName = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
return null;
}
}
ここで脱出する前に、私がバイナリフォームターに割り当てます:
public static SaveData Load (string filePath)
{
SaveData data = null;//new SaveData ();
Stream stream;
stream = File.Open(filePath, FileMode.Open);
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Binder = new VersionDeserializationBinder();
data = (SaveData)bformatter.Deserialize(stream);
stream.Close();
Debug.Log("Binary version loaded from " + filePath);
return data;
}
皆さんは、私がそれを修正する方法についてアイデアを持っていますか?すごい、きれいにしてください:)
解決
作業ビットを別のアセンブリに移動し、「サーバー」と「クライアント」の両方でアセンブリを使用します。問題の説明に基づいて、それが中核的な問題である場合、これは「間違ったバージョン」の問題を回避するはずです。また、「モデル」(つまり、グリッドのような状態のビット)をドメインモデルプロジェクトに持ち込み、両方の場所でそれを使用します。
他のヒント
私は同じ問題を抱えている間にあなたのスレッドにぶつかりました。特に、SerializationBinderを使用したコードサンプルは私を大いに助けてくれました。自分のアセンブリとMicrosoftのアセンブリとの違いを伝えるために、少し変更する必要がありました。うまくいけば、それもあなたにも役立ちます:
sealed class VersionDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName;
//my modification
string currentAssemblyName = currentAssemblyInfo.Split(',')[0];
if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo;
typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
return typeToDeserialize;
}
}
問題は、実行中のアセンブリでリストを探すように言っているのに対し、実際にはシステムアセンブリにあることです。元のアセンブリがあなたのものである場合のみ、バインダーのアセンブリ名を再割り当てする必要があります。
また、タイプ名を解析し、パラメーター化されたジェネリックタイプを返すときにパラメータータイプが外部アセンブリに固有でないことを確認することにより、バインダー内のジェネリックのパラメータータイプを処理する必要があります。