2016-08-19 9 views
15

ASP.NET कोर, चीजें आप is bind "open generics" (सामान्य प्रकार एक ठोस प्रकार के अबाध) माइक्रोसॉफ्ट के निर्भरता इंजेक्शन ढांचे के साथ क्या कर सकते हैं में से एक तो जैसे में:फैक्टरी पैटर्न

public void ConfigureServices(IServiceCollection services) { 
    services.AddSingleton(typeof(IRepository<>), typeof(Repository<>)) 
} 

तुम भी उपयोग कर सकते हैं the factory pattern to hydrate dependencies। यहाँ एक काल्पनिक उदाहरण है:

public interface IFactory<out T> { 
    T Provide(); 
} 

public void ConfigureServices(IServiceCollection services) { 
    services.AddTransient(typeof(IFactory<>), typeof(Factory<>)); 

    services.AddSingleton(
     typeof(IRepository<Foo>), 
     p => p.GetRequiredService<IFactory<IRepository<Foo>>().Provide() 
    ); 
} 

हालांकि, मैं कैसे एक साथ दो अवधारणाओं गठबंधन करने के लिए यह पता लगाने के लिए सक्षम नहीं किया गया। ऐसा लगता है जैसे यह इस तरह से शुरू होगा, लेकिन मुझे ठोस प्रकार की आवश्यकता है जिसका प्रयोग IRepository<> के उदाहरण को हाइड्रेट करने के लिए किया जा रहा है।

public void ConfigureServices(IServiceCollection services) { 
    services.AddTransient(typeof(IFactory<>), typeof(Factory<>)); 

    services.AddSingleton(
     typeof(IRepository<>), 
     provider => { 
      // Say the IServiceProvider is trying to hydrate 
      // IRepository<Foo> when this lambda is invoked. 
      // In that case, I need access to a System.Type 
      // object which is IRepository<Foo>. 
      // i.e.: repositoryType = typeof(IRepository<Foo>); 

      // If I had that, I could snag the generic argument 
      // from IRepository<Foo> and hydrate the factory, like so: 

      var modelType = repositoryType.GetGenericArguments()[0]; 
      var factoryType = typeof(IFactory<IRepository<>>).MakeGenericType(modelType); 
      var factory = (IFactory<object>)p.GetRequiredService(factoryType); 

      return factory.Provide(); 
     }   
    ); 
} 

अगर मैं एक खुली जेनेरिक साथ Func<IServiceProvider, object> functor इस्तेमाल करने की कोशिश, मैं संदेश Open generic service type 'IRepository<T>' requires registering an open generic implementation type. डॉटनैट CLI से साथ this ArgumentException मिलता है। यह लैम्ब्डा तक भी नहीं पहुंचता है।

क्या माइक्रोसॉफ्ट के निर्भरता इंजेक्शन ढांचे के साथ इस प्रकार का बाध्यकारी संभव है?

+0

क्या registerin का लाभ है जी एक लैम्ब्डा जो एक कारखाने को हल करता है जो आवश्यक सेवा को हल करता है? – Steven

+0

अच्छा सवाल। यह सशर्त हाइड्रेशन के लिए जटिलता reroutes। लैम्ब्डा एक के रूप में कार्य करता है, इसलिए आपको एक स्पष्ट फैक्ट्री की आवश्यकता नहीं है (इसके चर को "कार्यान्वयन फैक्ट्री" भी कहा जाता है), लेकिन यदि आपको हाइड्रेट करना चाहते हैं तो इस पर निर्णय लेने के लिए आपको कई सेवाओं की आवश्यकता है, तो आपके पास एक लैम्ब्डा जो जटिल और परीक्षण करने में मुश्किल है। ऊपर दिए गए ब्लॉग पोस्ट में एक अच्छा उदाहरण है: http://dotnetliberty.com/index.php/2016/05/09/asp-net-core-factory-pattern- निर्भरता- इंजेक्शन/ – Technetium

+0

क्या आपको कभी अच्छा लगता है इसके लिए जवाब? मेरे पास एक ही समस्या है, लेकिन यहां कोई भी जवाब समस्या के लिए एक अच्छा समाधान प्रतीत नहीं होता है –

उत्तर

1

मैं भी आपके लैम्ब्डा अभिव्यक्ति के बिंदु को समझ नहीं पा रहा हूं, इसलिए मैं आपको इसे करने का अपना तरीका समझाऊंगा।

मैं तुम क्या इच्छा तक पहुँचने के लिए क्या लेख आपको

साझा यह मैं ASP.NET कोर निर्भरता इंजेक्शन प्रणाली में एक निर्भरता की आपूर्ति से पहले भेजे अनुरोध का निरीक्षण करने की अनुमति दी से समझाया गया है है लगता है

