.NET কোর ডিআই, কনস্ট্রাক্টরের পরামিতিগুলি পাস করার উপায়


102

নিম্নলিখিত পরিষেবা নির্মাণকারী রয়েছে

public class Service : IService
{
     public Service(IOtherService service1, IAnotherOne service2, string arg)
     {

     }
}

.NET কোর আইওসি মেকানিজম ব্যবহার করে পরামিতিগুলি পাস করার পছন্দগুলি কী

_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( _serviceCollection.BuildServiceProvider().GetService<IOtherService>(), _serviceCollection.BuildServiceProvider().GetService<IAnotherOne >(), "" ));

অন্য কোন উপায আছে কি ?


4
আপনার নকশা পরিবর্তন করুন। একটি প্যারামিটার অবজেক্টে আর্গটি বের করুন এবং এটি ইনজেক্ট করুন।
স্টিভেন

উত্তর:


121

কারখানার প্রতিনিধিটির এক্সপ্রেশন প্যারামিটার ( এই ক্ষেত্রে এক্স ) হ'ল ক IServiceProvider

নির্ভরতা সমাধানের জন্য এটি ব্যবহার করুন,

_serviceCollection.AddSingleton<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                ""));

কারখানার প্রতিনিধি বিলম্বের অনুরোধ। যখন কোনও প্রকারটি সমাধান করা যায় তখন এটি প্রতিনিধি প্যারামিটার হিসাবে সম্পূর্ণ সরবরাহকারীটিকে পাস করবে।


4
হ্যাঁ, আমি এখনই এটি এইভাবে করছি, তবে অন্য কোনও উপায় আছে? আরও মার্জিত হতে পারে? আমি বোঝাতে চাইছি যে নিবন্ধিত পরিষেবাগুলি রয়েছে এমন অন্যান্য পরামিতিগুলি দেখতে কিছুটা অদ্ভুত লাগবে। আমি আরও কিছু সন্ধান করছি যেমন পরিষেবাগুলিকে সাধারনত নিবন্ধন করুন এবং কেবলমাত্র পরিষেবা-বিহীন আর্গুমেন্টগুলি পাস করুন, এই ক্ষেত্রে যুক্তিটি দিন। Autofac মত কিছু নেই .WithParameter("argument", "");
বরিস

4
না আপনি ম্যানুয়ালি সরবরাহকারী তৈরি করছেন, যা খারাপ। প্রতিনিধিটি বিলম্বের অনুরোধ। যখন কোনও প্রকারটি সমাধান করা যায় তখন এটি প্রতিনিধি প্যারামিটার হিসাবে সম্পূর্ণ সরবরাহকারীটিকে পাস করবে।
Nkosi

@ এমসিআর এটি বাক্সের বাইরে কোর ডিআই-এর সাথে ডিফল্ট পন্থা।
এনকোসি

11
@Nkosi: একটি সময়ে কটাক্ষপাত আছে ActivatorUtilities.CreateInstance , তার অংশ Microsoft.Extensions.DependencyInjection.Abstractions(তাই কোন ধারক নির্দিষ্ট নির্ভরতা) প্যাকেজ
Tseng

ধন্যবাদ, @ সাসং যা দেখতে এখানে আসল উত্তরটির মতো দেখাচ্ছে।
BrainSlugs83

59

এটি লক্ষ করা উচিত যে প্রস্তাবিত উপায়টি বিকল্প প্যাটার্নটি ব্যবহার করা । তবে এমন ব্যবহারের কেস রয়েছে যেখানে এর অযৌক্তিক (যখন পরামিতিগুলি কেবল রানটাইমের সময় জানা থাকে, শুরু / সংকলনের সময় নয়) বা আপনাকে গতিশীলভাবে নির্ভরতা প্রতিস্থাপন করতে হবে।

এটি খুব কার্যকর যখন আপনার কোনও একক নির্ভরতা (এটি স্ট্রিং, পূর্ণসংখ্যা বা অন্য ধরণের নির্ভরতা প্রতিস্থাপনের প্রয়োজন হয়) বা কোনও তৃতীয় পক্ষের লাইব্রেরি ব্যবহার করার সময় যা কেবল স্ট্রিং / ইন্টিজার পরামিতি গ্রহণ করে এবং আপনার রানটাইম প্যারামিটার প্রয়োজন।

আপনি শর্টকাট হ্যান্ড হিসাবে ক্রিয়েটইনস্ট্যান্স (আইএসভারসপ্রাইডার, অবজেক্ট []) চেষ্টা করে দেখতে পারেন ( এটি স্ট্রিং প্যারামিটার / মান ধরণের / আদিম (ইন্ট, ফ্লোট, স্ট্রিং), অনির্ধারিত) দিয়ে কাজ করে তা নিশ্চিত নয়) এমনকি এটি ব্যবহার করেও এটির কাজটি নিশ্চিত করেছে একাধিক স্ট্রিং পরামিতি) হাত দ্বারা প্রতিটি একক নির্ভরতা সমাধানের পরিবর্তে:

_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কিন্তু অবশিষ্ট পরামিতি ধারক থেকে আসতে হবে।

