+2

Lập trình ứng dụng WPF với kiến trúc Plugin

Việc tạo ra một ứng dụng WPF liên quan đến sự kết hợp giữa các khuôn khổ và mẫu kiến trúc mạnh mẽ để đảm bảo khả năng bảo trì, khả năng mở rộng và tính linh hoạt. Bài viết này sẽ thảo luận về việc phát triển ứng dụng WPF bằng cách sử dụng mẫu MVVM (Model-View-ViewModel), EF Core để tích hợp cơ sở dữ liệu, kiến trúc plugin và Autofac đi kèm Dependency Injection. Trọng tâm chính sẽ là triển khai hệ thống quản lý người dùng mạnh mẽ bao gồm xác thực, ủy quyền và quản lý vai trò. Ngoài ra, chúng ta sẽ khám phá giao tiếp giữa các plugin bằng cách sử dụng trình tổng hợp sự kiện và nhà cung cấp dịch vụ dùng chung, đồng thời trình bày phiên bản plugin và tải trong cửa sổ chính

Tổng quan về kiến trúc

Các thành phần chính của ứng dụng của tôi bao gồm:

  • Mô hình MVVM: Phân tách mối quan tâm giữa UI và logic kinh doanh.
  • EF Core: Truy cập và quản lý cơ sở dữ liệu.
  • Kiến trúc plugin: Khả năng mở rộng thông qua các plugin được tải động.
  • Autofac: Quản lý việc tiêm phụ thuộc.
  • Trình tổng hợp sự kiện và nhà cung cấp dịch vụ chia sẻ: Tạo điều kiện thuận lợi cho việc giao tiếp giữa các plugin.

Triển khai ứng dụng chính

1. Cấu hình Dependency Injection với Autofac

Autofac cho phép chúng ta quản lý các phụ thuộc theo cách linh hoạt và có thể mở rộng. Dưới đây là một ví dụ về cấu hình cho DI container:

public class Bootstrapper
{
    public IContainer Bootstrap()
    {
        var builder = new ContainerBuilder();

        // Register MVVM components
        builder.RegisterType<MainViewModel>().AsSelf();
        builder.RegisterType<MainWindow>().AsSelf();

        // Register EF Core DbContext
        builder.RegisterType<AppDbContext>().AsSelf().InstancePerLifetimeScope();

        // Register Plugin Loader
        builder.RegisterType<PluginLoader>().As<IPluginLoader>();

        // Register Event Aggregator
        builder.RegisterType<EventAggregator>().As<IEventAggregator>().SingleInstance();

        // Register AuthService
        builder.RegisterType<AuthService>().As<IAuthService>().InstancePerLifetimeScope();

        return builder.Build();
    }
}

2. Tải Plugin và hiển thị trang đích

Ứng dụng tải các plugin trong khi khởi động và hiển thị một trang đích đơn giản. Sau đây là cách các plugin được tải:

public interface IPlugin
{
    void Initialize(IContainer container);
}

public class PluginLoader : IPluginLoader
{
    private readonly IContainer _container;

    public PluginLoader(IContainer container)
    {
        _container = container;
    }

    public void LoadPlugins(string path)
    {
        var pluginAssemblies = Directory.GetFiles(path, "*.dll")
                                         .Select(Assembly.LoadFrom);
        foreach (var assembly in pluginAssemblies)
        {
            var pluginTypes = assembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract);
            foreach (var pluginType in pluginTypes)
            {
                var plugin = (IPlugin)Activator.CreateInstance(pluginType);
                plugin.Initialize(_container);
            }
        }
    }
}

Để hiển thị trang đích, chúng tôi đảm bảo MainWindow được thiết lập chính xác:

public partial class MainWindow : Window
{
    private readonly IPluginLoader _pluginLoader;

    public MainWindow(IPluginLoader pluginLoader)
    {
        InitializeComponent();
        _pluginLoader = pluginLoader;
        Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        _pluginLoader.LoadPlugins("Plugins");
        // Display landing page content
        DataContext = new MainViewModel();
    }
}

Triển khai quản lý người dùng

1. Mô hình dữ liệu cho quản lý người dùng

Tôi bắt đầu bằng cách xác định mô hình dữ liệu cho người dùng, vai trò và mối quan hệ của họ:

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string PasswordHash { get; set; }
    public ICollection<UserRole> UserRoles { get; set; }
}

public class Role
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<UserRole> UserRoles { get; set; }
}

