少有人走的路

勇哥的工业自动化技术网站

待办事项小程序(3) Autofac改造

待办事项小程序(3) Autofac改造


Autofac是一个第三方的di框架(依赖注入框架),这次用它来改造这个小程序。


在这次改造时,用到Autofac下面的知识点:

  • 依赖注入的核心实现:通过 Autofac 以「构造函数注入」的方式,

    自动为 ViewModel 注入依赖服务,替代手动 new,降低代码耦合。

  • Autofac 核心概念区分:「注册模块(Module)」负责拆分注册逻辑,

    「服务组件(Service)」负责实现业务逻辑,二者职责分离。

  • Autofac 生命周期控制:掌握瞬时(每次解析新建实例、状态独立)和

    单例(全局唯一实例、状态共享)的特性与适用场景。

  • Autofac 核心工作流程:注册 → 构建容器 → 解析(自动递归注入依赖),

    完成对象的创建与依赖装配。



image.png


TodoServiceModule.cs


using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.Common.Services;

namespace WpfApp4.Common.AutofacModules
{
    /// <summary>
    /// 待办服务注册模块(配合:模块化注册、生命周期配置(瞬时/单例))
    /// </summary>
    public class TodoServiceModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // 注册瞬时服务:配合验证「瞬时生命周期」,每次解析创建新实例
            builder.RegisterType<TodoDataService>()
                   .As<ITodoDataService>()
                   .InstancePerDependency(); // 可省略,默认即为瞬时

            // 注册单例服务:配合验证「单例生命周期」,全局只创建一个实例
            builder.RegisterType<UserPreferenceService>()
                   .As<IUserPreferenceService>()
                   .SingleInstance();
        }
    }
}


TodoViewModelModule.cs


using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.ViewModel;

namespace WpfApp4.Common.AutofacModules
{
    /// <summary>
    /// 待办 ViewModel 注册模块(配合:模块化注册、构造函数注入自动解析)
    /// </summary>
    public class TodoViewModelModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // 注册 ViewModel:Autofac 自动解析其构造函数中的服务依赖,配合验证构造函数注入
            builder.RegisterType<TodoViewModel>()
                   .InstancePerDependency();
        }
    }
}


ITodoDataService.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.Model;

namespace WpfApp4.Common.Services
{
    /// <summary>
    /// 待办事项数据操作服务接口(配合:构造函数注入、瞬时生命周期验证)
    /// </summary>
    public interface ITodoDataService
    {
        ObservableCollection<TodoItem> GetTodoItems();
        void AddTodoItem(string content);
        void DeleteTodoItem(TodoItem? todo);
        void ClearCompletedTodoItems();
        int GetUnfinishedCount();
    }
}


IUserPreferenceService.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp4.Common.Services
{
    /// <summary>
    /// 用户偏好配置服务接口(配合:构造函数注入、单例生命周期验证)
    /// </summary>
    public interface IUserPreferenceService
    {
        string GetCurrentTheme(); // 配合单例:共享主题状态
        void SetCurrentTheme(string theme); // 配合单例:修改共享状态
        string GetInstanceGuid(); // 配合单例:验证实例唯一性
    }
}


TodoDataService.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.Model;

namespace WpfApp4.Common.Services
{
    /// <summary>
    /// 待办事项数据操作服务实现(配合:瞬时生命周期验证,每个实例维护独立待办集合)
    /// </summary>
    public class TodoDataService : ITodoDataService
    {
        // 私有独立集合:每个瞬时实例都有自己的集合,配合验证瞬时生命周期的「状态独立」
        private readonly ObservableCollection<TodoItem> _todoItems = new();

        // 所有方法都操作当前实例的私有集合,保证实例独立性
        public ObservableCollection<TodoItem> GetTodoItems() => _todoItems;
        public void AddTodoItem(string content) => _todoItems.Add(new TodoItem { Content = content.Trim(), IsCompleted = false });
        public void DeleteTodoItem(TodoItem? todo) { if (todo != null && _todoItems.Contains(todo)) _todoItems.Remove(todo); }
        public void ClearCompletedTodoItems()
        {
            var completedItems = _todoItems.Where(t => t.IsCompleted).ToList();
            foreach (var item in completedItems) _todoItems.Remove(item);
        }
        public int GetUnfinishedCount() => _todoItems.Count(t => !t.IsCompleted);
    }
}