HTTP अनुरोध में कस्टम हेडर का निरीक्षण करने के लिए मेरी आवश्यकता थी कि यह निर्धारित करने के लिए कि कौन सा ग्राहक मेरे एपीआई का अनुरोध कर रहा है। मैं कुछ समय बाद पाइपलाइन में यह निर्णय ले सकता हूं कि यह अद्वितीय अनुरोध प्रदान करने के लिए मेरे IDatabaseRepository (फ़ाइल सिस्टम या एक SQL डेटाबेस से जुड़ा फ़ाइल फ्रेमवर्क) का कार्यान्वयन कौन सा कार्यान्वयन करता है।

तो मैं एक मिडलवेयर

public class ContextSettingsMiddleware 
{ 
    private readonly RequestDelegate _next; 

    public ContextSettingsMiddleware(RequestDelegate next, IServiceProvider serviceProvider) 
    { 
     _next = next; 
    } 

    public async Task Invoke(HttpContext context, IServiceProvider serviceProvider, IHostingEnvironment env, IContextSettings contextSettings) 
    { 
     var customerName = context.Request.Headers["customer"]; 
     var customer = SettingsProvider.Instance.Settings.Customers.FirstOrDefault(c => c.Name == customerName); 
     contextSettings.SetCurrentCustomer(customer); 

     await _next.Invoke(context); 
    } 
} 

मेरे SettingsProvider सिर्फ एक सिंगलटन मुझे उनका संबंधित ग्राहक वस्तु प्रदान करता है लिख कर शुरू करते हैं।

हमारे मिडलवेयर पहुँच इस ContextSettings हम पहले Startup.cs

var contextSettings = new ContextSettings(); 
services.AddSingleton<IContextSettings>(contextSettings); 

में ConfigureServices में यह रजिस्टर करने की आवश्यकता जाने के लिए और Configure विधि में हम अब हमारे ग्राहक है कि हमारे मिडलवेयर

app.UseMiddleware<ContextSettingsMiddleware>(); 

रजिस्टर अन्यत्र से सुलभ है चलिए अपनी फैक्टरी लिखते हैं।

public class DatabaseRepositoryFactory 
{ 
    private IHostingEnvironment _env { get; set; } 

    public Func<IServiceProvider, IDatabaseRepository> DatabaseRepository { get; private set; } 

    public DatabaseRepositoryFactory(IHostingEnvironment env) 
    { 
     _env = env; 
     DatabaseRepository = GetDatabaseRepository; 
    } 

    private IDatabaseRepository GetDatabaseRepository(IServiceProvider serviceProvider) 
    { 
     var contextSettings = serviceProvider.GetService<IContextSettings>(); 
     var currentCustomer = contextSettings.GetCurrentCustomer(); 

     if(SOME CHECK) 
     { 
      var currentDatabase = currentCustomer.CurrentDatabase as FileSystemDatabase; 
      var databaseRepository = new FileSystemDatabaseRepository(currentDatabase.Path); 
      return databaseRepository; 
     } 
     else 
     { 
      var currentDatabase = currentCustomer.CurrentDatabase as EntityDatabase; 
      var dbContext = new CustomDbContext(currentDatabase.ConnectionString, _env.EnvironmentName); 
      var databaseRepository = new EntityFrameworkDatabaseRepository(dbContext); 
      return databaseRepository; 
     } 
    } 
} 

आदेश serviceProvider.GetService<>() विधि का उपयोग करने के लिए आप को शामिल कर अपनी सीएस फ़ाइल में निम्न का उपयोग कर

using Microsoft.Extensions.DependencyInjection; 

अंत में हम ConfigureServices विधि

var databaseRepositoryFactory = new DatabaseRepositoryFactory(_env); 
services.AddScoped<IDatabaseRepository>(databaseRepositoryFactory.DatabaseRepository); 

इसलिए हर में हमारे फैक्टरी का उपयोग कर सकते की आवश्यकता होगी में एक ही HTTP अनुरोध मेरे DatabaseRepository कई मानकों के आधार पर अलग हो सकता है। मैं एक फाइल सिस्टम या SQL डेटाबेस का उपयोग कर सकता हूं और मैं अपने ग्राहक से संबंधित उचित डेटाबेस प्राप्त कर सकता हूं। (हाँ मेरे पास प्रति ग्राहक एकाधिक डेटाबेस हैं, समझने की कोशिश न करें)

मैंने इसे यथासंभव सरल बना दिया, मेरा कोड वास्तविकता में अधिक जटिल है लेकिन आपको विचार (मुझे आशा है)। अब आप अपनी आवश्यकताओं के अनुरूप इसे संशोधित कर सकते हैं।

+0

