มันควรจะตั้งข้อสังเกตว่าวิธีที่แนะนำคือการใช้รูปแบบตัวเลือก แต่มีกรณีการใช้งานที่ไม่สามารถใช้งานได้จริง (เมื่อทราบพารามิเตอร์ที่รันไทม์เท่านั้นไม่ใช่ในเวลาเริ่มต้น / คอมไพล์) หรือคุณต้องแทนที่การอ้างอิงแบบไดนามิก
มีประโยชน์มากเมื่อคุณต้องการแทนที่การอ้างอิงเดียว (ไม่ว่าจะเป็นสตริงจำนวนเต็มหรือการอ้างอิงประเภทอื่น) หรือเมื่อใช้ไลบรารีของบุคคลที่สามซึ่งยอมรับเฉพาะพารามิเตอร์สตริง / จำนวนเต็มและคุณต้องการพารามิเตอร์รันไทม์
คุณสามารถลองCreateInstance (IServiceProvider, Object [])เป็นทางลัด(ไม่แน่ใจว่าใช้งานได้กับพารามิเตอร์สตริง / ประเภทค่า / primitives (int, float, string), ยังไม่ทดสอบ) (เพิ่งทดลองใช้และยืนยันว่าใช้งานได้แม้จะมี พารามิเตอร์สตริงหลายตัว)แทนที่จะแก้ไขทุกการพึ่งพาด้วยมือ:
_serviceCollection.AddSingleton<IService>(x =>
ActivatorUtilities.CreateInstance<Service>(x, "");
);
พารามิเตอร์ (พารามิเตอร์สุดท้ายของCreateInstance<T>
/ CreateInstance
) กำหนดพารามิเตอร์ที่ควรเปลี่ยน (ไม่ได้รับการแก้ไขจากผู้ให้บริการ) โดยจะใช้จากซ้ายไปขวาตามที่ปรากฏ (เช่นสตริงแรกจะถูกแทนที่ด้วยพารามิเตอร์ที่พิมพ์สตริงแรกของประเภทที่จะสร้างอินสแตนซ์)
ActivatorUtilities.CreateInstance<Service>
ถูกใช้ในหลาย ๆ ที่เพื่อแก้ไขบริการและแทนที่หนึ่งในการลงทะเบียนเริ่มต้นสำหรับการเปิดใช้งานครั้งเดียวนี้
ตัวอย่างเช่นถ้าคุณมีระดับมีชื่อMyService
และมีIOtherService
, ILogger<MyService>
เป็นการอ้างอิงและคุณต้องการที่จะแก้ปัญหาการให้บริการ แต่แทนที่บริการเริ่มต้นของIOtherService
(พูดของมันOtherServiceA
) ด้วยOtherServiceB
คุณสามารถทำสิ่งที่ชอบ:
myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider, new OtherServiceB())
จากนั้นพารามิเตอร์แรกของIOtherService
จะได้รับการOtherServiceB
ฉีดแทนที่จะOtherServiceA
เป็นพารามิเตอร์ที่เหลือจะมาจากคอนเทนเนอร์
สิ่งนี้มีประโยชน์เมื่อคุณมีการอ้างอิงจำนวนมากและต้องการเพียงแค่ปฏิบัติต่อผู้ให้บริการรายเดียวเป็นพิเศษ (เช่นแทนที่ผู้ให้บริการเฉพาะฐานข้อมูลด้วยค่าที่กำหนดค่าในระหว่างการร้องขอหรือสำหรับผู้ใช้เฉพาะบางสิ่งบางอย่างที่คุณรู้ในเวลาดำเนินการและระหว่างการร้องขอและ ไม่ใช่เมื่อแอปพลิเคชันถูกสร้าง / เริ่มต้น)
นอกจากนี้คุณยังสามารถใช้ActivatorUtilities.CreateFactory (ชนิดประเภท []) วิธีการสร้างโรงงานวิธีแทนเพราะมันมีประสิทธิภาพที่ดีขึ้นGitHub อ้างอิงและเกณฑ์มาตรฐาน
ประการต่อมามีประโยชน์เมื่อประเภทได้รับการแก้ไขบ่อยมาก (เช่นใน SignalR และสถานการณ์การร้องขอสูงอื่น ๆ ) โดยทั่วไปคุณจะต้องสร้างObjectFactory
ผ่าน
var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new[] { typeof(IOtherService) });
จากนั้นแคช (เป็นตัวแปร ฯลฯ ) และเรียกมันเมื่อจำเป็น
MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);
## อัปเดต: เพิ่งลองใช้ตัวเองเพื่อยืนยันว่ามันยังทำงานกับสตริงและจำนวนเต็มและมันใช้งานได้ นี่คือตัวอย่างที่เป็นรูปธรรมที่ฉันทดสอบด้วย:
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddTransient<HelloWorldService>();
services.AddTransient(p => p.ResolveWith<DemoService>("Tseng", "Stackoverflow"));
var provider = services.BuildServiceProvider();
var demoService = provider.GetRequiredService<DemoService>();
Console.WriteLine($"Output: {demoService.HelloWorld()}");
Console.ReadKey();
}
}
public class DemoService
{
private readonly HelloWorldService helloWorldService;
private readonly string firstname;
private readonly string lastname;
public DemoService(HelloWorldService helloWorldService, string firstname, string lastname)
{
this.helloWorldService = helloWorldService ?? throw new ArgumentNullException(nameof(helloWorldService));
this.firstname = firstname ?? throw new ArgumentNullException(nameof(firstname));
this.lastname = lastname ?? throw new ArgumentNullException(nameof(lastname));
}
public string HelloWorld()
{
return this.helloWorldService.Hello(firstName, lastName);
}
}
public class HelloWorldService
{
public string Hello(string name) => $"Hello {name}";
public string Hello(string firstname, string lastname) => $"Hello {firstname} {lastname}";
}
static class ServiceProviderExtensions
{
public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class =>
ActivatorUtilities.CreateInstance<T>(provider, parameters);
}
พิมพ์
Output: Hello Tseng Stackoverflow