2013-10-18 2 views
2

나는 작업중인 시스템 콜렉션에 대해 불가지론 뷰어를 작성 중입니다. 이 뷰어는 특정 시스템의 컨텍스트를 알 필요없이 내 데이터의 일반적인 구조를 보여줍니다.deserialize시 참조가 누락되면 예외를 throw하지 않음

Foo<T>Foo에서 상속되는 형식 인 Foo<T> 만 포함하는 메모리 스트림을 deserialise하려고합니다. 불가 지 한 시청자의 관점에서 내가 필요한 모든 데이터는 Foo에 있습니다. <T> 부분은 부적합합니다.

유형 T는 다른 어셈블리 (들)에 정의됩니다. 정상적인 작동 상태에서, 시스템은 분명히 적절한 모든 상황에 맞는 어셈블리를로드합니다. 문제는 뷰어를 실행할 때 상황 별 어셈블리가로드되지 않는다는 것입니다. Foo의 인스턴스를 deserialise하려고하면 참조 된 어셈블리가로드되지 않아서 분명히 예외가 발생합니다.

필요한 참조 된 어셈블리가 모두로드되어 있는지 여부를 감지하려고하므로 데이터를 deserialise 할 것인지 또는 클래스의 다른 측면에서 필요한 데이터를 재구성 할 것인지를 알 수 있습니다.

아주 간단한 예외 try/catch 블록을 사용하여이 작업을 수행 할 수 있다는 것을 알고 있지만 예외적 인 경우는 아닙니다. 나는 을 알고 있습니다.을 알게됩니다. 데이터를로드 할 때 수천 번이 아니더라도 수 백 번 발생할 것입니다. 그리고 이것은 내가 예외를 켰을 때 휴식을 취하는 것처럼 악몽을 불러 일으킬 수 있습니다. 나는 또한 "Exception - 힌트가 그 이름에있다"는 생각의 학교에 가입하고 따라서 Exception은 당신의 primary case code의 일부가되어서는 안된다.

는 -------- 편집 21/10/2013 ------------

전체 설명 예를 들어 here 볼 수 있지만, 여기에 중요한 비트는 다음과 같습니다

일반적인 정의

푸 클래스 :

[Serializable] 
public class Foo 
{ 
    public string Agnostic { get; set; } 
} 

[Serializable] 
public class Foo<T> : Foo 
{ 
    public string Contextual { get; set; } 
} 

콘텐츠 저장 :

BinaryFormatter bf = new BinaryFormatter(); 
FileInfo tempFile = TempFileGetter.GetTempFile(); 


Foo<Bar> fooBar = new Foo<Bar>(); 
fooBar.Agnostic = "Agnostic"; 
fooBar.Contextual = "Contextual"; 


using (var fs = tempFile.OpenWrite()) 
{ 
    bf.Serialize(fs, fooBar); 
    fs.Flush(); 
} 

불가지론로드 :

내 말은
BinaryFormatter bf = new BinaryFormatter(); 
FileInfo tempFile = TempFileGetter.GetTempFile(); 

using (var fs = tempFile.OpenRead()) 
{ 
    Foo foo = (Foo)bf.Deserialize(fs); 
    Controls.DataContext = foo; 
} 

, 내가이 일을하지 않으려는, 거기에 아무것도 로켓 과학이 코드에 있고, 그것은 잘로드 "불가지론 자"뷰어, 참고로 상황에 맞는 뷰어를로드하는 경우지만, 우리가 항상로드 할 문맥 라이브러리를 갖지는 않을 것이기 때문입니다.

는 경우 BinaryFormatter이 정보를 포함
+0

비 직렬화 코드 샘플을 제공해 주시겠습니까? 직렬화 코드를 변경할 수 있습니까? –

+0

문제점을 설명하는 코드 추가 (다운로드 가능한 버전으로 완료) –

답변

0

은 필요로하는 참조보고 내용의 분석과 별도로 직렬화 직렬화 컨테이너 생성했다 :

[serializable] 
public class Container 
{ 
    private IEnumerable<object> data; 
    public Container(IEnumerable data); 

    public string[] GetFullyQualifiedReferences();   
} 

그래서 당신은 당신이 여기에서 원하는대로 데이터를 넣어, 당신의 목록을 완전한 어셈블리 이름은 별도로 저장됩니다. Wibble 가정

은 푸 나는이 그림 주어 완벽한 이해가되지 않습니다 알고

public class Wibble : ISerializable 
{   