public class UserRole
{
    public int UserId { get; set; }
    public User User { get; set; }
    public int RoleId { get; set; }
    public Role Role { get; set; }
}

2. Dịch vụ xác thực và ủy quyền

AuthService xử lý xác thực và ủy quyền:

public interface IAuthService
{
    User Authenticate(string username, string password);
    bool Authorize(User user, string role);
}

public class AuthService : IAuthService
{
    private readonly AppDbContext _context;

    public AuthService(AppDbContext context)
    {
        _context = context;
    }

    public User Authenticate(string username, string password)
    {
        var user = _context.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role)
                                 .FirstOrDefault(u => u.Username == username);
        if (user == null || !VerifyPassword(user.PasswordHash, password))
        {
            return null;
        }
        return user;
    }

    public bool Authorize(User user, string role)
    {
        return user.UserRoles.Any(ur => ur.Role.Name == role);
    }

    private bool VerifyPassword(string hashedPassword, string password)
    {
        // Implement password verification logic here
        return true;
    }
}

Plugin Giao tiếp với Event Aggregator

1. Tổng hợp sự kiện

Trình tổng hợp sự kiện tạo điều kiện thuận lợi cho việc giao tiếp tách biệt giữa các plugin:

public interface IEventAggregator
{
    void Publish<TEvent>(TEvent eventToPublish);
    void Subscribe<TEvent>(Action<TEvent> eventHandler);
}

public class EventAggregator : IEventAggregator
{
    private readonly ConcurrentDictionary<Type, List<object>> _subscribers = new();

    public void Publish<TEvent>(TEvent eventToPublish)
    {
        if (_subscribers.TryGetValue(typeof(TEvent), out var handlers))
        {
            foreach (var handler in handlers.OfType<Action<TEvent>>())
            {
                handler(eventToPublish);
            }
        }
    }

    public void Subscribe<TEvent>(Action<TEvent> eventHandler)
    {
        _subscribers.AddOrUpdate(
            typeof(TEvent),
            _ => new List<object> { eventHandler },
            (_, handlers) => { handlers.Add(eventHandler); return handlers; }
        );
    }
}

Phiên bản và Tải Plugin

1. Siêu dữ liệu plugin và kiểm tra phiên bản

Để đảm bảo các plugin tương thích, chúng tôi triển khai kiểm tra phiên bản:

public interface IPluginMetadata
{
    string Name { get; }
    Version Version { get; }
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginAttribute : Attribute, IPluginMetadata
{
    public string Name { get; }
    public Version Version { get; }

    public PluginAttribute(string name, string version)
    {
        Name = name;
        Version = Version.Parse(version);
    }
}

public class PluginLoader : IPluginLoader
{
    private readonly IContainer _container;

    public PluginLoader(IContainer container)
    {
        _container = container;
    }

    public void LoadPlugins(string path)
    {
        var pluginAssemblies = Directory.GetFiles(path, "*.dll")
                                         .Select(Assembly.LoadFrom);
        foreach (var assembly in pluginAssemblies)
        {
            var pluginTypes = assembly.GetTypes()
                                      .Where(t => typeof(IPlugin).IsAssignableFrom(t) && 
                                                  t.GetCustomAttribute<PluginAttribute>() != null);
            foreach (var pluginType in pluginTypes)
            {
                var metadata = pluginType.GetCustomAttribute<PluginAttribute>();
                if (IsCompatible(metadata))
                {
                    var plugin = (IPlugin)Activator.CreateInstance(pluginType);
                    plugin.Initialize(_container);
                }
            }
        }
    }

    private bool IsCompatible(IPluginMetadata metadata)
    {
        // Implement compatibility check logic here
        return true;
    }
}

Phần kết luận

Kiến trúc được phác thảo ở trên cung cấp nền tảng vững chắc để phát triển ứng dụng WPF dạng mô-đun và có thể mở rộng. Bằng cách tích hợp mẫu MVVM, EF Core, kiến trúc plugin và Autofac, ứng dụng trở nên có khả năng bảo trì cao và có thể sử dụng trong tương lai. Việc triển khai hệ thống quản lý người dùng toàn diện, cùng với giao tiếp plugin an toàn luồng, đảm bảo khả năng sử dụng và độ tin cậy. Chiến lược quản lý phiên bản plugin được lập kế hoạch tốt giúp ứng dụng tương thích và có thể mở rộng. Cảm ơn các bạn đã theo dõi.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí