2015-01-19 9 views
7

यह एक समान प्रश्न है जिसे मैंने कुछ हफ्ते पहले here से आवश्यकता में एक महत्वपूर्ण बदलाव के साथ पूछा था।एकाधिक dbcontext उदाहरणों और निर्भरता इंजेक्शन का उपयोग

मैं एक नया और अनूठा (मैं अपने stackoverflow खोज में इस तरह कुछ भी नहीं मिला है) व्यापार आवश्यकता है:

मैं दो अलग इकाई के ढांचे 6 DbContexts कि दो संरचना की दृष्टि से विभिन्न डेटाबेस को इंगित बनाया है, हमें कॉल करते हैं उन्हें पीसीएमस्टर और पीसीएसबीएस। जबकि पीसीएमस्टर एक सीधा आगे डेटाबेस है और पीसीएमस्टर कॉन्टेक्स्ट में एक स्थिर कनेक्शन स्ट्रिंग होगी, पीसीएसबीएस डेटाबेस का उपयोग नए डेटाबेस बनाने के लिए टेम्पलेट के रूप में किया जाता है। जाहिर है, चूंकि इन कॉपी किए गए डेटाबेसों में एक ही सटीक संरचना होगी, इसलिए विचार है कि dbcontext को तत्कालित होने पर एक अलग डीबी को इंगित करने के लिए कनेक्शन स्ट्रिंग में डेटाबेस (कैटलॉग) नाम को बदलना है। मैंने रिपोजिटरी पैटर्न और निर्भरता इंजेक्शन का भी उपयोग किया है (वर्तमान में निनजेक, लेकिन ऑटोफैक पर जाने की सोच)।

मैंने डीबीकॉन्टेक्स्ट के लिए आईडीबीकॉन्टेक्स्ट इंटरफ़ेस नहीं देखा है, जब तक कि आप स्वयं को बनाना नहीं चाहते हैं। लेकिन फिर, मैंने कई लोगों को यह कहते हुए देखा है कि यह एक अच्छा विचार नहीं है या न ही सर्वोत्तम अभ्यास है।

असल में, मैं कुछ शर्तों के तहत, केवल कुछ ही शर्तों के तहत, पीसीएमस्टर कॉन्टेक्स्ट और पीसीएसब्स कॉन्टेक्स्ट के बीच स्विच करने की आवश्यकता है, लेकिन पीसीएसब्स कॉन्टेक्स्ट में कनेक्शन स्ट्रिंग को भी संशोधित करने की आवश्यकता है, यदि पीसीएसब्स कॉन्टेक्स्ट वर्तमान संदर्भ है। भंडार में उपयोग किए गए dbContext को एक अलग डेटाबेस को इंगित करने की आवश्यकता है। मुझे नहीं पता कि मैं इसे आईओसी कंटेनर जैसे निनजेक्ट या ऑटोफैक के साथ कैसे कर सकता हूं। यहां कुछ कोड स्निपेट्स हैं जिन्हें मैंने अभी तक बनाया है। कुछ वास्तविक कामकाजी समाधानों में सहायता की अत्यधिक सराहना की जाती है।

यहाँ आधार भंडार के लिए मेरे इंटरफेस है

public interface IPCRepositoryBase<T> where T : class 
{ 
    void Add(T entity); 
    void Delete(T entity); 
    T FindOne(Expression<Func<T, bool>> predicate); 
    IQueryable<T> FindBy(Expression<Func<T, bool>> predicate); 
    IQueryable<T> GetAll(); 
    void SetConnection(string connString); 
    //... 
    //... 
} 

यहाँ मेरी सार भंडार आधार

public abstract class PCRepositoryBase<T> : IPCRepositoryBase<T>, IDisposable where T : class 
{ 
    protected readonly IDbSet<T> dbSet; 
    protected DbContext dbCtx; 

    public PCRepositoryBase(DbContext dbCtx) 
    { 
    this.dbCtx = dbCtx; 
    dbSet = dbCtx.Set<T>(); 
    } 
    public void SetConnection(string connString) 
    { 
    dbCtx.Database.Connection.ConnectionString = connString; 
    } 
    public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate) 
    { 
    return dbSet.Where(predicate); // DataContext.Set<T>().Where(predicate ); 
    } 
    public virtual IQueryable<T> GetAll() 
    { 
    return dbSet; 
    } 
    public T FindOne(Expression<Func<T, bool>> predicate) 
    { 
    return dbSet.SingleOrDefault(predicate); 
    } 
    //... Not all implementations listed 
    //... 
} 

है और अब, यहाँ व्युत्पन्न खजाने में से एक के लिए इंटरफेस है:

public interface ISubscriberRepository : IPCRepositoryBase<Subscriber> 
{ 
    IQueryable<Subscriber> GetByStatusName(PCEnums.enumSubsStatusTypes status ); 
    IQueryable<Subscriber> GetByBusinessName(string businessName); 
    //... 
    //... 
} 

public class SubscriberRepository : PCRepositoryBase<Subscriber>, ISubscriberRepository 
{ 
    public SubscriberRepository(DbContext context) : base(context) { } 
    public IQueryable<Subscriber> GetByStatusName(PCEnums.enumSubsStatusTypes status) 
    { 
    return FindBy(x => x.SubsStatusType.Name == status.ToString()); 
    } 
    public IQueryable<Subscriber> GetByBusinessName(string businessName) 
    { 
    return FindBy(s => s.BusinessName.ToUpper() == businessName.ToUpper() ); 
    } 
    //... other operations omitted for brevity! 
} 

अब, मेरे पीसीएसबीएस डीबीकॉन्टेक्स्ट डिजाइनर द्वारा उत्पन्न:

