Có cách nào để buộc tất cả các hội đồng tham chiếu được tải vào miền ứng dụng không?


83

Các dự án của tôi được thiết lập như thế này:

  • Dự án "Định nghĩa"
  • Dự án "Thực hiện"
  • Dự án "Người tiêu dùng"

Dự án "Người tiêu dùng" tham chiếu đến cả "Định nghĩa" và "Triển khai", nhưng không tham chiếu tĩnh bất kỳ loại nào trong "Triển khai".

Khi ứng dụng khởi động, Dự án "Người tiêu dùng" gọi một phương thức tĩnh trong "Định nghĩa", phương thức này cần tìm các loại trong "Triển khai"

Có cách nào tôi có thể buộc bất kỳ hội đồng tham chiếu nào được tải vào Miền ứng dụng mà không cần biết đường dẫn hoặc tên, và tốt nhất là không cần phải sử dụng khung IOC chính thức không?


1
Nó đang gây ra vấn đề gì? Tại sao bạn cần phải ép tải?
Mike Hai

Nó không nhận được nạp ở tất cả, có lẽ vì không có sự phụ thuộc tĩnh
Daniel Schaffer

Bạn đang cố gắng "tìm các loại" trong triển khai như thế nào? Bạn đang tìm kiếm thứ gì đó triển khai một giao diện cụ thể?
Mike Hai

2
@Mike: Vâng. Tôi đang thực hiện AppDomain.CurrentDomain.GetAssemblies, và sử dụng truy vấn linq để gọi đệ quy GetTypes () trên mỗi người trong số họ.
Daniel Schaffer

Câu trả lời:


90

Điều này dường như thực hiện thủ thuật:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();

var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();

toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

Như Jon đã lưu ý, giải pháp lý tưởng sẽ cần phải đệ quy lại thành phần phụ thuộc cho mỗi cụm được tải, nhưng trong kịch bản cụ thể của tôi, tôi không phải lo lắng về điều đó.


Cập nhật: Khung hỗ trợ mở rộng được quản lý (System.ComponentModel) có trong .NET 4 có nhiều phương tiện tốt hơn để hoàn thành những việc như thế này.


5
Điều này không hoạt động đối với tôi, các hội đồng tham chiếu của tôi, không được tải, không hiển thị trong AppDomain.CurrentDomain.GetAssemblies () .. Hmm ...
Ted

11
Cơ sở vật chất gì? Tôi đã không tìm thấy bất cứ điều gì thông qua tìm kiếm.
nuzzolilo

8
Sử dụng MEF, đoạn mã trên có thể được rút ngắn thành: new DirectoryCatalog(".");(yêu cầu tham chiếu System.ComponentModel.Composition).
Allon Guralnek

1
Câu trả lời này đã giải quyết được vấn đề của tôi và phù hợp với tôi. Tôi có một dự án thử nghiệm đơn vị MS tham chiếu đến một tổ hợp khác của tôi và AppDomain.CurrentDomain.GetAssemblies () không trả về lắp ráp đó khi chạy thử nghiệm. Tôi nghi ngờ rằng mặc dù các bài kiểm tra đơn vị của tôi đang sử dụng mã từ thư viện đó, hội đồng có thể không hiển thị trong "GetAssemblies" vì cách vs.net tải dự án kiểm tra đơn vị MS (thư viện lớp) so với chạy một ứng dụng .exe. Một điều cần lưu ý nếu mã của bạn sử dụng phản chiếu và không đạt các bài kiểm tra đơn vị của bạn.
Dean Lunz

4
Chỉ muốn thêm, hãy cẩn thận với các tập hợp được tải động Thành viên được gọi không được hỗ trợ trong một tập hợp động. Lọc ra các tập hợp mà IsDynamic = false hoặc nếu bạn có thể chịu được lỗi khi tải, hãy thử / bắt lệnh gọi của bạn tới CurrentDomain.Load. Và assembly.Location. Cái đó cũng cần được kiểm tra. Không hoạt động cho IsDynamiccác hội đồng.
Eli Gassert

63

Bạn có thể sử dụng Assembly.GetReferencedAssembliesđể nhận một AssemblyName[], và sau đó gọi Assembly.Load(AssemblyName)cho từng người trong số họ. Tất nhiên, bạn sẽ cần phải đệ quy - nhưng tốt hơn là theo dõi các tập hợp bạn đã tải :)


Tôi đã tìm thấy điều đó, nhưng vấn đề là tôi phải làm bất cứ điều gì tôi đang làm từ assembly được tham chiếu ... và ít nhất là trong ngữ cảnh của bài kiểm tra đơn vị, GetCallingAssembly, GetExecutingAssembly tất nhiên trả về assembly được tham chiếu và GetEntryAssembly trả về null : \
Daniel Schaffer

