如何使用Microsoft DI容器传递构造函数参数,实现单文档单IUndoRedo实例
问题
需要配置Microsoft DI容器解析相关类型,期望能通过类似以下方式注册服务,且可轻松替换实现(比如将IParagraph的实现换成ParagraphVersion2):
var services = new ServiceCollection(); services.AddTransient<IHeader, Header>(); services.AddTransient<IParagraph, Paragraph>(); services.AddTransient<IFooter, Footer>(); services.AddTransient<IDocument, Document>(); services.AddTransient<IUndoRedo, UndoRedo>();
核心需求:每个Document实例对应唯一的IUndoRedo实例,该文档内的所有IObject对象必须使用这个IUndoRedo实例作为构造函数参数。目前仅通过属性注入实现,但容易创建不完整对象,寻求构造函数注入的DI配置方案。
解决方案
要实现文档与撤销重做实例的绑定,同时保留构造函数注入和实现替换的灵活性,推荐以下两种方案:
方案一:利用Scoped作用域自动关联实例
通过将IUndoRedo注册为Scoped服务,确保同一作用域内的Document和所有IObject共享同一个IUndoRedo实例,完全依赖DI容器管理生命周期。
1. 改造对象工厂
让工厂从DI容器中获取实例,而非手动创建,确保使用当前作用域的IUndoRedo:
public class ObjectFactory { private readonly IServiceProvider _serviceProvider; public ObjectFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IObject Create(ObjectType objectType) { return objectType switch { ObjectType.Header => _serviceProvider.GetRequiredService<IHeader>(), ObjectType.Footer => _serviceProvider.GetRequiredService<IFooter>(), ObjectType.Paragraph => _serviceProvider.GetRequiredService<IParagraph>(), _ => throw new NotSupportedException(nameof(objectType)) }; } }
2. 注册服务
var services = new ServiceCollection(); // 每个作用域对应一个唯一的IUndoRedo实例 services.AddScoped<IUndoRedo, UndoRedo>(); // 文档和所有对象实现注册为Scoped,确保同一作用域内共享UndoRedo services.AddScoped<IHeader, Header>(); services.AddScoped<IParagraph, Paragraph>(); services.AddScoped<IFooter, Footer>(); services.AddScoped<IDocument, Document>(); // 注册工厂,用于动态创建对象 services.AddTransient<ObjectFactory>();
3. 使用方式
每次创建文档时,开启一个新的作用域,该作用域内的所有实例自动绑定同一个IUndoRedo:
using var provider = services.BuildServiceProvider(); // 创建第一个文档及关联对象 using (var scope = provider.CreateScope()) { var doc = scope.ServiceProvider.GetRequiredService<IDocument>(); var factory = scope.ServiceProvider.GetRequiredService<ObjectFactory>(); doc.Objects.Add(factory.Create(ObjectType.Header)); doc.Objects.Add(factory.Create(ObjectType.Footer)); doc.Objects.Add(factory.Create(ObjectType.Paragraph)); doc.Objects.Add(factory.Create(ObjectType.Paragraph)); // 验证:所有对象与文档共享同一个UndoRedo bool isShared = doc.Objects.All(o => o.UndoRedo == doc.UndoRedo); Console.WriteLine($"文档1的对象共享UndoRedo:{isShared}"); // 输出True } // 创建第二个文档及关联对象 using (var scope = provider.CreateScope()) { var doc = scope.ServiceProvider.GetRequiredService<IDocument>(); var factory = scope.ServiceProvider.GetRequiredService<ObjectFactory>(); doc.Objects.Add(factory.Create(ObjectType.Header)); doc.Objects.Add(factory.Create(ObjectType.Footer)); doc.Objects.Add(factory.Create(ObjectType.Paragraph)); bool isShared = doc.Objects.All(o => o.UndoRedo == doc.UndoRedo); Console.WriteLine($"文档2的对象共享UndoRedo:{isShared}"); // 输出True }
方案二:自定义文档工厂手动绑定实例
如果不想依赖DI作用域,可以通过自定义工厂,为每个文档创建专属的IUndoRedo,并在创建对象时传入该实例:
1. 定义文档工厂接口
public interface IDocumentFactory { IDocument CreateDocument(); IObject CreateObjectForDocument(IDocument document, ObjectType objectType); }
2. 实现文档工厂
利用ActivatorUtilities从DI容器中创建对象,并传入文档的IUndoRedo实例:
public class DocumentFactory : IDocumentFactory { private readonly IServiceProvider _serviceProvider; public DocumentFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IDocument CreateDocument() { // 创建专属的UndoRedo实例 var undoRedo = _serviceProvider.GetRequiredService<IUndoRedo>(); return new Document(undoRedo); } public IObject CreateObjectForDocument(IDocument document, ObjectType objectType) { var undoRedo = document.UndoRedo; // 从DI容器获取对象并传入指定的UndoRedo return objectType switch { ObjectType.Header => ActivatorUtilities.CreateInstance<Header>(_serviceProvider, undoRedo), ObjectType.Footer => ActivatorUtilities.CreateInstance<Footer>(_serviceProvider, undoRedo), ObjectType.Paragraph => ActivatorUtilities.CreateInstance<Paragraph>(_serviceProvider, undoRedo), _ => throw new NotSupportedException(nameof(objectType)) }; } }
3. 注册服务
var services = new ServiceCollection(); // 每次获取都创建新的UndoRedo实例 services.AddTransient<IUndoRedo, UndoRedo>(); // 对象实现注册为Transient,方便替换 services.AddTransient<IHeader, Header>(); services.AddTransient<IParagraph, Paragraph>(); services.AddTransient<IFooter, Footer>(); // 注册自定义文档工厂 services.AddTransient<IDocumentFactory, DocumentFactory>();
4. 使用方式
using var provider = services.BuildServiceProvider(); var factory = provider.GetRequiredService<IDocumentFactory>(); // 创建第一个文档 var doc1 = factory.CreateDocument(); doc1.Objects.Add(factory.CreateObjectForDocument(doc1, ObjectType.Header)); doc1.Objects.Add(factory.CreateObjectForDocument(doc1, ObjectType.Footer)); doc1.Objects.Add(factory.CreateObjectForDocument(doc1, ObjectType.Paragraph)); // 创建第二个文档 var doc2 = factory.CreateDocument(); doc2.Objects.Add(factory.CreateObjectForDocument(doc2, ObjectType.Header)); doc2.Objects.Add(factory.CreateObjectForDocument(doc2, ObjectType.Footer));
待转换的原始代码(中文注释版)
public enum ObjectType { Header, Footer, Paragraph } public interface IObject { IUndoRedo UndoRedo { get; } } public interface IHeader : IObject { } public class Header : IHeader { public IUndoRedo UndoRedo { get; } public Header(IUndoRedo undoRedo) { UndoRedo = undoRedo; } } public interface IParagraph : IObject { } public class Paragraph : IParagraph { public IUndoRedo UndoRedo { get; } public Paragraph(IUndoRedo undoRedo) { UndoRedo = undoRedo; } } public interface IFooter : IObject { } public class Footer : IFooter { public IUndoRedo UndoRedo { get; } public Footer(IUndoRedo undoRedo) { UndoRedo = undoRedo; } } public interface IDocument { IUndoRedo UndoRedo { get; } List<IObject> Objects { get; } } public interface IUndoRedo { } public class UndoRedo : IUndoRedo { } public class Document : IDocument { public IUndoRedo UndoRedo { get; } public List<IObject> Objects { get; } = new(); public Document(IUndoRedo undoRedo) { UndoRedo = undoRedo; } } public class ObjectFactory { public IObject Create(ObjectType objectType, IUndoRedo undoRedo) { switch (objectType) { case ObjectType.Header: return new Header(undoRedo); case ObjectType.Footer: return new Footer(undoRedo); case ObjectType.Paragraph: return new Paragraph(undoRedo); default: throw new NotSupportedException(nameof(objectType)); } } } internal class Program { static void Main(string[] args) { var factory = new ObjectFactory(); // 每个新文档对应唯一的UndoRedo实例,文档内所有对象共享该实例 { var undoRedo1 = new UndoRedo(); var document1 = new Document(undoRedo1); document1.Objects.Add(factory.Create(ObjectType.Header, undoRedo1)); document1.Objects.Add(factory.Create(ObjectType.Footer, undoRedo1)); document1.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo1)); document1.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo1)); } // 每个新文档对应唯一的UndoRedo实例,文档内所有对象共享该实例 { var undoRedo2 = new UndoRedo(); var document2 = new Document(undoRedo2); document2.Objects.Add(factory.Create(ObjectType.Header, undoRedo2)); document2.Objects.Add(factory.Create(ObjectType.Footer, undoRedo2)); document2.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo2)); } } }
内容的提问来源于stack exchange,提问作者Vastar
相关产品推荐
相关产品推荐

