Κοινή χρήση τεχνολογίας

WPF/C#: Πώς να εφαρμόσετε την ένεση εξάρτησης στο WPF

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Πρόλογος

Αυτό το άρθρο χρησιμοποιεί το έργο WPF Gallery για να μάθει τις σχετικές έννοιες της ένεσης εξάρτησης και τον τρόπο εκτέλεσης της ένεσης εξάρτησης στο WPF.

Τι είναι η ένεση εξάρτησης

Το Dependency Injection (DI) είναι ένα μοτίβο σχεδιασμού που χρησιμοποιείται για την εφαρμογή της αρχής Inversion of Control (IoC). Ο κύριος σκοπός της ένεσης εξάρτησης είναι να μεταφέρει τη δημιουργία αντικειμένων και τη διαχείριση των εξαρτήσεων μεταξύ αντικειμένων από το εσωτερικό του αντικειμένου σε ένα εξωτερικό δοχείο ή πλαίσιο, βελτιώνοντας έτσι τη δυνατότητα συντήρησης, τη δυνατότητα δοκιμής και την ευελιξία του κώδικα.

Η βασική έννοια της ένεσης εξάρτησης

  1. βασίζομαι : Ένα αντικείμενο απαιτεί ένα άλλο αντικείμενο για να ολοκληρώσει την εργασία του, τότε το πρώτο εξαρτάται από το δεύτερο. Για παράδειγμα, μια κλάση OrderService μπορεί να βασίζεται σε μια κατηγορία ProductRepository για τη λήψη πληροφοριών προϊόντος.
  2. ένεση : Περάστε το εξαρτημένο αντικείμενο στο αντικείμενο που το χρειάζεται, αντί να αφήσετε το αντικείμενο που το χρειάζεται να δημιουργήσει το ίδιο το εξαρτημένο αντικείμενο. Η έγχυση μπορεί να επιτευχθεί μέσω κατασκευαστών, ιδιοτήτων ή παραμέτρων μεθόδου.
  3. δοχείο : Ένα πλαίσιο ή βιβλιοθήκη που διαχειρίζεται τη δημιουργία αντικειμένων και τις εξαρτήσεις. Το δοχείο είναι υπεύθυνο για τη δημιουργία αντικειμένων, την επίλυση εξαρτήσεων και την έγχυση εξαρτημένων αντικειμένων σε αντικείμενα που τα απαιτούν.

Τύποι ένεσης εξάρτησης

έγχυση κατασκευαστή: Το εξαρτημένο αντικείμενο περνά από τον κατασκευαστή της κλάσης.

public class OrderService
{
    private readonly IProductRepository _productRepository;