UserPreferenceService.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp4.Common.Services
{
    /// <summary>
    /// 用户偏好配置服务实现(配合:单例生命周期验证,全局共享一个实例和状态)
    /// </summary>
    public class UserPreferenceService : IUserPreferenceService
    {
        private readonly string _instanceGuid; // 实例唯一标识:配合验证单例的「实例唯一性」
        private string _currentTheme = "Light"; // 共享主题状态:配合验证单例的「状态共享性」

        public UserPreferenceService()
        {
            // 构造时生成唯一 Guid,后续界面展示,验证所有解析请求都获取同一个实例
            _instanceGuid = Guid.NewGuid().ToString("N").Substring(0, 8);
        }

        public string GetCurrentTheme() => _currentTheme;
        public void SetCurrentTheme(string theme) { if (!string.IsNullOrWhiteSpace(theme)) _currentTheme = theme; }
        public string GetInstanceGuid() => _instanceGuid;
    }

}


TodoItem.cs


using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp4.Model
{

    // 改写后:使用 ObservableObject(框架内置)+ [ObservableProperty],消除所有手搓的 INotifyPropertyChanged 样板代码
    public partial class TodoItem : ObservableObject
    {
        // 框架自动生成:私有字段 _content + 公共属性 Content + PropertyChanged 通知
        [ObservableProperty]
        private string _content = string.Empty;

        // 框架自动生成:私有字段 _isCompleted + 公共属性 IsCompleted + PropertyChanged 通知
        [ObservableProperty]
        private bool _isCompleted;
    }


    //// 旧的手搓代码
    //// 待办项实体,实现INotifyPropertyChanged(状态变更通知界面)
    //public class TodoItem : INotifyPropertyChanged
    //{
    //    private string _content;
    //    private bool _isCompleted;

    //    public string Content
    //    {
    //        get => _content;
    //        set { _content = value; OnPropertyChanged(); }
    //    }

    //    public bool IsCompleted
    //    {
    //        get => _isCompleted;
    //        set { _isCompleted = value; OnPropertyChanged(); }
    //    }

    //    public event PropertyChangedEventHandler? PropertyChanged;
    //    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    //    {
    //        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    //    }
    //}
}



MainWindow.xaml


<Window x:Class="WpfApp4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp4"
        xmlns:vm="clr-namespace:WpfApp4.ViewModel"   
        mc:Ignorable="d"
           Title="待办事项列表" Height="450" Width="800">
        <!-- 新增xmlns:vm这一行,指向 ViewModel 命名空间 -->



    <!--移除硬编码 DataContext,配合 App.xaml.cs 中的手动注入
    <Window.DataContext>
        <vm:TodoViewModel />
    </Window.DataContext>-->

    <!-- 布局代码(Grid) -->
    <Grid Margin="20">
        <!-- 输入区:第0行 -->
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <!-- 新增:配合单例生命周期验证,展示用户偏好信息 -->
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- 原有输入区:配合验证构造函数注入后的基础功能 -->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,10,0">
            <TextBox x:Name="txtNewTodo" Width="300" Text="{Binding NewTodoText, UpdateSourceTrigger=PropertyChanged}"/>
            <Button Content="添加" Command="{Binding AddTodoCommand}"/>
        </StackPanel>

        <!-- 原有待办列表区:配合验证构造函数注入后的基础功能 -->
        <ListView Grid.Row="1" ItemsSource="{Binding TodoItems}" Margin="0,10,0,10">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <!-- CheckBox只负责勾选状态,文字用独立TextBlock控制样式 -->
                        <CheckBox IsChecked="{Binding IsCompleted}" Width="20" VerticalAlignment="Center"/>

                        <!-- 待办文字:通过DataTrigger控制删除线 -->
                        <TextBlock Text="{Binding Content}" 
                       Width="250" 
                       VerticalAlignment="Center"
                       Margin="5,0,5,0"> <!-- 左右留5px间距 -->
                            <TextBlock.Style>
                                <Style TargetType="TextBlock">
                                    <!-- 默认样式:无文字装饰 -->
                                    <Setter Property="TextDecorations" Value="None"/>

                                    <!-- 数据触发器:IsCompleted=true时显示删除线 -->
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding IsCompleted}" Value="True">
                                            <Setter Property="TextDecorations" Value="Strikethrough"/>
                                            <!-- 可选:完成后文字变灰色,视觉更明显 -->
                                            <Setter Property="Foreground" Value="Gray"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TextBlock.Style>
                        </TextBlock>

                        <!-- 删除按钮 -->
                        <Button Content="×" 
                    Command="{Binding DataContext.DeleteTodoCommand, RelativeSource={RelativeSource AncestorType=ListView}}"
                    CommandParameter="{Binding}"
                    VerticalAlignment="Center"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <!-- 原有操作区:配合验证构造函数注入后的基础功能 -->
        <StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,10,0,10">
            <TextBlock Text="{Binding UnfinishedCount, StringFormat='待办数量:{0}'}"/>
            <Button Content="清空已完成" Command="{Binding ClearCompletedCommand}"/>
        </StackPanel>

        <!-- 新增:用户偏好展示区(配合单例生命周期验证) -->
        <StackPanel Grid.Row="3" Orientation="Horizontal" Background="#F5F5F5" Margin="0,10,0,10">
            <TextBlock Text="当前主题:" VerticalAlignment="Center"/>
            <TextBlock Text="{Binding CurrentTheme}" FontWeight="Bold" Margin="5,0,20,0" VerticalAlignment="Center"/>
            <Button Content="切换主题" Command="{Binding ToggleThemeCommand}" Margin="0,0,20,0"/>
            <TextBlock Text="偏好服务实例ID:" VerticalAlignment="Center"/>
            <TextBlock Text="{Binding PreferenceServiceGuid}" Foreground="Blue" Margin="5,0,0,0" VerticalAlignment="Center"/>
        </StackPanel>
        
    </Grid>