    public string Agnostic { get { return agnostic; } } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     //construct a container based on my data. 
     Container container = new Container(new object[]{myFoo}); 
     info.AddValue("RequiredReferences",container.GetFullyQualifiedReferences()); 

     byte[] containerData = Serialize(container); 
     info.AddValue("TypeUnsafeData",containerData); 

     //store some safe typed versions of the context data, if necessary. 
     info.AddValue("SafeValues", GetSafeValues()) 
    } 

    public Wibble(SerializationInfo info, StreamingContext context) 
    { 
     var requiredAssemblies = 
      (string[])info.GetValue("RequiredReferences",typeof(string[])); 

     if(AreAssembliesLoaded(requiredAssemblies))) 
     { 
      //deserialise the container as normal 
     } 
     else 
     { 
      //instead, load the "safe" data that we previously stored. 
     } 

    } 

} 

를 직렬화하는 클래스이지만, 그림은 내 구현 문제의 약간 불완전한 추상화,하지만 솔루션입니다 두 가지 모두 작동해야합니다 (제 구현에 효과가 있습니다).

다음 단계로 넘어 가서 컨테이너에 컨테이너를 지정하면 모든 컨테이너에 필요한 어셈블리가 나열되지만이 예에서는 실제로 필요하지 않습니다. 거기는 의미로이 구현에 약간의 문제가, 당신은 상황에 맞는 어셈블리 버전 번호를 업데이트하면 즉

------ 업데이트 -------는

, 디시리얼라이저는하지 않습니다 구 어셈블리를 찾으십시오. 그래서, 당신이 중 하나를 수행해야합니다

이 어셈블리의 현대 버전을 찾아 다음을 조회 할 수 있습니다 경우 deseralize 코드를 볼 수 있도록하기위한 메커니즘의 일종을 제공,

--- 또는 ---

버전을 구분하지 않도록 정규화 된 이름의 동일성을 업데이트하십시오.

-1

, MemoryStream을에 직렬화 된 데이터를 읽고 당신은 고통을 구문 분석처럼 보이는 뭔가 끝날 것 Assembly.Name.MyObject<int>를 들어 문자열

// There's probably a better way to do this: 
new String(memoryStream.GetBuffer().Select<byte, char>(b => (char)b).ToArray()); 

로 변환하려고하지만,

[버전] 1.0.0.0, 문화 = 중립, PublicKeyToken = nullgMyObject`1 [[System.Int32, mscorlib, 버전 = 4.0.0.0, 문화 = 중립, PublicKeyToken = b77a5c561934e089]] n1n2str]

또는 직렬화를 제어 할 수있는 경우 먼저 파일에 필요한 정보로 객체를 직렬화하십시오 (예 : 그냥 형식과 T의 어셈블리), 다음 BinaryFormatter에서 데이터입니다. (원하는 경우이 확장 할 수 있습니다.)

개체가로드되었는지 확인하려면 형식을 찾을 수 없으면 대신 Type.GetType("Assembly.Name.Space.ClassName")을 사용할 수 있지만 그 대신 사용할 수있는 "how to check if namespace, class, or method exists" 질문에 대해 다른 메서드가 나열되어 있습니다. 내가 무슨 짓을

+0

솔루션으로보기에 편할지 모르겠다. 직렬화 엔진의 캡슐화를 깨뜨린 것처럼 보인다 ... 바이너리 시리얼 라이저가 사용하는 형식이 변경되면 또는 직렬화 엔진을 변경하면 많은 일들이 일어날 것입니다. 이유는 분명하지 않습니다 ... –

+0

직렬화 엔진을 변경하면 기존에 저장된 모든 데이터와 함께 작업 할 수 있습니다 ...하지만 맞아, 이것은 캡슐화를 깨뜨리지 않습니다. 따라서 대량의 작업을 수행하기 위해 내장 엔진을 호출하기 전에 약간의 메타 데이터 (클래스/어셈블리 이름)를 작성하는 자체 직렬화 엔진을 작성하십시오. 직렬화 복원은 해당 메타 데이터를 읽고 해당 클래스가 있는지 확인한 다음 해당되는 경우 내장 된 엔진에 전달합니다. 해당 엔진을 변경할 수 있다고 생각되면 사용자 정의 클래스에 사용중인 엔진을 나타내는 플래그가 포함되어 향후 데이터를 읽을 수 있습니다. –

+0

오, 그게 당신이 한 일이지만 파일의 데이터 대신에 클래스 구조를 가진 것입니다. –

관련 문제