0

というモジュールにこの単純なクラスがあるとしますClassLibrary1

[DataContract]
public class Class1
{
    [DataMember]
    private DataTable _data;

    public void SetData(string key, object value)
    {
        _data = new DataTable("SomeName");
        _data.Columns.Add("Key", typeof(String));   
        _data.Columns.Add("Value", value.GetType());
        _data.Rows.Add(key, value);
    }
}

と呼ばれる別のモジュールClassLibrary2には、次のクラスがあります。

[DataContract]
public class Configuration
{
    [DataMember]
    private Class1 _obj;

    public Configuration()
    {
        _obj = new Class1();
        _obj.SetData("Key", MyEnum.Value2);
    }
}

さらに、モジュールは public としてマークされていないClassLibrary2という列挙型を定義しますMyEnum。つまり、それはモジュールの内部にあります (ご覧のとおり、上記のコードで使用されています)。

現在、私のメイン モジュールでは、 に依存したくありませんがClassLibrary2、実行時に動的にロードする必要があります。つまり、 を使用Assembly.LoadFromしてから、使用する必要がある型を見つけ (私の単純な例では、"Configuration" という名前の型を検索するだけです)、 を使用してインスタンスを作成しますActivator.CreateInstance。次に、次のようにインスタンスをシリアル化します。

var ser = new DataContractSerializer(config.GetType());
var outstream = new FileStream("c:\\test.xml", FileMode.Create);
ser.WriteObject(outstream, config);
outstream.Close();

これまでのところ、すべてが機能します。しかし、次のように逆シリアル化しようとすると:

var instream = new FileStream("c:\\test.xml", FileMode.Open);
var conf = ser.ReadObject(instream);

「列にArgumentExceptionは有効なデータ型が必要です」というメッセージが表示されます。次のいずれかの場合、これは発生しないことがわかりました。

  • 代わりにメインモジュールで MyEnum を定義してください
  • DataTable を単純なものに置き換えますString key; Object value
  • enum MyEnum の代わりに別のものを使用します (例: 文字列)

しかし、これらのオプションはどれも、私の実際のアプリケーションではあまり望ましいものではありません。最初のオプションは、互いに依存すべきではないモジュール間に強い結合を作成します。2 番目のオプションも可能ですが、少し手間がかかります (私はすでにクラス全体で DataTable を多くの場所で使用しており、シリアライゼーションを行う必要があることが最近わかりました)。3 番目のオプションもやや面倒です。

これを機能させる他の方法はありますか?また、コンストラクターを介して、およびクラス宣言の前にDataContractSerializer追加することにより、シリアライザーの既知の型のリストに MyEnum を追加しようとしましたが、これは役に立ちませんでした。[KnownType(typeof(MyEnum))]Configuration

4

1 に答える 1

0

基になるコードは、test.xml ファイルに自動的に書き留められた ClassLibrary2 アセンブリの完全な名前を見つけることができません。助けが必要です。

次のように、逆シリアル化の前にAppDomain.AssemblyResolve イベントをフックすると、これを回避できます。

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
...
var instream = new FileStream("test.xml", FileMode.Open);
var conf = ser.ReadObject(instream);
...

private static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name == "ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") // adapt to your needs of course
        return Assembly.LoadFrom(@"mypath\ClassLibrary2.dll");

    return null; // don't know for this one
}
于 2013-06-14T09:45:44.053 に答える