</Window>


TodoViewModel.cs


using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using WpfApp4.Common.Services;
using WpfApp4.Model;

namespace WpfApp4.ViewModel
{

    public partial class TodoViewModel : ObservableObject
    {
        #region 配合构造函数注入:新增私有服务字段(接收 Autofac 注入的实例)
        private readonly ITodoDataService _todoDataService;
        private readonly IUserPreferenceService _userPreferenceService;
        #endregion

        #region 改造原有属性:配合构造函数注入,依赖服务获取数据
        [ObservableProperty]
        private string _newTodoText = string.Empty;

        // 待办列表:从注入的瞬时服务中获取,配合验证瞬时生命周期的独立状态
        public ObservableCollection<TodoItem> TodoItems => _todoDataService.GetTodoItems();

        // 未完成数量:调用注入的瞬时服务方法,配合验证构造函数注入成功
        public int UnfinishedCount => _todoDataService.GetUnfinishedCount();
        #endregion

        #region 新增属性:配合单例生命周期验证,展示单例服务的状态和实例ID
        public string CurrentTheme => _userPreferenceService.GetCurrentTheme(); // 共享主题状态
        public string PreferenceServiceGuid => _userPreferenceService.GetInstanceGuid(); // 唯一实例ID
        #endregion

        #region 配合构造函数注入:新增带参构造函数(Autofac 注入入口)
        public TodoViewModel(ITodoDataService todoDataService, IUserPreferenceService userPreferenceService)
        {
            _todoDataService = todoDataService;
            _userPreferenceService = userPreferenceService;

            // 保留原有集合变更监听,确保绑定正常
            TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(UnfinishedCount));
            TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(CurrentTheme));
        }
        #endregion

        #region 改造原有命令:配合构造函数注入,调用服务方法替代硬编码
        partial void OnNewTodoTextChanged(string value) => AddTodoCommand.NotifyCanExecuteChanged();

        [RelayCommand(CanExecute = nameof(CanAddTodo))]
        private void AddTodo()
        {
            _todoDataService.AddTodoItem(NewTodoText.Trim()); // 调用注入服务,配合验证注入成功
            NewTodoText = string.Empty;
        }

        private bool CanAddTodo() => !string.IsNullOrWhiteSpace(NewTodoText);

        [RelayCommand]
        private void DeleteTodo(TodoItem? todo) => _todoDataService.DeleteTodoItem(todo); // 调用注入服务

        [RelayCommand]
        private void ClearCompleted() => _todoDataService.ClearCompletedTodoItems(); // 调用注入服务
        #endregion

        #region 新增命令:配合单例生命周期验证,修改单例服务的共享状态
        [RelayCommand]
        private void ToggleTheme()
        {
            var currentTheme = _userPreferenceService.GetCurrentTheme();
            _userPreferenceService.SetCurrentTheme(currentTheme == "Light" ? "Dark" : "Light");

            // 通知界面更新,展示单例状态变更结果
            OnPropertyChanged(nameof(CurrentTheme));
            OnPropertyChanged(nameof(PreferenceServiceGuid));
        }
        #endregion
    }


    /*
     * 
    // 改写后:继承 ObservableObject(框架内置,替代手搓的 ViewModelBase)
    public partial class TodoViewModel : ObservableObject
    {
        // 改写后:使用 [ObservableProperty] 特性,编译时自动生成以下内容:
        // 1. 私有字段 _newTodoText
        // 2. 公共属性 NewTodoText(包含 get/set,且 set 时自动触发 PropertyChanged 通知)
        // 3. 无需手动调用 OnPropertyChanged()
        [ObservableProperty]
        private string _newTodoText = string.Empty;

        // 当 NewTodoText 变化时,通知 AddTodoCommand 刷新 CanExecute
        partial void OnNewTodoTextChanged(string value)
        {
            AddTodoCommand.NotifyCanExecuteChanged();
        }

        // 待办列表(核心:ObservableCollection,保持不变,框架不替代集合本身)
        public ObservableCollection<TodoItem> TodoItems { get; } = new();

        // 未完成待办数量(计算属性,保持逻辑不变,仅触发通知的方式简化)
        public int UnfinishedCount => TodoItems.Count(t => !t.IsCompleted);

        // 改写后:使用 [RelayCommand] 特性,编译时自动生成以下内容:
        // 1. 公共 ICommand 属性 AddTodoCommand
        // 2. 无需手动初始化 RelayCommand,框架自动绑定对应的执行方法
        // 3. 支持配套的 CanAddTodo 方法(命名规范:命令方法名 + CanExecute),自动作为 CanExecute 逻辑
        [RelayCommand(CanExecute = nameof(CanAddTodo))]
        private void AddTodo()
        {
            var newTodo = new TodoItem { Content = NewTodoText.Trim(), IsCompleted = false };
            TodoItems.Add(newTodo);
            NewTodoText = string.Empty; // 清空输入框(赋值时,框架自动触发 PropertyChanged)
        }

        // AddTodo 命令的 CanExecute 逻辑(命名规范:命令方法名 + CanExecute)
        private bool CanAddTodo()
        {
            return !string.IsNullOrWhiteSpace(NewTodoText);
        }

        // 改写后:泛型命令,[RelayCommand] 自动支持参数类型,无需手动写 RelayCommand<T>
        [RelayCommand]
        private void DeleteTodo(TodoItem? todo)
        {
            if (todo != null && TodoItems.Contains(todo))
            {
                TodoItems.Remove(todo);
            }
        }

        // 改写后:无参数命令,框架自动生成 ClearCompletedCommand
        [RelayCommand]
        private void ClearCompleted()
        {
            var completedItems = TodoItems.Where(t => t.IsCompleted).ToList();
            foreach (var item in completedItems)
            {
                TodoItems.Remove(item);
            }
        }

        public TodoViewModel()
        {
            // 监听 TodoItems 集合变更,更新 UnfinishedCount
            // 仅需手动触发计算属性的通知,框架无额外简化(集合变更本身是 ObservableCollection 的职责)
            TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(UnfinishedCount));
        }
    }
    */


    //下面是手搓代码
    //public class TodoViewModel : ViewModelBase // 复用之前的ViewModelBase
    //{
    //    // 绑定输入框的待办内容
    //    private string _newTodoText;
    //    public string NewTodoText
    //    {
    //        get => _newTodoText;
    //        set { _newTodoText = value; OnPropertyChanged(); }
    //    }

    //    // 待办列表(核心:ObservableCollection)
    //    public ObservableCollection<TodoItem> TodoItems { get; } = new();

    //    // 未完成待办数量(计算属性,实时更新)
    //    public int UnfinishedCount
    //    {
    //        get => TodoItems.Count(t => !t.IsCompleted);
    //    }

    //    // 命令:添加待办
    //    public ICommand AddTodoCommand { get; }
    //    // 命令:删除待办
    //    public ICommand DeleteTodoCommand { get; }
    //    // 命令:清空已完成
    //    public ICommand ClearCompletedCommand { get; }

    //    public TodoViewModel()
    //    {
    //        // 初始化命令
    //        AddTodoCommand = new RelayCommand(AddTodo, CanAddTodo);
    //        DeleteTodoCommand = new RelayCommand<TodoItem>(DeleteTodo);
    //        ClearCompletedCommand = new RelayCommand(ClearCompleted);

    //        // 监听TodoItems集合变更,更新UnfinishedCount
    //        TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(UnfinishedCount));
    //        // 监听每个TodoItem的IsCompleted变更,更新UnfinishedCount
    //        // (需额外处理:新增TodoItem时订阅其PropertyChanged事件)
    //    }

    //    // AddTodoCommand的执行逻辑
    //    private void AddTodo(object? parameter)
    //    {
    //        var newTodo = new TodoItem { Content = NewTodoText.Trim(), IsCompleted = false };
    //        TodoItems.Add(newTodo);
    //        NewTodoText = string.Empty; // 清空输入框
    //    }

    //    // AddTodoCommand的CanExecute逻辑(输入为空时禁用)
    //    private bool CanAddTodo(object? parameter)
    //    {
    //        return !string.IsNullOrWhiteSpace(NewTodoText);
    //    }

    //    // DeleteTodoCommand的执行逻辑
    //    private void DeleteTodo(TodoItem? todo)
    //    {
    //        if (todo != null && TodoItems.Contains(todo))
    //        {
    //            TodoItems.Remove(todo);
    //        }
    //    }

    //    // ClearCompletedCommand的执行逻辑
    //    private void ClearCompleted(object? parameter)
    //    {
    //        var completedItems = TodoItems.Where(t => t.IsCompleted).ToList();
    //        foreach (var item in completedItems)
    //        {
    //            TodoItems.Remove(item);
    //        }
    //    }
    //}
}