এটি আপনার পক্ষে যখন অনেক নির্ভরতা থাকে এবং কেবলমাত্র একটির সাথে বিশেষভাবে চিকিত্সা করতে চান (যেমন অনুরোধের সময় বা কোনও নির্দিষ্ট ব্যবহারকারীর জন্য একটি ডাটাবেস নির্দিষ্ট সরবরাহকারীর প্রতিস্থাপন করতে চান, যা আপনি রান চালানোর সময় এবং একটি অনুরোধকালে জানেন এবং অ্যাপ্লিকেশন নির্মিত / শুরু হওয়ার সময় নয়)।

আপনি পরিবর্তে কারখানা পদ্ধতি তৈরির জন্য অ্যাক্টিভেটর ইউটিলিটিস .স ক্রিয়েট ফ্যাক্টরি (প্রকার, প্রকার []) পদ্ধতিটিও ব্যবহার করতে পারেন, কারণ এটি গিটহাব রেফারেন্স এবং বেঞ্চমার্কের আরও ভাল পারফরম্যান্স সরবরাহ করে ।

পরে যখন টাইপটি খুব ঘন ঘন সমাধান করা হয় তখন এটি কার্যকর হয় (যেমন সিগন্যালআর এবং অন্যান্য উচ্চ অনুরোধের পরিস্থিতিতে)। মূলত আপনি একটি ObjectFactoryমাধ্যমে তৈরি করতে চাই

var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new[] { typeof(IOtherService) });

তারপরে এটি ক্যাশে করুন (পরিবর্তনশীল ইত্যাদি হিসাবে) এবং যেখানে প্রয়োজন সেখানে এটি কল করুন

MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);

## আপডেট: এটির স্ট্রিং এবং পূর্ণসংখ্যার সাথে এটি কাজ করছে তা নিশ্চিত করার জন্য আমি নিজে চেষ্টা করেছি এবং এটি সত্যিই কাজ করে। এখানে আমি যে দৃ concrete় উদাহরণটি পরীক্ষা করেছি তা এখানে:

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}";
}

// Just a helper method to shorten code registration code
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

6
এই হল কিভাবে ASP.NET কোর ডিফল্টরূপে কন্ট্রোলার instantiates ControllerActivatorProvider , তারা সরাসরি আইওসি (যদি না থেকে সমাধান করা না হয় .AddControllersAsServicesব্যবহার করা হয়, যা প্রতিস্থাপন ControllerActivatorProviderসঙ্গেServiceBasedControllerActivator
Tseng

4
ActivatorUtilities.CreateInstance()আমার যা প্রয়োজন ছিল ঠিক তা-ই। ধন্যবাদ!
বিলি জো

4
@ চেঞ্জ আপনি কি আপনার পোস্ট করা কোড পর্যালোচনা এবং একটি আপডেট পোস্ট করতে এত দয়াবান হন? এক্সটেনশান এবং হেল্লো ওয়ার্ল্ড সার্ভিস শীর্ষ স্তরের ক্লাসগুলি করার পরে, আমি এখনও ডেমোসারভাইস.হেলো ওয়ার্ল্ডের সাথে অনির্ধারিত হিসাবে মুখোমুখি হয়েছি। এটি ঠিক করার জন্য এটি কীভাবে যথেষ্ট কাজ করার কথা ছিল তা আমি বুঝতে পারি না। আমার লক্ষ্যটি আমার প্রয়োজন হিসাবে এটি কীভাবে কাজ করে তা বোঝা।
সোহো বিকাশকারী

4
@ সোহোডাবল্পার: স্পষ্টতই public string HelloWorld()পদ্ধতিটির প্রয়োগটি অনুপস্থিত ছিল
সাসেং

এই উত্তরটি আরও মার্জিত এবং গ্রহণ করা উচিত ... ধন্যবাদ!
এক্সোডিয়াম

15

আপনি যদি পরিষেবাটি নতুন করে অস্বস্তিতে পড়ে থাকেন তবে আপনি Parameter Objectপ্যাটার্নটি ব্যবহার করতে পারেন ।

সুতরাং স্ট্রিং প্যারামিটারটিকে তার নিজস্ব ধরণের ract

public class ServiceArgs
{
   public string Arg1 {get; set;}
}

এবং কনস্ট্রাক্টর এখন এটির মতো দেখাবে

public Service(IOtherService service1, 
               IAnotherOne service2, 
               ServiceArgs args)
{

}

এবং সেটআপ

_serviceCollection.AddSingleton<ServiceArgs>(_ => new ServiceArgs { Arg1 = ""; });
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService, Service>();

প্রথম সুবিধাটি হ'ল যদি আপনাকে পরিষেবা নির্মাতা পরিবর্তন করতে হয় এবং এতে নতুন পরিষেবাদি যুক্ত করতে হয়, তবে আপনাকে new Service(...কলগুলি পরিবর্তন করতে হবে না । আরেকটি সুবিধা হ'ল সেটআপটি কিছুটা পরিষ্কার।

একটি একক বা দুটি পরামিতি সহ কোনও কনস্ট্রাক্টরের জন্য এটি যদিও খুব বেশি হতে পারে।


4
জটিল পরামিতিগুলির জন্য বিকল্প প্যাটার্নটি ব্যবহার করা আরও স্বজ্ঞাত হবে এবং এটি বিকল্প প্যাটার্নের জন্য প্রস্তাবিত উপায়, তবে প্যারামিটারগুলির জন্য কেবল রানটাইমের সময় (যেমন একটি অনুরোধ বা দাবি থেকে) জেনে যেতে কম উপযুক্ত
সেং
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.