    public OrderService(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Ένεση ιδιοκτησίας: Τα εξαρτημένα αντικείμενα περνούν μέσα από τις δημόσιες ιδιότητες της κλάσης.

public class OrderService
{
    public IProductRepository ProductRepository { get; set; }
}
  • 1
  • 2
  • 3
  • 4

μέθοδος ένεσης: Το εξαρτημένο αντικείμενο περνά μέσα από την παράμετρο μεθόδου της κλάσης.

public class OrderService
{
    public void ProcessOrder(IProductRepository productRepository)
    {
        // 使用 productRepository 处理订单
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Γιατί είναι απαραίτητη η ένεση εξάρτησης

Το Dependency Injection (DI) είναι ένα σχέδιο σχεδίασης μέσω του οποίου η δημιουργία αντικειμένων και η διαχείριση των εξαρτήσεων μεταξύ αντικειμένων μπορεί να μεταφερθεί από το εσωτερικό του αντικειμένου σε ένα εξωτερικό δοχείο ή πλαίσιο. Υπάρχουν αρκετοί σημαντικοί λόγοι και πλεονεκτήματα για την ένεση εξάρτησης:

  1. Μειώστε τη σύζευξη : Η ένεση εξάρτησης μεταφέρει τη διαχείριση των εξαρτήσεων από το εσωτερικό του αντικειμένου στο εξωτερικό κοντέινερ, έτσι ώστε το αντικείμενο να μην χρειάζεται να γνωρίζει πώς να δημιουργεί τα αντικείμενα από τα οποία εξαρτάται, αλλά χρειάζεται μόνο να γνωρίζει τη διεπαφή του εξαρτημένου αντικειμένου. Αυτό μπορεί να μειώσει σημαντικά τη σύζευξη μεταξύ αντικειμένων και να κάνει τον κώδικα πιο αρθρωτό και ευέλικτο.
  2. Βελτιώστε τη δυνατότητα δοκιμής : Η έγχυση εξάρτησης κάνει τη δοκιμή της μονάδας ευκολότερη και πιο αποτελεσματική. Χρησιμοποιώντας εικονικά αντικείμενα ή στελέχη αντί για πραγματικά εξαρτημένα αντικείμενα, οι προγραμματιστές μπορούν να εκτελέσουν δοκιμές μονάδων χωρίς να βασίζονται στην πραγματική υλοποίηση. Αυτό βοηθά στη διασφάλιση της ανεξαρτησίας και της αξιοπιστίας του τεστ.
  3. Βελτιώστε τη συντηρησιμότητα : Εφόσον η ένεση εξάρτησης μειώνει τη σύζευξη μεταξύ αντικειμένων, ο κώδικας γίνεται πιο αρθρωτός και σαφής. Αυτό διευκολύνει την κατανόηση και τη διατήρηση του κώδικα. Όταν χρειάζεται να τροποποιήσετε ή να αντικαταστήσετε ένα εξαρτημένο αντικείμενο, χρειάζεται μόνο να τροποποιήσετε τις πληροφορίες διαμόρφωσης ή εγγραφής, χωρίς να τροποποιήσετε τον κωδικό που χρησιμοποιεί το αντικείμενο.
  4. ΒΕΛΤΙΩΣΕ την ευελιξια : Η έγχυση εξάρτησης κάνει το σύστημα πιο ευέλικτο και μπορεί εύκολα να αντικαταστήσει εξαρτημένα αντικείμενα για να επιτύχει διαφορετικές λειτουργίες ή συμπεριφορές. Για παράδειγμα, διαφορετικές υλοποιήσεις επιπέδου πρόσβασης στη βάση δεδομένων μπορούν να αλλάξουν μέσω αρχείων διαμόρφωσης ή κώδικα χωρίς τροποποίηση του επιχειρηματικού λογικού κώδικα.
  5. Προωθήστε τον διαχωρισμό των ανησυχιών : Η ένεση εξάρτησης βοηθά στην επίτευξη του διαχωρισμού των ανησυχιών (Separation of Concerns), έτσι ώστε κάθε αντικείμενο να χρειάζεται μόνο να επικεντρωθεί στις δικές του ευθύνες, χωρίς να ενδιαφέρεται για το πώς θα δημιουργήσει και θα αποκτήσει τα αντικείμενα από τα οποία εξαρτάται. Αυτό βοηθά στη βελτίωση της σαφήνειας και της συντηρησιμότητας του κώδικα.
  6. Υποστηρίξτε μοτίβα σχεδίασης και βέλτιστες πρακτικές : Η έγχυση εξάρτησης είναι η βάση πολλών μοτίβων σχεδίασης και βέλτιστων πρακτικών, όπως η αντιστροφή του ελέγχου (IoC), το μοτίβο εντοπισμού υπηρεσίας κ.λπ. Χρησιμοποιώντας την ένεση εξάρτησης, οι προγραμματιστές μπορούν να εφαρμόσουν πιο εύκολα αυτά τα μοτίβα και πρακτικές, βελτιώνοντας έτσι την ποιότητα και την επεκτασιμότητα του κώδικα.

Πώς να εφαρμόσετε την ένεση εξάρτησης

Αυτό το άρθρο χρησιμοποιεί το έργο WPF Gallery για να μάθετε πώς να χρησιμοποιείτε την ένεση εξάρτησης στη διεύθυνση κώδικα WPF.

https://github.com/microsoft/WPF-Samples/blob/main/SampleApplications/WPFGallery

Για την εφαρμογή της ένεσης εξάρτησης σε αυτό το έργο, χρησιμοποιούνται αυτά τα δύο πακέτα:

εικόνα-20240711100435001

Πρώτα κοιτάξτε τα περιεχόμενα του App.xaml.cs:

public partial class App : Application
{

    private static readonly IHost _host = Host.CreateDefaultBuilder()
        .ConfigureServices((context, services) =>
        {
            services.AddSingleton<INavigationService, NavigationService>();
            services.AddSingleton<MainWindow>();
            services.AddSingleton<MainWindowViewModel>();
            
            services.AddTransient<DashboardPage>();
            services.AddTransient<DashboardPageViewModel>();

            services.AddTransient<ButtonPage>();
            services.AddTransient<ButtonPageViewModel>();
            services.AddTransient<CheckBoxPage>();
            services.AddTransient<CheckBoxPageViewModel>();
            services.AddTransient<ComboBoxPage>();
            services.AddTransient<ComboBoxPageViewModel>();
            services.AddTransient<RadioButtonPage>();
            services.AddTransient<RadioButtonPageViewModel>();
            services.AddTransient<SliderPage>();
            services.AddTransient<SliderPageViewModel>();
            services.AddTransient<CalendarPage>();
            services.AddTransient<CalendarPageViewModel>();
            services.AddTransient<DatePickerPage>();
            services.AddTransient<DatePickerPageViewModel>();
            services.AddTransient<TabControlPage>();
            services.AddTransient<TabControlPageViewModel>();
            services.AddTransient<ProgressBarPage>();
            services.AddTransient<ProgressBarPageViewModel>();
            services.AddTransient<MenuPage>();
            services.AddTransient<MenuPageViewModel>();
            services.AddTransient<ToolTipPage>();
            services.AddTransient<ToolTipPageViewModel>();
            services.AddTransient<CanvasPage>();
            services.AddTransient<CanvasPageViewModel>();
            services.AddTransient<ExpanderPage>();
            services.AddTransient<ExpanderPageViewModel>();
            services.AddTransient<ImagePage>();
            services.AddTransient<ImagePageViewModel>();
            services.AddTransient<DataGridPage>();
            services.AddTransient<DataGridPageViewModel>();
            services.AddTransient<ListBoxPage>();
            services.AddTransient<ListBoxPageViewModel>();
            services.AddTransient<ListViewPage>();
            services.AddTransient<ListViewPageViewModel>();
            services.AddTransient<TreeViewPage>();
            services.AddTransient<TreeViewPageViewModel>();
            services.AddTransient<LabelPage>();
            services.AddTransient<LabelPageViewModel>();
            services.AddTransient<TextBoxPage>();
            services.AddTransient<TextBoxPageViewModel>();
            services.AddTransient<TextBlockPage>();
            services.AddTransient<TextBlockPageViewModel>();
            services.AddTransient<RichTextEditPage>();
            services.AddTransient<RichTextEditPageViewModel>();
            services.AddTransient<PasswordBoxPage>();
            services.AddTransient<PasswordBoxPageViewModel>();
            services.AddTransient<ColorsPage>();
            services.AddTransient<ColorsPageViewModel>();

            services.AddTransient<LayoutPage>();
            services.AddTransient<LayoutPageViewModel>();
            services.AddTransient<AllSamplesPage>();
            services.AddTransient<AllSamplesPageViewModel>();
            services.AddTransient<BasicInputPage>();
            services.AddTransient<BasicInputPageViewModel>();
            services.AddTransient<CollectionsPage>();
            services.AddTransient<CollectionsPageViewModel>();
            services.AddTransient<MediaPage>();
            services.AddTransient<MediaPageViewModel>();
            services.AddTransient<NavigationPage>();
            services.AddTransient<NavigationPageViewModel>();
            services.AddTransient<TextPage>();
            services.AddTransient<TextPageViewModel>();
            services.AddTransient<DateAndTimePage>();
            services.AddTransient<DateAndTimePageViewModel>();
            services.AddTransient<StatusAndInfoPage>();
            services.AddTransient<StatusAndInfoPageViewModel>();
            services.AddTransient<SamplesPage>();
            services.AddTransient<SamplesPageViewModel>();
            services.AddTransient<DesignGuidancePage>();
            services.AddTransient<DesignGuidancePageViewModel>();

            services.AddTransient<UserDashboardPage>();
            services.AddTransient<UserDashboardPageViewModel>();

            services.AddTransient<TypographyPage>();
            services.AddTransient<TypographyPageViewModel>();

            services.AddSingleton<IconsPage>();
            services.AddSingleton<IconsPageViewModel>();

            services.AddSingleton<SettingsPage>();
            services.AddSingleton<SettingsPageViewModel>();

            services.AddSingleton<AboutPage>();
            services.AddSingleton<AboutPageViewModel>();
        }).Build();


    [STAThread]
    public static void Main()
    {
        _host.Start();

        App app = new();
        app.InitializeComponent();
        app.MainWindow = _host.Services.GetRequiredService<MainWindow>();
        app.MainWindow.Visibility = Visibility.Visible;
        app.Run();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

εικόνα-20240711083011393

Τι είναι το IHost;

Σε C#,IHost είναι μια διεπαφή που χρησιμοποιείται στο .NET για τη δημιουργία και τη διαμόρφωση εφαρμογώνHostέννοια αφηρημένη.IHost Μια διεπαφή ορίζει μια συλλογή υπηρεσιών και στοιχείων που απαιτούνται για την εκκίνηση, την εκτέλεση και τη διαχείριση μιας εφαρμογής. Συνήθως χρησιμοποιείται με εφαρμογές ASP.NET Core, αλλά λειτουργεί και με άλλους τύπους εφαρμογών .NET, όπως εφαρμογές κονσόλας ή προγράμματα WPF.

εικόνα-20240711082817268

IHostΗ διεπαφή αποτελείται απόHostBuilderΥλοποίηση κλάσης, η οποία παρέχει τη δημιουργία και τη διαμόρφωση τουIHostμέθοδοι παραδείγματος.HostBuilderΣας επιτρέπει να προσθέσετε διάφορες υπηρεσίες, όπως καταγραφή, διαμόρφωση, δοχεία έγχυσης εξάρτησης κ.λπ., και να διαμορφώσετε τη συμπεριφορά εκκίνησης και διακοπής της εφαρμογής σας.

εικόνα-20240711083048854

εικόνα-20240711083156306

Παρέχει πρακτικές μεθόδους για τη δημιουργία παρουσιών Microsoft.Extensions.Hosting.IHostBuilder με προρυθμισμένες προεπιλογές.

εικόνα-20240711083713204

Επιστρέφει ένα IHostBuilder.

εικόνα-20240711084145756

εικόνα-20240711084211035

Προσθέστε υπηρεσίες στο κοντέινερ. Αυτή η λειτουργία μπορεί να κληθεί πολλές φορές και τα αποτελέσματά της είναι αθροιστικά.

Η έννοια της παραμέτρου configureDelegate είναι να ρυθμίσετε τις παραμέτρους του πληρεξούσιου του Microsoft.Extensions.DependencyInjection.IServiceCollection.
Αυτή η συλλογή θα χρησιμοποιηθεί για την κατασκευή System.IServiceProvider.

Ο πληρεξούσιος απαιτεί δύο τύπους παραμέτρων: HostBuilderContext και IServiceCollection, οι οποίοι δεν έχουν τιμή επιστροφής.

εικόνα-20240711084853971

Μια έκφραση λάμδα που ικανοποιεί τον τύπο πληρεξουσίου μεταβιβάζεται εδώ.

Σε C#,() => {} Είναι μια σύνταξη για εκφράσεις λάμδα. Οι εκφράσεις λάμδα είναι ένα ελαφρύ περιτύλιγμα πληρεξουσίων που σας επιτρέπει να ορίσετε μια ανώνυμη μέθοδο και να τη μεταβιβάσετε ως παράμετρο σε μια μέθοδο που υποστηρίζει εκπροσώπους ή δέντρα εκφράσεων.

Οι εκφράσεις λάμδα παρέχουν έναν συνοπτικό τρόπο ορισμού μεθόδων και είναι χρήσιμες ειδικά όταν χρειάζεται να μεταβιβάσετε μεθόδους ως παραμέτρους σε άλλες μεθόδους.

εικόνα-20240711085344696

Κατά την προσθήκη υπηρεσιών, υπάρχουν δύο κύκλοι ζωής εδώ, εκτός από το AddSingleton και το AddTransient, υπάρχει και το AddScoped.

Αυτές οι μέθοδοι ορίζουν τον κύκλο ζωής της υπηρεσίας, δηλαδή πώς δημιουργούνται και διαχειρίζονται οι παρουσίες υπηρεσίας στην εφαρμογή.

Προσθήκη Singleton

  • κύκλος ζωής:Μοναδικό χαρτί
  • έννοια : Μόνο μία παρουσία υπηρεσίας δημιουργείται κατά τη διάρκεια ολόκληρου του κύκλου ζωής της εφαρμογής. Ανεξάρτητα από το πόσες φορές το ζητήσετε από το κοντέινερ, η ίδια παρουσία θα επιστραφεί.
  • Εφαρμόσιμη σκηνή: Κατάλληλο για υπηρεσίες χωρίς ιθαγένεια ή πόρους που μοιράζονται σε ολόκληρη την εφαρμογή, όπως διαμορφώσεις, καταγραφείς κ.λπ.

Προσθήκη Μεταβατικού

  • κύκλος ζωής: Παροδικό
  • έννοια: Κάθε φορά που ζητείται μια υπηρεσία από το κοντέινερ, δημιουργείται μια νέα παρουσία.
  • Εφαρμόσιμη σκηνή: Κατάλληλο για κρατικές υπηρεσίες ή σενάρια που απαιτούν μια νέα παρουσία για κάθε αίτημα, όπως σελίδες, προβολές μοντέλων κ.λπ.

AddScoped

  • κύκλος ζωής: Πεδίο εφαρμογής
  • έννοια : Σε κάθε εύρος, οι περιπτώσεις υπηρεσιών είναι μοναδικές. Τα πεδία συσχετίζονται συχνά με τον κύκλο ζωής ενός αιτήματος, για παράδειγμα σε εφαρμογές web δημιουργείται ένα νέο εύρος με κάθε αίτημα HTTP.
  • Εφαρμόσιμη σκηνή: Κατάλληλο για υπηρεσίες που χρειάζονται κοινή χρήση παρουσιών εντός του εύρους αιτήματος, όπως περιβάλλοντα βάσης δεδομένων.

χρησιμοποιήστε αυτές τις υπηρεσίες

Στην Κύρια συνάρτηση:

εικόνα-20240711095100016

ξεκίνα_host,πέρασμα_host.Services.GetRequiredService<MainWindow>();Λάβετε την παρουσία του MainWindow.

Λαμβάνοντας ως παράδειγμα την κλάση MainWindow, προβάλετε τον κατασκευαστή του MainWindow στο MainWindow.xaml.cs:

public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
{
    _serviceProvider = serviceProvider;
    ViewModel = viewModel;
    DataContext = this;
    InitializeComponent();

    Toggle_TitleButtonVisibility();

    _navigationService = navigationService;
    _navigationService.Navigating += OnNavigating;
    _navigationService.SetFrame(this.RootContentFrame);
    _navigationService.Navigate(typeof(DashboardPage));

    WindowChrome.SetWindowChrome(
        this,
        new WindowChrome
        {
            CaptionHeight = 50,
            CornerRadius = default,
            GlassFrameThickness = new Thickness(-1),
            ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4),
            UseAeroCaptionButtons = true
        }
    );

    this.StateChanged += MainWindow_StateChanged;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Μετά την κατάργηση του περιεχομένου που δεν σχετίζεται με αυτό το θέμα, έχει ως εξής:

public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
{
    _serviceProvider = serviceProvider;
    ViewModel = viewModel; 
    _navigationService = navigationService;  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Παρατηρήσατε ότι δεν χρειάζεται πλέον να ανεβάσετε αυτά τα αντικείμενα. Η διαχείριση αυτών των αντικειμένων γίνεται από το κοντέινερ έγχυσης εξάρτησης.

Εάν δεν χρησιμοποιείται ένεση εξάρτησης, μπορεί να μοιάζει με αυτό:

public MainWindow()
{
    _serviceProvider = new IServiceProvider();
    ViewModel = new MainWindowViewModel(); 
    _navigationService = new INavigationService();  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Συνοψίζω

Αυτό το άρθρο εισάγει πρώτα την έννοια της ένεσης εξάρτησης, στη συνέχεια εξηγεί γιατί απαιτείται ένεση εξάρτησης και, τέλος, μαθαίνει πώς να χρησιμοποιείτε την ένεση εξάρτησης στο WPF μέσω του έργου WPF Gallery.

αναφέρομαι σε

1、[WPF-Samples/Sample Applications/WPFGallery στο κύριο · microsoft/WPF-Samples (github.com)](https://github.com/microsoft/WPF-Samples/tree/main/Sample Applications/WPFGallery)