App.xaml


<Application x:Class="WpfApp4.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp4">
    <!--StartupUri="View/MainWindow.xaml">-->
    <Application.Resources>
         
    </Application.Resources>
</Application>


App.xaml.cs


using Autofac;
using System.Configuration;
using System.Data;
using System.Windows;
using WpfApp4.Common.AutofacModules;
using WpfApp4.ViewModel;

namespace WpfApp4
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        // 配合容器资源管理:保存全局根容器,退出时释放
        private static IContainer? _rootContainer;

        /// <summary>
        /// 改造:重写启动方法,配合验证 Autofac 容器构建、依赖解析特性
        /// </summary>
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            // 步骤 1:构建容器构建器,配合容器构建
            var containerBuilder = new ContainerBuilder();

            // 步骤 2:加载所有模块,配合验证模块化注册特性
            containerBuilder.RegisterModule<TodoServiceModule>();
            containerBuilder.RegisterModule<TodoViewModelModule>();

            // 步骤 3:构建根容器,配合验证容器构建特性
            _rootContainer = containerBuilder.Build();

            // 步骤 4:开启作用域,解析 ViewModel,配合验证依赖解析和构造函数注入
            using (var scope = _rootContainer.BeginLifetimeScope())
            {
                var todoViewModel = scope.Resolve<TodoViewModel>(); // Autofac 自动注入依赖

                // 步骤 5:手动创建窗口,赋值 DataContext,替换原硬编码
                var mainWindow = new MainWindow { DataContext = todoViewModel };
                mainWindow.Show();
            }

        }

        /// <summary>
        /// 改造:重写退出方法,配合验证容器资源管理特性
        /// </summary>
        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
            _rootContainer?.Dispose(); // 释放容器,避免内存泄漏
        }
    }

}