ऐसा लगता है कि यह मेरी समस्या से थोड़ा अलग है, हालांकि यह एक बहुत अच्छा उदाहरण है कि 'IServiceCollection' के लिए परिभाषाओं को किसी प्रति- अनुरोध आधार आप उस निर्णय का जिक्र कर रहे हैं जिस पर उपयोग करने के लिए एक ज्ञात प्रकार ('IDatabaseRepository') के कार्यान्वयन। मेरी स्थिति में, 'आईरिपोजिटरी <>' खुला/अज्ञात है क्योंकि मेरे पास 'ServiceProvider' के लिए' कार्यान्वयन फैक्ट्री 'लैम्ब्डा में इसे बंद करने के तरीके के बारे में आवश्यक जानकारी नहीं है। – Technetium

+0

बस कुछ चीजों को स्पष्ट करने के लिए। आप हमेशा लैम्ब्डा अभिव्यक्ति से छुटकारा पा सकते हैं।वे डेवलपर जीवन को सरल बनाने के लिए यहां हैं और हमें त्वरित कोड करने की अनुमति देता है। लेकिन उस स्थिति में मुझे अपनी Startup.cs फ़ाइल के अंदर कोड का यह बड़ा टुकड़ा नहीं होना पसंद था। यही कारण है कि मैंने इसे से छुटकारा पाने और 'डेटाबेस डाटापॉजिटरी फैक्टरी' में अपना तर्क लागू करने का फैसला किया। यदि आप चाहें तो आप भी वही कर सकते हैं। –

+0

इसके अलावा मेरे डेटाबेस भंडार वास्तव में 'IDatabaseRepository 'लागू करते हैं, इंटरफ़ेस जो वास्तव में' IRepository 'से प्राप्त होता है। तो मेरा असली निर्भरता इंजेक्शन 'सेवाएं है। एडस्स्कोड <आईडीबेसैट रिपोजिटरी > (डेटाबेस रिपोजिटरी फैक्ट्री। डाटाबेस रिपोजिटरी) '। मैंने सोचा कि बस मेरे उदाहरण में यह एक अच्छा विचार था, लेकिन शायद आपको पूरा नमूना चाहिए ... इससे कोई फर्क नहीं पड़ता कि मैंने यह तय नहीं किया है कि मैं 'आईडीबेसैट' का उपयोग किस कार्यान्वयन का उपयोग करता हूं। मेरे भंडार को सिर्फ अपने अनुबंध का सम्मान करना है और कुछ 'आईडीबेसैट' स्वीकार या स्वीकार करना है। –

4

नेट.कोर निर्भरता आपको खुले जेनेरिक प्रकार को पंजीकृत करते समय फ़ैक्टरी विधि प्रदान करने की अनुमति नहीं देती है, लेकिन आप अनुरोध किए गए इंटरफ़ेस को लागू करने वाले प्रकार को प्रदान करके इसके आसपास काम कर सकते हैं, लेकिन आंतरिक रूप से यह एक कारखाने के रूप में कार्य करेगा । भेष में एक कारखाना:

services.AddSingleton(typeof(IMongoCollection<>), typeof(MongoCollectionFactory<>)); //this is the important part 
services.AddSingleton(typeof(IRepository<>), typeof(Repository<>)) 


public class Repository : IRepository { 
    private readonly IMongoCollection _collection; 
    public Repository(IMongoCollection collection) 
    { 
     _collection = collection; 
    } 

    // .. rest of the implementation 
} 

//and this is important as well 
public class MongoCollectionFactory<T> : IMongoCollection<T> { 
    private readonly _collection; 

    public RepositoryFactoryAdapter(IMongoDatabase database) { 
     // do the factory work here 
     _collection = database.GetCollection<T>(typeof(T).Name.ToLowerInvariant()) 
    } 

    public T Find(string id) 
    { 
     return collection.Find(id); 
    } 
    // ... etc. all the remaining members of the IMongoCollection<T>, 
    // you can generate this easily with ReSharper, by running 
    // delegate implementation to a new field refactoring 
} 

कंटेनर MongoCollectionFactory ti का समाधान करता है पता चल जाएगा टी है कि किस प्रकार और सही ढंग से संग्रह बनाने जाएगा। फिर हम उस संग्रहित संग्रह को आंतरिक रूप से सहेजते हैं, और इसे सभी कॉल का प्रतिनिधि देते हैं। (हम this=factory.Create() जो कोई तिथि नहीं की अनुमति नहीं है नकल उतार रहे हैं :)।)

अद्यतन: के रूप में क्रिस्टियन Hellang द्वारा बताया एक ही पैटर्न ASP.NET लॉगिंग द्वारा किया जाता है

public class Logger<T> : ILogger<T> 
{ 
    private readonly ILogger _logger; 

    public Logger(ILoggerFactory factory) 
    { 
     _logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T))); 
    } 

    void ILogger.Log<TState>(...) 
    { 
     _logger.Log(logLevel, eventId, state, exception, formatter); 
    } 
} 

https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Abstractions/LoggerOfT.cs#L29

मूल यहाँ चर्चा:

https://twitter.com/khellang/status/839120286222012416

संबंधित मुद्दे