4
Nếu bạn đang sau khi tải các cụm tham chiếu thì phần trên sẽ giải quyết được vấn đề của bạn. Bạn cũng có thể hỏi một loại cụ thể typeof (T) .Assembly nếu điều đó hữu ích. Tôi có cảm giác rằng những gì bạn cần là tải động các hợp ngữ có chứa phần triển khai (không được tham chiếu). Nếu đúng như vậy, bạn sẽ phải giữ một danh sách tên tĩnh và tải chúng theo cách thủ công hoặc đi qua toàn bộ thư mục của bạn, tải và sau đó tìm loại có giao diện phù hợp.
Fadrian Sudaman

1
@vanhelgen: Theo kinh nghiệm của tôi, hiếm khi bạn cần phải nói rõ ràng. Thông thường "tải theo yêu cầu" của CLR hoạt động tốt.
Jon Skeet

2
Trong các trường hợp bình thường, điều đó có thể đúng nhưng khi sử dụng DI container để khám phá các dịch vụ có sẵn (thông qua System.Reflection), nó sẽ tự nhiên không tìm thấy các dịch vụ chứa trong các tập hợp chưa được tải. Cách tiếp cận mặc định của tôi kể từ đó là tạo một lớp con giả từ một loại ngẫu nhiên của mọi tổ hợp được tham chiếu trong CompositionRoot của ứng dụng của tôi để đảm bảo tất cả các phụ thuộc đều có sẵn. Tôi hy vọng mình có thể bỏ qua điều vô nghĩa này bằng cách tải trước mọi thứ, ngay cả khi phải tăng thêm thời gian khởi động. @JonSkeet có cách nào khác để làm điều này không? thx
mfeineis

12
Trường hợp điều này rơi xuống là GetReferencedAssemblies rõ ràng trả về một danh sách "được tối ưu hóa" - vì vậy nếu bạn không gọi mã một cách rõ ràng trong một hội đồng tham chiếu, nó sẽ không được đưa vào. ( Theo cuộc thảo luận này )
FTWinston

23

chỉ muốn chia sẻ một ví dụ đệ quy. Tôi đang gọi phương thức LoadReferencedAssembly trong quy trình khởi động của mình như sau:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    this.LoadReferencedAssembly(assembly);
}

Đây là phương pháp đệ quy:

private void LoadReferencedAssembly(Assembly assembly)
{
    foreach (AssemblyName name in assembly.GetReferencedAssemblies())
    {
        if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName))
        {
            this.LoadReferencedAssembly(Assembly.Load(name));
        }
    }
}

5
Tôi tự hỏi liệu các tham chiếu lắp ráp vòng tròn có thể gây ra các ngoại lệ tràn ngăn xếp được ném ra hay không.
Ronnie Overby

1
Ronnie, tôi tin rằng không, mã chỉ chạy đệ quy trong trường hợp namekhông có trong đã AppDomain.CurrentDomain.GetAssemblies(), có nghĩa là nó sẽ chỉ tái diễn nếu foreachnhặt AssemblyNameđược chưa nạp.
Felype

1
Tôi không hài lòng về O(n^2)thời gian chạy của thuật toán này ( GetAssemblies().Any(...)bên trong a foreach)). Tôi sẽ sử dụng một HashSetđể đưa điều này xuống một cái gì đó theo thứ tự O(n).
Đài

16

Nếu bạn sử dụng Fody.Costura hoặc bất kỳ giải pháp hợp nhất lắp ráp nào khác, câu trả lời được chấp nhận sẽ không hoạt động.

Phần sau tải các Hội đồng tham chiếu của bất kỳ Hội đồng nào đang được tải. Đệ quy được để lại cho bạn.

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

loadedAssemblies
    .SelectMany(x => x.GetReferencedAssemblies())
    .Distinct()
    .Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false)
    .ToList()
    .ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));

Bạn muốn tư vấn đoạn mã này nên đi đâu?
Telemat

1
trong bộ tải khởi động / khởi động của bạn mà tôi tưởng tượng.
Meirion Hughes

1
Tôi có thể sai, nhưng tôi nghĩ rằng bạn chỉ có thể kiểm tra !y.IsDynamictrong bạn.Where
Felype

1

Khi tôi phải tải một assembly + các phụ thuộc từ một đường dẫn cụ thể, hôm nay tôi đã viết lớp này để thực hiện.

public static class AssemblyLoader
{
    private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>();

