C# İle Modüler Yapıda Bir Eklenti Sistemi Nasıl Yapılır?

C# İle Modüler Yapıda Bir Eklenti Sistemi Nasıl Yapılır?
C# İle Modüler Yapıda Bir Eklenti Sistemi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Bu projeyi hayata geçirmek için bilgisayarınızda .NET 9 SDK veya daha güncel bir sürümün yüklü olması gerekmektedir. Geliştirme ortamı olarak Visual Studio 2022 veya JetBrains Rider kullanmanız önerilir. Proje yapımız üç ana parçadan oluşacaktır: Ana Uygulama (Host), Eklenti Arayüzü (Contract) ve Eklenti (Plugin).

  • SDK: .NET 9.0 veya üzeri.
  • IDE: Visual Studio 2022 (v17.12+).
  • Temel Bilgi: C# Interface (Arayüz) mantığı ve temel dosya sistemi işlemleri.

Adım 1: Eklenti Arayüzünü Tanımlama

Eklenti sisteminin temel taşı, ana uygulama ile eklentinin "ortak dil" konuşmasını sağlayan bir Interface kütüphanesidir. Bu kütüphane, hem ana uygulama hem de eklentiler tarafından referans alınmalıdır.

// IPlugin.cs
namespace PluginContract;

public interface IPlugin
{
    string Name { get; }
    string Version { get; }
    void Execute();
}

Bu arayüz, her eklentinin sahip olması gereken zorunlu özellikleri ve metotları belirler. Ana uygulama, eklentiyi yüklediğinde bu arayüz üzerinden işlem yapacaktır.

Adım 2: Eklenti Geliştirme

Şimdi bir eklenti projesi oluşturalım. Bu proje, PluginContract kütüphanesine referans vermeli ve IPlugin arayüzünü uygulamalıdır.

// SamplePlugin.cs
using PluginContract;

public class SamplePlugin : IPlugin
{
    public string Name => "Örnek Eklenti";
    public string Version => "1.0.0";

    public void Execute()
    {
        Console.WriteLine("Eklenti başarıyla çalıştırıldı!");
    }
}

Eklenti projesini derlediğinizde oluşan .dll dosyasını, ana uygulamanın erişebileceği bir "Plugins" klasörüne kopyalamanız gerekecektir.

Adım 3: Dinamik Eklenti Yükleyici Oluşturma

Ana uygulamada, belirtilen klasördeki DLL dosyalarını tarayan ve bunları çalışma zamanında yükleyen bir sınıf oluşturmalıyız. Burada AssemblyLoadContext kullanarak eklentileri izole bir şekilde yükleyeceğiz.

// PluginLoader.cs
using System.Reflection;
using System.Runtime.Loader;

public class PluginLoader : AssemblyLoadContext
{
    private AssemblyDependencyResolver _resolver;

    public PluginLoader(string pluginPath) : base(isCollectible: true)
    {
        _resolver = new AssemblyDependencyResolver(pluginPath);
    }

    protected override Assembly? Load(AssemblyName assemblyName)
    {
        string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
        return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null;
    }
}

Bu sınıf, eklentinin ihtiyaç duyduğu diğer bağımlılıkları çözmek için AssemblyDependencyResolver kullanır. isCollectible: true parametresi, eklentinin uygulama kapanmadan bellekten temizlenebilmesini sağlar.

Adım 4: Eklentileri Çalıştırma ve Test Etme

Şimdi ana uygulama içerisinde eklentileri bulup, arayüz üzerinden çağırma mantığını kuruyoruz.

// Program.cs
using System.Reflection;
using PluginContract;

string pluginPath = Path.Combine(AppContext.BaseDirectory, "Plugins");
var pluginFiles = Directory.GetFiles(pluginPath, "*.dll");

foreach (var file in pluginFiles)
{
    var loader = new PluginLoader(file);
    var assembly = loader.LoadFromAssemblyPath(file);

    foreach (var type in assembly.GetTypes())
    {
        if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface)
        {
            var plugin = Activator.CreateInstance(type) as IPlugin;
            plugin?.Execute();
        }
    }
}

Bu kod bloğu, klasördeki tüm DLL dosyalarını tarar, IPlugin arayüzünü uygulayan sınıfları bulur ve Execute metodunu tetikler.

Eklenti Mimarisi Karşılaştırması

Yöntem Avantaj Dezavantaj
AssemblyLoadContext İzolasyon ve bellek yönetimi Karmaşık yapı
MEF (Managed Extensibility Framework) Hızlı entegrasyon Eski teknoloji (Legacy)
Dependency Injection Modern ve temiz Dinamik yüklemede zorlayıcı
Kritik Güvenlik Uyarısı: Dışarıdan gelen DLL dosyalarını çalıştırmak ciddi bir güvenlik riskidir. Sadece güvendiğiniz kaynaklardan gelen eklentileri yükleyin. Mümkünse eklentileri dijital imza (code signing) ile doğrulayın.

Sıkça Sorulan Sorular

