2010-05-28 50 views
7

Tôi đã cố gắng để lấy mã sau đây để làm việc (tất cả mọi thứ được định nghĩa trong cùng một assembly):Làm cách nào để chuyển tham chiếu dưới dạng tham số phương thức trên AppDomain?

namespace SomeApp{ 

public class A : MarshalByRefObject 
{ 
    public byte[] GetSomeData() { // } 
} 

public class B : MarshalByRefObject 
{ 
    private A remoteObj; 

    public void SetA(A remoteObj) 
    { 
     this.remoteObj = remoteObj; 
    } 
} 

public class C 
{ 
    A someA = new A(); 
    public void Init() 
    { 
     AppDomain domain = AppDomain.CreateDomain("ChildDomain"); 
     string currentAssemblyPath = Assembly.GetExecutingAssembly().Location; 
     B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B; 
     remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type." 
    } 
} 

} 

Những gì tôi đang cố gắng làm là vượt qua một tài liệu tham khảo của 'A' dụ tạo ra trong AppDomain đầu tiên cho miền con và có miền con thực hiện một phương thức trên miền đầu tiên. Trong một số điểm trên mã 'B' tôi sẽ gọi 'remoteObj.GetSomeData()'. Điều này phải được thực hiện vì 'byte []' từ phương thức 'GetSomeData' phải được 'tính' trên appdomain đầu tiên. Tôi nên làm gì để tránh ngoại lệ, hoặc tôi có thể làm gì để đạt được kết quả tương tự?

+0

Mã của bạn hoạt động cho tôi. –

+0

+1 Đối với tôi nữa. Phiên bản CLR, Visual Studio (nếu có), C#, v.v ...? Bất kỳ hoàn cảnh nào khác? –

+0

lạ, tôi sẽ kiểm tra lại –

Trả lời

2

Tôi có thể sao chép vấn đề và dường như có liên quan đến TestDriven.net và/hoặc xUnit.net. Nếu tôi chạy C.Init() như là một phương pháp thử nghiệm, tôi nhận được thông báo lỗi tương tự. Tuy nhiên, nếu tôi chạy C.Init() từ một ứng dụng giao diện điều khiển, tôi không nhận được ngoại lệ.

Bạn có thấy điều tương tự, chạy C.Init() từ thử nghiệm đơn vị không?

Chỉnh sửa: Tôi cũng có thể sao chép vấn đề bằng NUnit và TestDriven.net. Tôi cũng có thể sao chép lỗi bằng cách sử dụng nhân tố NUnit thay vì TestDriven.net. Vì vậy, vấn đề dường như liên quan đến việc chạy mã này thông qua một khung kiểm thử, mặc dù tôi không chắc chắn tại sao.

+0

Nó không nằm trong một khuôn khổ thử nghiệm, nhưng nó đã giúp tôi hình dung điều gì đã gây ra nó, cảm ơn. –

+0

Tuyệt, điều gì đã gây ra nó? –

+0

Tôi không chắc chắn lắm, nhưng nó có liên quan đến đường dẫn khởi động ứng dụng –

10

Nguyên nhân gốc thực sự là dll của bạn đã được tải từ các vị trí khác nhau trong hai miền ứng dụng khác nhau. Điều này khiến .NET nghĩ rằng chúng là các assembly khác nhau, tất nhiên có nghĩa là các kiểu khác nhau (mặc dù chúng có cùng tên lớp, không gian tên vv).

Lý do kiểm tra của Jeff không thành công khi chạy qua khung kiểm tra đơn vị là do các khung kiểm tra đơn vị thường tạo AppDomains với ShadowCopy được đặt thành "true". Nhưng AppDomain được tạo thủ công của bạn sẽ mặc định là ShadowCopy = "false". Điều này sẽ khiến các dll được tải từ các vị trí khác nhau dẫn đến "Loại đối tượng" đẹp không thể chuyển đổi thành loại mục tiêu. " lỗi.

CẬP NHẬT: Sau khi thử nghiệm thêm, có vẻ như xuống đến ApplicationBase khác nhau giữa hai AppDomains. Nếu chúng khớp nhau, thì kịch bản trên sẽ hoạt động. Nếu chúng khác nhau thì không (mặc dù tôi đã xác nhận rằng dll được nạp vào cả hai AppDomains từ cùng một thư mục sử dụng windbg) Ngoài ra, nếu tôi bật ShadowCopy = "true" trong cả hai AppDomains của tôi, thì nó thất bại với một thông báo khác: "System.InvalidCastException: Object phải triển khai IConvertible".

UPDATE2: Đọc thêm dẫn tôi tin rằng nó có liên quan đến Load Contexts. Khi bạn sử dụng một trong các phương thức "From" (Assembly.LoadFrom, hoặc appDomain.CreateInstanceFromAndUnwrap), nếu assembly được tìm thấy trong một trong các đường dẫn tải bình thường (ApplicationBase hoặc một trong các đường dẫn thăm dò) thì nó được nạp vào Default Tải ngữ cảnh. Nếu assembly không được tìm thấy ở đó, nó sẽ được tải vào Context Context. Vì vậy, khi cả hai AppDomains có phù hợp với ApplicationBase của, sau đó mặc dù chúng tôi sử dụng một "Từ" phương pháp, chúng đều được nạp vào AppDomain của họ mặc định Tải ngữ cảnh. Nhưng khi các ApplicationBase là khác nhau, thì một AppDomain sẽ có assembly trong Default Load Context của nó trong khi bên kia có assembly trong nó là Load-From Context.

+0

nhưng ngay cả sau khi ** 'thiết lập.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;' ** vấn đề vẫn trình bày ... – Shelest

+0

@Shelest Hãy thử thiết lập ApplicationBase của bạn để Path.GetDirectoryName ("đường dẫn của tập tin dll") – Anshul

+0

@RussellMcClure : Cảm ơn thông tin của bạn - nó giúp tôi giải quyết vấn đề này cho tôi. Có thể bạn có thể có một cái nhìn vào câu trả lời của tôi và cho tôi những suy nghĩ của bạn về giải pháp của tôi vì nó không phải là thực sự thanh lịch IMO ... – ChrFin

0

Đây là một bình luận cho @RussellMcClure nhưng vì nó là đến phức tạp cho một lời nhận xét tôi đăng bài này như một câu trả lời:

tôi bên trong một ASP.NET ứng dụng và tắt bóng-copy (mà cũng có thể giải quyết vấn đề) là không thực sự là một lựa chọn, nhưng tôi thấy các giải pháp sau đây:

AppDomainSetup adSetup = new AppDomainSetup(); 
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true") 
{ 
    var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
    if (shadowCopyDir.Contains("assembly")) 
     shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly")); 

    var privatePaths = new List<string>(); 
    foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll")) 
    { 
     var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault(); 
     if (!String.IsNullOrWhiteSpace(shadowPath)) 
      privatePaths.Add(Path.GetDirectoryName(shadowPath)); 
    } 

    adSetup.ApplicationBase = shadowCopyDir; 
    adSetup.PrivateBinPath = String.Join(";", privatePaths); 
} 
else 
{ 
    adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; 
    adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; 
} 

này sẽ sử dụng thư mục shadow-bản sao của chính app-miền làm cơ sở ứng dụng và thêm tất cả các cụm sao được sao chép vào đường dẫn riêng nếu shadow-copy được kích hoạt.

Nếu ai đó có cách làm tốt hơn, vui lòng cho tôi biết.

Các vấn đề liên quan