    static AssemblyLoader()
    {
        AssemblyDirectories[GetExecutingAssemblyDirectory()] = true;
        AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;

    }

    public static Assembly LoadWithDependencies(string assemblyPath)
    {
        AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true;
        return Assembly.LoadFile(assemblyPath);
    }

    private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        string dependentAssemblyName = args.Name.Split(',')[0] + ".dll";
        List<string> directoriesToScan = AssemblyDirectories.Keys.ToList();

        foreach (string directoryToScan in directoriesToScan)
        {
            string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName);
            if (File.Exists(dependentAssemblyPath))
                return LoadWithDependencies(dependentAssemblyPath);
        }
        return null;
    }

    private static string GetExecutingAssemblyDirectory()
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        var uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}

1
mã tốt, ngoại trừ không cần Từ điển, trong trường hợp này chỉ cần một danh sách đơn giản là đủ. Tôi giả sử rằng mã ban đầu của bạn cần phải biết những tổ hợp nào đã được tải và những tổ hợp nào chưa được tải, đó là lý do tại sao bạn có Từ điển.
Reinis

0

Tuy nhiên, một phiên bản khác (dựa trên câu trả lời của Daniel Schaffer ) là trường hợp khi bạn có thể không cần tải tất cả các Assemblies, nhưng một số lượng được xác định trước trong số đó:

var assembliesToLoad = { "MY_SLN.PROJECT_1", "MY_SLN.PROJECT_2" };

// First trying to get all in above list, however this might not 
// load all of them, because CLR will exclude the ones 
// which are not used in the code
List<Assembly> dataAssembliesNames =
   AppDomain.CurrentDomain.GetAssemblies()
            .Where(assembly => AssembliesToLoad.Any(a => assembly.GetName().Name == a))
            .ToList();

var loadedPaths = dataAssembliesNames.Select(a => a.Location).ToArray();

var compareConfig = StringComparison.InvariantCultureIgnoreCase;
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
    .Where(f =>
    {
       // filtering the ones which are in above list
       var lastIndexOf = f.LastIndexOf("\\", compareConfig);
       var dllIndex = f.LastIndexOf(".dll", compareConfig);

       if (-1 == lastIndexOf || -1 == dllIndex)
       {
          return false;
       }

       return AssembliesToLoad.Any(aName => aName == 
          f.Substring(lastIndexOf + 1, dllIndex - lastIndexOf - 1));
     });

var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();

toLoad.ForEach(path => dataAssembliesNames.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

if (dataAssembliesNames.Count() != AssembliesToLoad.Length)
{
   throw new Exception("Not all assemblies were loaded into the  project!");
}

0

Nếu bạn có các hội đồng mà không có mã nào được tham chiếu tại thời điểm biên dịch, các hội đồng đó sẽ không được đưa vào làm tham chiếu đến hội ngữ khác của bạn, ngay cả khi bạn đã thêm gói dự án hoặc gói số làm tham chiếu. Điều này không phụ thuộc vào Debughoặc Releasecài đặt xây dựng, tối ưu hóa mã, v.v. Trong những trường hợp này, bạn phải gọi rõ ràng Assembly.LoadFrom(dllFileName)để tải lắp ráp.


0

Để nhận được assembly tham chiếu theo tên, bạn có thể sử dụng phương pháp sau:

public static Assembly GetAssemblyByName(string name)
{
    var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == name);
    if (asm == null)
        asm = AppDomain.CurrentDomain.Load(name);
    return asm;
}

0

Trong ứng dụng winforms của mình, tôi cung cấp cho JavaScript (trong một điều khiển WebView2) khả năng gọi nhiều thứ .NET khác nhau, ví dụ như các phương thức Microsoft.VisualBasic.Interactiontrong hợp ngữ Microsoft.VisualBasic.dll (chẳng hạn như InputBox()v.v.).

Nhưng ứng dụng của tôi như vậy không sử dụng lắp ráp đó, vì vậy lắp ráp không bao giờ được tải.

Vì vậy, để buộc lắp ráp tải, tôi đã kết thúc chỉ cần thêm điều này vào Form1_Load của mình:

if (DateTime.Now < new DateTime(1000, 1, 1, 0, 0, 0)) { // never happens
  Microsoft.VisualBasic.Interaction.Beep();
  // you can add more things here
}

Trình biên dịch nghĩ rằng hợp ngữ có thể cần thiết, nhưng trong thực tế, điều này tất nhiên không bao giờ xảy ra.

Không phải là một giải pháp quá phức tạp, nhưng nhanh chóng và bẩn thỉu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.