2012-05-07 19 views
8

Vui lòng đọc toàn bộ câu hỏi. Tôi có một tình huống duy nhất với một số ràng buộc mà tôi muốn giải quyết.Đang tải một bản lắp ráp và áp dụng biến vị ngữ trên các loại của nó trong một tên miền ứng dụng khác

Trong mã của tôi, tôi có cây biểu thức được biên soạn thành Predicate<System.Type>. Mục tiêu của tôi là tải một assembly mà không khóa nó (nó là assembly đầu ra của project, được xây dựng lại liên tục), áp dụng vị từ này vào danh sách các kiểu của nó và lấy lại danh sách kiểu kết quả tên:

// this is what I need: 
return assembly.GetTypes().Where(t => predicate(t)).Select(t => t.FullName); 

Hội đồng này phải được tải trong một miền ứng dụng khác, bởi vì tôi muốn dỡ bỏ nó ngay sau khi tôi nhận được thông tin tôi yêu cầu.

Đây là nơi nó trở nên phức tạp. Có một số vấn đề tôi đang gặp phải:

Nếu tôi tải cụm từ trong miền ứng dụng khác và chỉ cần trả về một mảng của tất cả các loại của nó, để tôi có thể áp dụng vị từ đó trong miền ứng dụng chính của mình được marshalled trở lại appdomain chính của tôi tôi nhận được một FileNotFoundException, nói rằng lắp ráp này không được tìm thấy. Điều này làm cho sence, bởi vì lắp ráp chỉ được nạp trong appdomain khác tôi tạo ra. Tải nó cũng trong appdomain chính sẽ đánh bại mục đích.

Nếu, cách khác, tôi cố gắng chuyển vị từ này vào miền ứng dụng khác, để áp dụng nó và lấy lại một chuỗi các chuỗi (tên đầy đủ), tôi nhận được SerializationException: "Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly.", vì biến vị ngữ là một phương thức động (được biên dịch) từ một cây biểu thức).

Tải nó trong tên miền phụ chính sẽ không có vấn đề gì, nhưng vì không thể dỡ một assembly đã tải mà không dỡ toàn bộ appdomain, ngay khi assembly sẽ thay đổi (sau khi xây dựng lại), bất kỳ nỗ lực nào để nạp assembly với cùng một tên sẽ dẫn đến một ngoại lệ.

Bối cảnh:
Tôi đang xây dựng một plugin cho ReSharper gọi Agent Mulder. Ý tưởng đằng sau plugin là phân tích đăng ký DI/IoC Container trong giải pháp của bạn và giúp ReSharper tìm ra cách sử dụng các loại đã đăng ký qua DI Container (bạn có thể xem video ngắn về cách hoạt động của here).

Phần lớn, phân tích đăng ký vùng chứa là đơn giản - tôi chỉ phải phát hiện đủ thông tin để biết loại bê tông nào bị ảnh hưởng. Trong ví dụ này với Castle Windsor: Component.For<IFoo>().ImplementedBy<Foo>() loại kết quả là hiển nhiên, như vậy là AllTypes.FromThisAssembly().BasedOn<IFoo>() - cung cấp cho tôi đủ thông tin để đánh giá các loại cụ thể sẽ bị ảnh hưởng bởi dòng này. Tuy nhiên, hãy xem xét đăng ký này tại Lâu đài Windsor:

container.Register(Classes 
    .FromAssemblyInDirectory(new AssemblyFilter(".").FilterByName(an => an.Name.StartsWith("Ploeh.Samples.Booking"))) 
    .Where(t => !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dispatcher<>))) 
    .WithServiceAllInterfaces()); 

(source)

Đây là thông tin phụ thuộc vào một vị đó sẽ chỉ được đánh giá trong thời gian chạy.

Vì tất cả những gì tôi có thể làm là phân tích tĩnh điều này, tôi có trong tay đại diện AST của ReSharper (được gọi là PSI trong ReSharper) của biểu thức lambda từ mệnh đề Where. Tôi có thể chuyển đổi AST này thành một cây biểu thức LINQ, sau đó biên dịch nó thành một đại biểu.

Ý tưởng của tôi là tải cụm đầu ra (được xác định bởi bộ mô tả FromAssembly*) qua phản xạ và áp dụng đại biểu này cho các loại lắp ráp để có được tên kiểu (tôi chỉ cần tên). Điều này sẽ phải được đánh giá lại mỗi lần lắp ráp thay đổi, quá (Tôi không quan tâm vào thời điểm này về hiệu suất). Trong kết luận, trừ khi ai đó có thể đề xuất một cách tốt hơn để xác định các loại bị ảnh hưởng bởi vị từ, tôi muốn biết cách làm điều này với sự phản chiếu (không may là tôi đã không coi các trình đọc siêu dữ liệu khác, vì tôi muốn phải bằng cách nào đó chuyển đổi biểu thức lambda AST thành một biến vị ngữ của kiểu dữ liệu khác nhau, và tôi không biết nếu có tồn tại một chuyển đổi 1-đến-1).