(一)autofac依赖注入的过程解析


先明确两个核心概念(先分清「注册」和「解析」)

在 Autofac 中,注入的前提是「所有相关组件都已注册」,

注入的过程是「容器解析目标组件时,自动查找并注入其依赖」,二者是先后关系。

注册(Register):相当于给 Autofac 容器「提交产品说明书」,告诉容器:

有一个组件 TodoViewModel(产品);

有一个组件 TodoDataService,它对应 ITodoDataService 接口(产品 + 产品编号);

有一个组件 UserPreferenceService,它对应 IUserPreferenceService 接口(另一个产品 + 编号);

每个组件的生命周期是什么(生产规则)。


这里 TodoServiceModule(注册两个服务)和 TodoViewModelModule(注册 TodoViewModel),

就是在做这件事。


解析 / 注入(Resolve/Inject):相当于给 Autofac 容器「下订单」,

告诉容器:「我需要一个 TodoViewModel 实例」,此时容器会根据「产品说明书」,

自动完成所有依赖的查找和实例化,最终给你一个完整的、依赖齐全的 TodoViewModel 实例,

这个过程中,构造函数的参数会被自动填充,这就是注入。


完整注入过程拆解(对应这里的代码,一步一步来)

我们以App.xaml.cs 中的这行代码为起点,这是「下订单」的开始:

var todoViewModel = scope.Resolve<TodoViewModel>();


从这行代码开始,Autofac 容器开始工作,整个注入过程分为 5 步,

全程自动完成,无需你手动写任何赋值代码。


步骤 1:容器接收解析请求,查找 TodoViewModel 的注册信息

容器收到「需要一个 TodoViewModel 实例」的请求;

容器去自己的「注册列表」中查找,发现 TodoViewModel 已经在 TodoViewModelModule 中注册了:


// TodoViewModelModule 中的注册代码(给容器的「产品说明书」)
builder.RegisterType<TodoViewModel>().InstancePerDependency();


容器获取到 TodoViewModel 的注册信息:「实例类型是 TodoViewModel,

生命周期是瞬时(每次解析新建实例)」。


步骤 2:容器反射检查 TodoViewModel 的构造函数,提取依赖项

容器通过「反射」(C# 自带的能力,可读取类的结构信息),

找到 TodoViewModel 的公开构造函数:


public TodoViewModel(ITodoDataService todoDataService, IUserPreferenceService userPreferenceService)


容器分析构造函数的参数,提取出两个「依赖项」(也就是需要注入的接口):

第一个参数:ITodoDataService(接口类型);

第二个参数:IUserPreferenceService(接口类型);

容器明白:「要创建 TodoViewModel 实例,必须先拿到这两个接口对应的具体实例,否则无法调用构造函数」。


步骤 3:容器递归解析第一个依赖项 —— ITodoDataService

容器先处理第一个依赖:「我需要一个 ITodoDataService 实例」;

容器再次去「注册列表」中查找,发现 ITodoDataService 已经在 TodoServiceModule 中注册了,

对应的实现类是 TodoDataService:


// TodoServiceModule 中的注册代码(产品说明书)
builder.RegisterType<TodoDataService>().As<ITodoDataService>().InstancePerDependency();


容器根据注册信息,创建 TodoDataService 实例(因为是瞬时生命周期,新建一个实例,记为「实例 A」);

容器检查 TodoDataService 的构造函数,发现它是「无参构造函数」

(没有依赖项),无需继续递归,「实例 A」创建完成,备用。


步骤 4:容器递归解析第二个依赖项 —— IUserPreferenceService

容器处理第二个依赖:「我需要一个 IUserPreferenceService 实例」;

容器去「注册列表」中查找,发现 IUserPreferenceService 已经在 TodoServiceModule 中注册了,

对应的实现类是 UserPreferenceService:


// TodoServiceModule 中的注册代码(产品说明书)
builder.RegisterType<UserPreferenceService>().As<IUserPreferenceService>().SingleInstance();


容器根据注册信息,创建 UserPreferenceService 实例(因为是单例生命周期,

这是第一次解析,新建一个实例,记为「实例 B」;后续再解析,直接复用这个实例);

容器检查 UserPreferenceService 的构造函数,发现它是「无参构造函数」,

无需继续递归,「实例 B」创建完成,备用。


步骤 5:容器调用 TodoViewModel 构造函数,完成注入,返回最终实例

容器现在有了两个备用实例:「实例 A(TodoDataService)」和「实例 B(UserPreferenceService)」;

容器通过「反射」,调用 TodoViewModel 的带参构造函数,把「实例 A」和「实例 B」作为参数传入:


// 这是 Autofac 内部自动完成的,你看不到这段代码,但它确实发生了
new TodoViewModel(实例 A, 实例 B);


TodoViewModel 实例创建成功,它的私有字段 _todoDataService 和 _userPreferenceService 被成功赋值(注入完成);

容器把这个完整的 TodoViewModel 实例返回给你,也就是 todoViewModel 变量;

你后续把 todoViewModel 赋值给 MainWindow 的 DataContext,整个流程结束。








发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«    2026年1月    »
1234
567891011
12131415161718
19202122232425
262728293031
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接

Powered By Z-BlogPHP 1.7.3

Copyright www.skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864