public partial class PCSubsDBContext : DbContext 
{ 
    public PCSubsDBContext() : base("name=PCSubsDBContext") 
    { 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     throw new UnintentionalCodeFirstException(); 
    } 

    public virtual DbSet<Currency> Currencies { get; set; } 
    public virtual DbSet<DurationName> DurationNames { get; set; } 
    public virtual DbSet<Subscriber> Subscribers { get; set; } 
} 

क्या कोई तरीका है, मैं अलग-अलग डेटाबेस के लिए कनेक्शन स्ट्रिंग के साथ दोनों डेटाबेस के लिए एक जेनेरिक dbcontext का उपयोग और/या इंजेक्ट कर सकता हूं। मैं एक इंटरफ़ेस के बिना आईओसी कंटेनर में "डीबीकॉन्टेक्स्ट" कैसे पंजीकृत करूं और फिर भी रनटाइम पर कनेक्शन स्ट्रिंग इंजेक्ट करने में सक्षम हूं? कुछ कोड उदाहरण वास्तव में मदद करेंगे।

उत्तर

8

समाधान वास्तव में काफी सरल है। आपको यह सुनिश्चित करने की ज़रूरत है कि आपके PCSubsDBContext में एक कन्स्ट्रक्टर है जो कनेक्शन स्ट्रिंग, कनेक्शन स्ट्रिंग नाम, डेटाबेस नाम या कुछ समान लेता है। इस तरह आप उस संदर्भ के आधार पर उचित PCSubsDBContext बना सकते हैं। इसके सीटीआर में इंजेक्ट करने का क्या मूल्य लॉग इन उपयोगकर्ता या एक निश्चित अनुरोध पर निर्भर करता है। यह ऐसा कुछ है जिसे आप पहले ही जानते हैं कि कैसे करना है।

पंजीकरण कैसे करें आपके कंटेनर पर थोड़ा सा निर्भर करता है, लेकिन आपको आमतौर पर इसके लिए एक प्रतिनिधि पंजीकृत करना होगा।यह इस प्रकार दिखाई देंगे:

// Autofac 
builder.Register<PCSubsDBContext>(c => 
    new PCSubsDBContext(GetConnectionStringForCurrentRequest()); 

// Ninject 
kernel.Bind<PCSubsDBContext>().ToMethod(m => 
    new PCSubsDBContext(GetConnectionStringForCurrentRequest()); 

// Simple Injector 
container.Register<PCSubsDBContext>(() => 
    new PCSubsDBContext(GetConnectionStringForCurrentRequest()); 

संदर्भ बनाने के बाद से एक अनुरोध की उपलब्धता के आधार है, तो यह और भी अच्छा बदलाव अपने डिजाइन एक सा हो सकता है तो PCSubsDBContext lazily लोड किया जा सकता है, जबकि आप अभी भी निर्माण कर सकते हैं वेब अनुरोध के अस्तित्व के बिना शेष वस्तु ग्राफ। यह बहुत मूल्यवान है, क्योंकि यह आपको verify your container's configuration पर अनुमति देता है। अब आप एक IPcSubsContextProvider इंजेक्षन और के दौरान अपने Context संपत्ति का उपयोग कर सकते हैं, बजाय एक PCSubsDBContext उपभोक्ताओं में सीधे इंजेक्शन लगाने की

public interface IPcSubsContextProvider 
{ 
    PCSubsDBContext Context { get; } 
} 

, अब:

समाधान (हमेशा की तरह) इस तरह के रूप में एक नया अमूर्त लागू करने के लिए, है निष्पादन (लेकिन ऑब्जेक्ट ग्राफ़ के निर्माण के दौरान नहीं)। यह PCSubsDBContext को केवल तभी बनाया जा सकता है जब इसकी आवश्यकता हो, और शेष ऑब्जेक्ट ग्राफ़ के निर्माण के बाद ही बनाया जा सके। एक कार्यान्वयन तुच्छ होगा:

class LazyPcSubsContextProvider : IPcSubsContextProvider 
{ 
    private readonly Lazy<PCSubsDBContext> context; 
    public LazyPcSubsContextProvider(Func<PCSubsDBContext> factory) { 
     this.context = new Lazy<PCSubsDBContext>(factory); 
    } 

    public PCSubsDBContext Context { get { return this.context.Value; } } 
} 

इस कार्यान्वयन एक दायरे वाला/अनुरोध जीवन शैली के साथ पंजीकृत किया जा सकता:

:

// Autofac 
builder.Register<IPcSubsContextProvider>(c => 
    new LazyPcSubsContextProvider(() => 
     new PCSubsDBContext(GetConnectionStringForCurrentRequest()))) 
    .InstancePerHttpRequest(); 

// Ninject 
kernel.Bind<IPcSubsContextProvider>().ToMethod(m => 
    new LazyPcSubsContextProvider(() => 
     new PCSubsDBContext(GetConnectionStringForCurrentRequest()))) 
    .InRequestScope(); 

// Simple Injector 
container.RegisterPerWebRequest<IPcSubsContextProvider>(() => 
    new LazyPcSubsContextProvider(() => 
     new PCSubsDBContext(GetConnectionStringForCurrentRequest()))); 

इस के बाद, सरल इंजेक्टर यह बहुत आसान विन्यास को सत्यापित करने के लिए कर देगा

container.Verify(); 

और यह आपको diagnose your configuration भी करने की अनुमति देता है।

अन्य कंटेनरों के साथ यह करना कठिन होगा।

+0

धन्यवाद स्टीवन। यह पूरी तरह से काम किया। –

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