Eklentilerimi nasıl güncellerim?

Eklentiyi yeni bir DLL ile değiştirip uygulamayı yeniden başlatarak veya AssemblyLoadContext'i unload ederek güncelleyebilirsiniz.

Eklenti çökerse ana uygulama etkilenir mi?

Evet, eğer eklenti ana uygulama ile aynı süreçte (process) çalışıyorsa, eklentideki bir hata uygulamayı durdurabilir. Bunun için eklentileri ayrı bir süreçte çalıştırmayı düşünebilirsiniz.

Bağımlılık çakışmalarını nasıl önlerim?

AssemblyLoadContext kullanarak her eklentiyi kendi izole konteynerinde yükleyerek versiyon çakışmalarını engelleyebilirsiniz.

Performans kaybı yaşar mıyım?

Reflection kullanımı başlangıçta küçük bir maliyet yaratır, ancak eklenti yüklendikten sonra performans normal kod ile aynıdır.

Eklentiye nasıl veri aktarırım?

Arayüzünüze parametre ekleyerek veya bir "Context" nesnesi üzerinden veri alışverişi sağlayabilirsiniz.

Gelişmiş Eklenti Yönetimi: Manifest Dosyası ile Metadata Kontrolü

Eklentilerinizi DLL dosyalarını doğrudan belleğe yüklemeden önce tanımlamak, sistem kaynaklarını korumak adına kritik bir adımdır. Her eklenti klasöründe bulunan bir manifest.json dosyası, eklentinin adı, sürümü ve bağımlılıkları hakkında ön bilgi sağlar.

Aşağıdaki örnekte, eklentiyi yüklemeden önce JSON manifest dosyasını okuyan bir yapı bulunmaktadır:

public class PluginManifest
{
    public string Name { get; set; }
    public string Version { get; set; }
    public string EntryPoint { get; set; }
}

// Eklenti klasöründeki manifesti okuma
public PluginManifest LoadManifest(string pluginFolder)
{
    string path = Path.Combine(pluginFolder, "manifest.json");
    if (!File.Exists(path)) return null;

    string json = File.ReadAllText(path);
    return JsonSerializer.Deserialize(json);
}

Eklenti Sistemlerinde Hata Ayıklama (Debugging) Stratejileri

Dinamik olarak yüklenen eklentilerde hata ayıklamak, standart bir projeye göre daha karmaşıktır. Eklentiler farklı AssemblyLoadContext içerisinde çalıştığı için, Visual Studio'nun eklenti koduna "attach" olması için bazı yapılandırmalar gerekebilir.

İzleme ve Loglama Mekanizması

Eklentilerinizde oluşan hataları merkezi bir loglama sistemine aktarmak için ILogger arayüzünü eklentiye enjekte etmelisiniz. Bu sayede eklenti hata verdiğinde, ana uygulama üzerinden hatanın kaynağını tespit edebilirsiniz.

public interface IPlugin
{
    void Execute(ILogger logger);
}

// Eklenti tarafında kullanım
public void Execute(ILogger logger)
{
    try 
    {
        // Eklenti mantığı
    }
    catch (Exception ex)
    {
        logger.LogError($"Eklenti hatası: {ex.Message}");
    }
}

İleri Düzey İpuçları

  • Sürüm Kontrolü: Ana uygulamanızın desteklediği minimum eklenti sürümünü manifest dosyasında zorunlu tutun.
  • Güvenlik: Eklentileri yüklemeden önce dijital imza (Digital Signature) kontrolü yaparak sadece güvendiğiniz geliştiricilerin kodlarını çalıştırın.
  • Bellek Yönetimi: Eklenti işi bittiğinde AssemblyLoadContext.Unload() metodunu çağırarak bellek sızıntılarını önleyin.
Strateji Avantajı
Manifest Kullanımı Performans artışı ve hızlı filtreleme.
Dependency Injection Modüller arası gevşek bağlılık (loose coupling).
Sandboxing Uygulama kararlılığını koruma.

Sonuç

C# ile modüler bir eklenti sistemi kurmak, uygulamanızın uzun ömürlü ve genişletilebilir olmasını sağlar. Bu rehberde öğrendiğiniz AssemblyLoadContext ve IPlugin arayüzü yaklaşımı, modern .NET uygulamaları için en güvenli ve esnek yöntemdir. Bir sonraki adım olarak, eklentileriniz için bir "Manifest" dosyası (JSON) oluşturarak eklenti bilgilerini DLL'i yüklemeden okumayı deneyebilirsiniz.

Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamında (Production) kullanmadan önce hata yönetimi (try-catch), yetkilendirme ve dosya doğrulama mekanizmalarını projenize entegre etmeyi unutmayın.
Bu yazıya tepkinizi paylaşın:
Mert Çelik

Kendin yap (DIY) projeleri ve teknik tamirat rehberleri konusunda uzmanım. Denenmiş ve test edilmiş yöntemlerle okuyuculara güvenilir bilgiler sunuyorum.

Yorumlar (0)

Yorum Yaz