Cảm ơn bạn đã đọc. Câu hỏi này sẽ có tiền thưởng 500 điểm khi nó có sẵn.

+0

Nếu 'Expression' là serializable, bạn có thể tạo ra các' Predicate' delegate như một 'Expression' và chuyển nó qua ranh giới appdomain và biên dịch nó trong appdomain 'từ xa'. Nhưng thật đáng buồn, đó không phải là trường hợp đó: ( – leppie

+0

@leppie Vâng, tôi đã nghĩ về việc sắp xếp lại biểu thức và chuyển nó đi, bạn nói đúng, nó không đơn giản như vậy ... –

Trả lời

2

Bạn cần tải lắp ráp để có được các phiên bản Type, do đó, một AppDomain riêng biệt có vẻ như giải pháp phù hợp.

Vì vậy, bạn cần lấy vị từ Expression vào số AppDomain, điều đó có nghĩa là bạn phải tuần tự hóa/deserialize nó.

Yêu cầu này ngày càng trở nên thường xuyên vì nhiều lý do khác nhau. Tôi đã nhìn vào điều này bởi vì tôi muốn phun LINQ to Entities biểu hiện trên một dịch vụ WCF.

May mắn thay, có một vài triển khai hiện có.

Tôi thấy điều này một: CodePlex - Expression Tree Serializer


Tôi vừa thử nghiệm nó với Types, và các công trình này:

Expression<Func<Type,bool>> predicate = 
    t => (!t.IsGenericType && t.Name == "Int32"); 

var s = new ExpressionSerialization.ExpressionSerializer(); 
var xml = s.Serialize(predicate); 

var p = s.Deserialize(xml) as Expression<Func<Type, bool>>; 
var f = p.Compile(); 

Console.WriteLine("Int32: " + f(typeof(int))); // true 
Console.WriteLine("String: " + f(typeof(string))); // false 
+0

Cảm ơn bạn! Sau khi thêm nhào lộn (XElement bản thân nó không được đánh dấu là '[Serializable]', và điều này phá vỡ sự chuyển giao giữa appdomains), tôi đã làm việc xung quanh nó bằng cách chuyển đổi/từ một chuỗi. Nếu nó hoạt động, câu trả lời của bạn có thể là :) –

+0

Bạn được chào đón. Thư viện đó đã cứu tôi rất nhiều công việc. Xin vui lòng cho tôi biết nếu bạn tìm thấy bất kỳ trường hợp nào nó không xử lý. –

1

Lets bọc ngữ thể hiện ủy nhiệm với đối tượng MBR, tham số có kiểu Type sẽ bị so sánh tốt với tên miền khác:

class RemotablePredicate<T> : MarshalByRefObject 
{ 
    readonly Predicate<T> predicate; 
    public RemotablePredicate(Predicate<T> predicate) { this.predicate = predicate; } 
    public bool Accepts(T arg) { return predicate(arg); } 
} 

Xây dựng một số loại để tải, khám phá lắp ráp và trả lại kết quả cho tên miền chính:

class AssemblyExplorer : MarshalByRefObject 
{ 
    public string[] GetTypesByPredicate(
    string assemblyPath, RemotablePredicate<Type> predicate) 
    { 
    // MS reflection api reqires all dependencies here 
    var bytes = File.ReadAllBytes(assemblyPath); 
    var assembly = Assembly.ReflectionOnlyLoad(bytes); 

    var types = new List<string>(); 
    foreach (var type in assembly.GetTypes()) 
     if (predicate.Accepts(type)) 
     types.Add(type.FullName); 

    return types.ToArray(); 
    } 
} 

Làm cho tất cả công việc này:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq.Expressions; 
using System.Reflection; 

class Program 
{ 
    static void Main() 
    { 
    var fooDomain = AppDomain.CreateDomain("Foo"); 

    Expression<Predicate<Type>> expr = t => t.IsValueType; 
    var compiledPredicate = expr.Compile(); 
    var remotablePredicate = new RemotablePredicate<Type>(compiledPredicate); 

    var explorerType = typeof(AssemblyExplorer); 
    var explorerInstance = (AssemblyExplorer) fooDomain 
     .CreateInstanceAndUnwrap(explorerType.Assembly.FullName, explorerType.FullName); 

    var types = explorerInstance.GetTypesByPredicate(
     "JetBrains.Annotations.dll", remotablePredicate); 

    Console.WriteLine("Matched types: {0}", types.Length); 
    foreach (var type in types) Console.WriteLine(" {0}", type); 
    } 
}