2009-09-03 5 views
1

동기 부여. 클라이언트 - 서버 응용 프로그램이 있습니다. 어떤 시점에서 서버 측은 클라이언트가 사용할 수없는 특정 메타 데이터를 기반으로 동적으로 새로운 유형을 만듭니다. 서버는 유형의 인스턴스를 클라이언트에 전송해야합니다. 그러나 유형을 알 수 없기 때문에 클라이언트는 인스턴스를 비 직렬화하지 못합니다.AssemblyResolve 메서드 안에 추가 컨텍스트를 포함 할 수 있습니까?

하나의 솔루션은 메타 데이터와 데이터를 함께 묶어 클라이언트에 전송하고 동적 유형과 인스턴스를 다시 만들도록하는 것입니다.

특정 인스턴스가 개체 그래프 안에 깊이 중첩되어 있으면 상황이 더러워집니다. 내가 무엇을하고 싶습니다 개체 그래프를 클라이언트로 보내려면 deserialization 코드가 AppDomain.AssemblyResolved 이벤트를 발생시키고 거기에 각각의 동적 유형을 다시 생성하십시오. 아아! 이벤트 처리기에서 메타 데이터를 사용할 수있게 만드는 방법을 모르므로 할 수 없습니다.

CallContext를 사용해 보았지만 작동하지 않았습니다.

가 여기에 내가에서 내가 성공하지 않았다, 해결책을 찾기 위해 사용되는 전체 예제 코드입니다 :

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Runtime.Remoting.Messaging; 
using System.Security; 
using System.Security.Permissions; 

namespace DynamicTypes 
{ 
    [Serializable] 
    public class LogicalCallContextData : ILogicalThreadAffinative 
    { 
    public string DynamicAssemblyName { get; private set; } 
    public string DynamicTypeName { get; private set; } 

    public LogicalCallContextData(string dynamicAssemblyName, string dynamicTypeName) 
    { 
     DynamicAssemblyName = dynamicAssemblyName; 
     DynamicTypeName = dynamicTypeName; 
    } 
    } 

    class Program 
    { 
    private static string DynamicAssemblyName; 
    private static string DynamicTypeName; 
    private static Type m_type; 

    static void CreateDynamicType() 
    { 
     if (m_type == null) 
     { 
     var assemblyName = new AssemblyName(DynamicAssemblyName); 
     var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
     var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); 
     var typeBuilder = moduleBuilder.DefineType(DynamicTypeName, TypeAttributes.Public | TypeAttributes.Serializable, typeof(object)); 
     var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); 
     var ilGenerator = constructorBuilder.GetILGenerator(); 
     ilGenerator.Emit(OpCodes.Ldarg_0); 
     ilGenerator.Emit(OpCodes.Call, typeof(object).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null)); 
     ilGenerator.Emit(OpCodes.Ret); 
     m_type = typeBuilder.CreateType(); 
     } 
    } 

    static void AppDomainInitialize(string[] args) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; 
    } 

    static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args) 
    { 
     var data = (LogicalCallContextData)CallContext.GetData("test data"); 
     if (data != null) 
     { 
     DynamicAssemblyName = data.DynamicAssemblyName; 
     DynamicTypeName = data.DynamicTypeName; 

     CreateDynamicType(); 
     if (m_type.Assembly.FullName == args.Name) 
     { 
      return m_type.Assembly; 
     } 
     } 
     return null; 
    } 

    [Serializable] 
    private class CrossAppDomain 
    { 
     private object m_obj; 
     public CrossAppDomain() 
     { 
     CreateDynamicType(); 
     m_obj = Activator.CreateInstance(m_type); 
     } 

     public void DoIt() 
     { 
     } 
    } 

    [PermissionSet(SecurityAction.LinkDemand)] 
    static void Main(string[] args) 
    { 
     DynamicAssemblyName = Guid.NewGuid().ToString("N"); 
     DynamicTypeName = Guid.NewGuid().ToString("N"); 

     var data = new LogicalCallContextData(DynamicAssemblyName, DynamicTypeName); 
     CallContext.SetData("test data", data); 

     AppDomainInitialize(null); 
     var appDomainSetup = new AppDomainSetup(); 
     appDomainSetup.AppDomainInitializer = AppDomainInitialize; 
     var appDomain = AppDomain.CreateDomain("second", null, appDomainSetup); 
     appDomain.DoCallBack(new CrossAppDomain().DoIt); 
    } 
    } 
} 

dataOnAssemblyResolve 이벤트 핸들러에 반환 null입니다.

누구나 방법을 알고 있습니까?

편집 : 두 번의 왕복 이동에서 할 수 있습니다. 첫 번째에는 메타 데이터를 전달하고 두 번째에는 개체 자체를 전달합니다. 한 번의 왕복 해결책을 찾고 싶습니다.

편집 : 2 나는 완전히 미친 해결책을 생각해 냈습니다. 그것은 작동하지만, 성능에 대한 의문에 대해 궁금합니다. 동적 유형마다 정확히 하나의 동적 어셈블리를 만들고 해당 어셈블리 이름에 유형의 메타 데이터를 인코딩하면 어떻게됩니까? 나는이 방법을 점검했고 그것은 효과가있는 것으로 보인다. 최대 500 자의 어셈블리 이름을 받았습니다. 각 어셈블리는 단일 모듈 "DynamicModule"과 단일 유형 "DynamicType"을 정의합니다. 아직도 나는 더 나은 해결책을 기대하고있다.

답변

0

비 정적 메서드를 AppDomain.AssemblyResolve 이벤트 처리기로 등록 할 수 있습니다. 그런 다음 인스턴스의 멤버에 액세스 할 수 있습니다.이 메서드는 등록되었습니다. 그것은 꽤 AssemblyResolver 클래스처럼 내가 여기에 제시하십시오 AssemblyResolve 이벤트가 해고되기 전에 직렬화에

Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true

당신은 AssemblyResolver 인스턴스에서 메타 데이터를 저장할 수 있습니다. 흥미로운 점은 "언제"메타 데이터를 AssemblyResolver에 저장하는지입니다. 단일 deserialization 실행을 요구하면 동적 유형의 객체를 보유하는 객체에 메타 데이터의 비 직렬화를 구현해야합니다. 어쩌면 편의를 위해 일종의 래퍼에 동적 객체를 넣을 수 있습니다. 래퍼가 메타 데이터와 동적 유형 객체 (직렬화에 따라 문자열 또는 바이트 []로 직렬화 됨)를 가져 오게합니다. 래퍼의 비 직렬화 프로세스를 사용자 지정하여 먼저 메타 데이터를 AssemblyResolver에 푸시합니다. 그런 다음 래퍼의 string 또는 byte [] 멤버에서 동적 유형의 객체를 deserialize합니다.

아마도 AssemblyResolver에 액세스하는 가장 간단한 솔루션은 싱글 톤 패턴이지만, 대다수는 의존성 주입을 선호합니다.

실제로 개체 구조의 일부분에 대해 재귀 적 직렬화가 "로컬"로 실행됩니다. 그러나 상위 수준의 개체 구조 deserialization에 아무런 영향을주지 않습니다. 메타 데이터를 푸시하기 전에 AssemblyResolver를 차단해야하기 때문에이 솔루션은 스레드로부터 안전 할 수있는 추가 작업이 필요합니다. 다른 동적 유형의 객체를 보유하는 동적 유형의 객체가있는 경우 문제가 발생합니다. AssemblyResolve 이벤트 처리가 끝나면 AssemblyResolver를 릴리스해야하기 때문입니다.

관련 문제