WPF / MVVM एप्लिकेशन में निर्भरता इंजेक्शन को कैसे संभालना है


102

मैं एक नया डेस्कटॉप एप्लिकेशन शुरू कर रहा हूं और मैं इसे MVVM और WPF का उपयोग करके बनाना चाहता हूं।

मैं भी TDD का उपयोग करने का इरादा कर रहा हूं।

समस्या यह है कि मुझे नहीं पता कि मुझे अपने उत्पादन कोड पर अपनी निर्भरता को इंजेक्ट करने के लिए आईओसी कंटेनर का उपयोग कैसे करना चाहिए।

मान लीजिए कि मेरे पास फ़ॉउलिंग क्लास और इंटरफ़ेस है:

public interface IStorage
{
    bool SaveFile(string content);
}

public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

और फिर मेरे पास एक और वर्ग है जो IStorageएक निर्भरता के रूप में है, यह भी मान लीजिए कि यह वर्ग एक ViewModel या एक व्यावसायिक वर्ग है ...

public class SomeViewModel
{
    private IStorage _storage;

    public SomeViewModel(IStorage storage){
        _storage = storage;
    }
}

इसके साथ मैं यह सुनिश्चित करने के लिए यूनिट परीक्षण आसानी से लिख सकता हूं कि वे ठीक से काम कर रहे हैं, मॉक और आदि का उपयोग कर रहे हैं।

समस्या यह है कि जब यह वास्तविक अनुप्रयोग में इसका उपयोग करने की बात आती है। मुझे पता है कि मेरे पास एक IoC कंटेनर होना चाहिए जो IStorageइंटरफ़ेस के लिए डिफ़ॉल्ट कार्यान्वयन को जोड़ता है , लेकिन मैं ऐसा कैसे करूंगा?

उदाहरण के लिए, यह कैसे होगा यदि मेरे पास निम्नलिखित xaml है:

<Window 
    ... xmlns definitions ...
>
   <Window.DataContext>
        <local:SomeViewModel />
   </Window.DataContext>
</Window>

उस स्थिति में निर्भरता को इंजेक्ट करने के लिए मैं 'WPF' को सही ढंग से कैसे बता सकता हूं?

यह भी मान लीजिए कि मुझे SomeViewModelअपने C # कोड से एक उदाहरण की आवश्यकता है , मुझे यह कैसे करना चाहिए?

मुझे लगता है कि मैं इसमें पूरी तरह से खो गया हूं, मैं किसी भी उदाहरण या मार्गदर्शन की सराहना करता हूं कि इसे संभालने का सबसे अच्छा तरीका कैसे है।

मैं स्ट्रक्च्योर मैप से परिचित हूं, लेकिन मैं विशेषज्ञ नहीं हूं। इसके अलावा, अगर कोई बेहतर / आसान / आउट-ऑफ-द-बॉक्स ढांचा है, तो कृपया मुझे बताएं।


पूर्वावलोकन में .net कोर 3.0 के साथ आप इसे कुछ Microsoft नगेट पैकेज के साथ कर सकते हैं।
बैली मिलर

जवाबों:


87

मैं Ninject का उपयोग कर रहा हूं, और पाया कि इसके साथ काम करना खुशी की बात है। सब कुछ कोड में सेट किया गया है, वाक्यविन्यास काफी सीधा है और इसमें एक अच्छा प्रलेखन है (और एसओ पर बहुत सारे उत्तर)।

तो मूल रूप से यह इस प्रकार है:

व्यू मॉडल बनाएं, और IStorageइंटरफ़ेस को कंस्ट्रक्टर पैरामीटर के रूप में लें:

class UserControlViewModel
{
    public UserControlViewModel(IStorage storage)
    {

    }
}

ViewModelLocatorव्यू मॉडल के लिए एक प्रॉपर्टी के साथ बनाएं , जो व्यू मॉडल को Ninject से लोड करता है:

class ViewModelLocator
{
    public UserControlViewModel UserControlViewModel
    {
        get { return IocKernel.Get<UserControlViewModel>();} // Loading UserControlViewModel will automatically load the binding for IStorage
    }
}

बनाओ ViewModelLocatorApp.xaml में एक आवेदन विस्तृत संसाधन:

<Application ...>
    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

ViewModelLocator में संबंधित गुण DataContextके UserControlलिए बाइंड करें ।

<UserControl ...
             DataContext="{Binding UserControlViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
    </Grid>
</UserControl>

NinjectModule को विरासत में देने वाला एक वर्ग बनाएं, जो आवश्यक बाइंडिंग ( IStorageऔर दृश्यमॉडल) स्थापित करेगा :

class IocConfiguration : NinjectModule
{
    public override void Load()
    {
        Bind<IStorage>().To<Storage>().InSingletonScope(); // Reuse same storage every time

        Bind<UserControlViewModel>().ToSelf().InTransientScope(); // Create new instance every time
    }
}

आवश्यक Ninject मॉड्यूल (अब के लिए ऊपर एक) के साथ आवेदन स्टार्टअप पर IoC कर्नेल को प्रारंभ करें:

public partial class App : Application
{       
    protected override void OnStartup(StartupEventArgs e)
    {
        IocKernel.Initialize(new IocConfiguration());

        base.OnStartup(e);
    }
}

मैंने IocKernelIoC कर्नेल के अनुप्रयोग विस्तृत उदाहरण को धारण करने के लिए एक स्थिर वर्ग का उपयोग किया है , इसलिए मैं आवश्यकता पड़ने पर इसे आसानी से एक्सेस कर सकता हूं:

public static class IocKernel
{
    private static StandardKernel _kernel;

    public static T Get<T>()
    {
        return _kernel.Get<T>();
    }

    public static void Initialize(params INinjectModule[] modules)
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(modules);
        }
    }
}

यह समाधान स्थिर ServiceLocator( a) का उपयोग करता हैIocKernel ) , जिसे आमतौर पर एक विरोधी पैटर्न के रूप में माना जाता है, क्योंकि यह वर्ग की निर्भरता को छुपाता है। हालाँकि UI क्लासेस के लिए कुछ प्रकार की मैनुअल सर्विस लुकअप से बचना बहुत मुश्किल है, क्योंकि उनके पास एक पैरामीटर रहित कंस्ट्रक्टर होना चाहिए, और आप किसी भी तरह इंस्टेंटेशन को नियंत्रित नहीं कर सकते हैं, इसलिए आप VM को इंजेक्ट नहीं कर सकते हैं। कम से कम इस तरह से आप वीएम को अलगाव में परीक्षण कर सकते हैं, जो कि सभी व्यावसायिक तर्क है।

अगर किसी के पास बेहतर तरीका है, तो कृपया शेयर करें।

EDIT: लकी लिसी ने निनॉज इंस्टेंट यूआई क्लासेज देकर स्टेटिक सर्विस लोकेटर से छुटकारा पाने का जवाब दिया। उत्तर का विवरण यहां देखा जा सकता है


13
मैं निर्भरता इंजेक्शन के लिए नया हूं, फिर भी आपके दिल में इसका समाधान निनॉज के साथ सर्विस लोकेटर एंटी-पैटर्न को मिला रहा है क्योंकि आप स्टैटिक व्यूमॉडल लोकेटर का उपयोग कर रहे हैं। कोई तर्क कर सकता है कि इंजेक्शन Xaml फ़ाइल में किया गया है, जिसके परीक्षण की संभावना कम है। मेरे पास एक बेहतर समाधान नहीं है और संभवतः आपका उपयोग करेगा - फिर भी मुझे लगता है कि उत्तर में भी इसका उल्लेख करना उपयोगी होगा।
user3141326

यार तुम्हारा हल बस बहुत अच्छा है, निम्नलिखित लाइन के साथ केवल एक "समस्या" है DataContext="{Binding [...]}":। यह वीएसएम-डिज़ाइनर को ViewModel के कंस्ट्रक्टर में सभी प्रोग्राम-कोड को निष्पादित करने का कारण बन रहा है। मेरे मामले में विंडो को निष्पादित किया जा रहा है और वी.एस. के लिए किसी भी बातचीत को औपचारिक रूप से अवरुद्ध करता है। शायद किसी को डिज़ाइन-टाइम में "वास्तविक" व्यूमॉडल का पता नहीं लगाने के लिए ViewModelLocator को संशोधित करना चाहिए। - एक अन्य समाधान "प्रोजेक्ट कोड को अक्षम करना" है, जो अन्य सभी चीजों को मधुमक्खी के प्रदर्शन से भी बचाएगा। हो सकता है कि आप पहले से ही इसका समाधान पा चुके हों। इस मामले में मैं आपको इसे दिखाना चाहता हूं।
लकीलीकी

@LuckyLikey आप d: DataContext = "{d: DesignInstance vm: UserControlViewModel, IsDesignTimeCreatable = True}" का उपयोग करने का प्रयास कर सकते हैं, लेकिन मुझे यकीन नहीं है कि इससे कोई फर्क पड़ता है। लेकिन क्यों / कैसे VM निर्माता एक मोडल विंडो लॉन्च कर रहा है? और किस तरह की खिड़की?
सोनडरगार्ड

@son वास्तव में मुझे नहीं पता कि क्यों और कैसे, लेकिन जब मैं समाधान एक्सप्लोरर से एक विंडो डिज़ाइनर को खोलता हूं, जैसा कि नया टैब खोला जा रहा है, तो विंडो डिजाइनर द्वारा प्रदर्शित की जा रही है और एक ही विंडो दिखाई देती है जैसे कि डीबगिंग मोडल, वीएस "माइक्रोसेफ्ट विजुअल स्टूडियो एक्सएएमएल डिजाइनर" के बाहर एक नई प्रक्रिया में होस्ट किया गया। यदि प्रक्रिया बंद हो जाती है, तो वीएस-डिज़ाइनर पहले से वर्णित अपवाद के साथ विफल हो जाता है। मैं अपने समाधान की कोशिश करने जा रहा हूँ। जब मैं नई जानकारी का पता लगाता हूँ तो आपको सूचित करता हूँ :)
LuckyLikey

1
@sondergard मैंने ServiceLocator एंटी-पैटर्न से बचते हुए, आपके उत्तर में सुधार पोस्ट किया है। जांचने के लिए स्वतंत्र हैं।
LuckyLikey

52

अपने प्रश्न में आप DataContextXAML में दृश्य की संपत्ति का मूल्य निर्धारित करते हैं। इसके लिए जरूरी है कि आपके व्यू-मॉडल में डिफॉल्ट कंस्ट्रक्टर हो। हालाँकि, जैसा कि आपने नोट किया है, यह निर्भरता इंजेक्शन के साथ अच्छी तरह से काम नहीं करता है जहाँ आप कंस्ट्रक्टर में निर्भरता को इंजेक्ट करना चाहते हैं।

इसलिए आप DataContextXAML में संपत्ति सेट नहीं कर सकते । इसके बजाय आपके पास अन्य विकल्प हैं।

यदि आप आवेदन एक सरल श्रेणीबद्ध दृश्य-मॉडल पर आधारित हैं, तो आप आवेदन शुरू होने पर संपूर्ण व्यू-मॉडल पदानुक्रम का निर्माण कर सकते हैं (आपको फ़ाइल StartupUriसे संपत्ति को निकालना होगा App.xaml):

public partial class App {

  protected override void OnStartup(StartupEventArgs e) {
    base.OnStartup(e);
    var container = CreateContainer();
    var viewModel = container.Resolve<RootViewModel>();
    var window = new MainWindow { DataContext = viewModel };
    window.Show();
  }

}

यह देखने वाले मॉडल के ऑब्जेक्ट ग्राफ के आसपास आधारित है, RootViewModelलेकिन आप कुछ व्यू-मॉडल कारखानों को पेरेंट व्यू-मॉडल में इंजेक्ट कर सकते हैं, जिससे उन्हें नए चाइल्ड व्यू-मॉडल बनाने की अनुमति मिलती है, ताकि ऑब्जेक्ट ग्राफ को ठीक न करना पड़े। यह भी उम्मीद है कि आपके प्रश्न का उत्तर मुझे लगता है कि मुझे SomeViewModelअपने csकोड से एक उदाहरण की आवश्यकता है , मुझे यह कैसे करना चाहिए?

class ParentViewModel {

  public ParentViewModel(ChildViewModelFactory childViewModelFactory) {
    _childViewModelFactory = childViewModelFactory;
  }

  public void AddChild() {
    Children.Add(_childViewModelFactory.Create());
  }

  ObservableCollection<ChildViewModel> Children { get; private set; }

 }

class ChildViewModelFactory {

  public ChildViewModelFactory(/* ChildViewModel dependencies */) {
    // Store dependencies.
  }

  public ChildViewModel Create() {
    return new ChildViewModel(/* Use stored dependencies */);
  }

}

यदि आपका एप्लिकेशन प्रकृति में अधिक गतिशील है और शायद नेविगेशन के आसपास आधारित है, तो आपको उस कोड में हुक करना होगा जो नेविगेशन करता है। हर बार जब आप एक नए दृश्य पर जाते हैं, तो आपको व्यू-मॉडल (DI कंटेनर से) बनाने की आवश्यकता होती है, दृश्य को स्वयं देखें और DataContextदृश्य-मॉडल पर दृश्य सेट करें । आप इस दृश्य को पहले कर सकते हैं जहां आप किसी दृश्य के आधार पर दृश्य-मॉडल चुनते हैं या आप इसे पहले दृश्य-मॉडल कर सकते हैंजहां दृश्य-मॉडल निर्धारित करता है कि किस दृश्य का उपयोग करना है। एक MVVM फ्रेमवर्क आपके डीआई कंटेनर को व्यू-मॉडल्स के निर्माण में हुक करने के लिए आपको किसी तरह से यह महत्वपूर्ण कार्यक्षमता प्रदान करता है, लेकिन आप इसे स्वयं भी लागू कर सकते हैं। मैं यहां थोड़ा अस्पष्ट हूं क्योंकि आपकी आवश्यकताओं के आधार पर यह कार्यक्षमता काफी जटिल हो सकती है। यह एक मुख्य कार्य है जो आपको MVVM फ्रेमवर्क से मिलता है, लेकिन एक साधारण एप्लिकेशन में अपना रोल करने से आपको एक अच्छी समझ मिलेगी कि MVVM फ्रेमवर्क हुड के तहत क्या प्रदान करता है।

DataContextXAML में घोषित करने में सक्षम नहीं होने से आप कुछ डिज़ाइन-टाइम समर्थन खो देते हैं। यदि आपके दृश्य-मॉडल में कुछ डेटा हैं तो यह डिज़ाइन-टाइम के दौरान दिखाई देगा जो बहुत उपयोगी हो सकता है। सौभाग्य से, आप WPF में डिज़ाइन-टाइम विशेषताओं का भी उपयोग कर सकते हैं । इसका एक तरीका यह है कि निम्नलिखित विशेषताओं को <Window>तत्व में या <UserControl>XAML में जोड़ा जाए :

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MyViewModel, IsDesignTimeCreatable=True}"

व्यू-मॉडल प्रकार में दो निर्माता होने चाहिए, डिज़ाइन-टाइम डेटा के लिए डिफ़ॉल्ट और निर्भरता इंजेक्शन के लिए दूसरा:

class MyViewModel : INotifyPropertyChanged {

  public MyViewModel() {
    // Create some design-time data.
  }

  public MyViewModel(/* Dependencies */) {
    // Store dependencies.
  }

}

ऐसा करने से आप निर्भरता इंजेक्शन का उपयोग कर सकते हैं और अच्छा डिज़ाइन-टाइम समर्थन बनाए रख सकते हैं।


12
यही वह है जिसकी तलाश में मैं हूं। यह मुझे निराश करता है कि मैं कितनी बार उत्तर पढ़ता हूं जो कहता है "बस [ येड्डे-य ] ढांचे का उपयोग करें।" यह सब अच्छी तरह से और अच्छा है, लेकिन मैं यह जानना चाहता हूं कि पहले कैसे खुद को रोल करना है और फिर मुझे पता चल सकता है कि वास्तव में मेरे लिए किस तरह का ढांचा हो सकता है। इतनी स्पष्ट रूप से वर्तनी के लिए धन्यवाद।
kmote

28

मैं यहां जो पोस्ट कर रहा हूं वह सोनगार्ड के उत्तर में सुधार है, क्योंकि मैं जो बताने जा रहा हूं वह टिप्पणी में फिट नहीं है :)

वास्तव में मैं एक स्वच्छ समाधान प्रस्तुत कर रहा हूंStandardKernel , जो -Instance के लिए एक ServiceLocator और एक आवरण की आवश्यकता से बचता है, जिसे sondergard के समाधान में कहा जाता है IocContainer। क्यों? जैसा कि उल्लेख किया गया है, वे प्रतिरूप हैं।

बनाना StandardKernelहर जगह उपलब्ध

Ninject के जादू की कुंजी है StandardKernel-Instance जिसे -Method का उपयोग करने की आवश्यकता होती है .Get<T>()

वैकल्पिक रूप से sondergard के IocContainerआप -Class के StandardKernelअंदर बना सकते हैं App

बस अपने App.xaml से StartUpUri को हटा दें

<Application x:Class="Namespace.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
             ... 
</Application>

यह App.xaml.cs के अंदर ऐप का CodeBehind है

public partial class App
{
    private IKernel _iocKernel;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        _iocKernel = new StandardKernel();
        _iocKernel.Load(new YourModule());

        Current.MainWindow = _iocKernel.Get<MainWindow>();
        Current.MainWindow.Show();
    }
}

अब से, Ninject जीवित है और लड़ने के लिए तैयार है :)

अपने को इंजेक्ट करना DataContext

Ninject के जीवित होने पर, आप सभी प्रकार के इंजेक्शन लगा सकते हैं, जैसे कि प्रॉपर्टी सेटर इंजेक्शन या सबसे आम कंस्ट्रक्टर इंजेक्शन

इस तरह आप अपने में अपने ViewModel इंजेक्षन है WindowकीDataContext

public partial class MainWindow : Window
{
    public MainWindow(MainWindowViewModel vm)
    {
        DataContext = vm;
        InitializeComponent();
    }
}

बेशक आप भी एक इंजेक्शन लगा सकते हैं IViewModelयदि आप सही बाइंडिंग करते हैं तो , लेकिन यह इस उत्तर का हिस्सा नहीं है।

सीधे कर्नेल तक पहुंचना

यदि आपको कर्नेल पर सीधे तरीके (जैसे .Get<T>()-मैथोड) को कॉल करने की आवश्यकता है , तो आप कर्नेल को खुद को इंजेक्ट कर सकते हैं।

    private void DoStuffWithKernel(IKernel kernel)
    {
        kernel.Get<Something>();
        kernel.Whatever();
    }

यदि आपको कर्नेल के स्थानीय उदाहरण की आवश्यकता होगी तो आप इसे संपत्ति के रूप में इंजेक्ट कर सकते हैं।

    [Inject]
    public IKernel Kernel { private get; set; }

हालांकि यह बहुत उपयोगी हो सकता है, मैं आपको ऐसा करने की सलाह नहीं दूंगा। बस ध्यान दें कि इस तरह से इंजेक्ट की गई वस्तुएं कंस्ट्रक्टर के अंदर उपलब्ध नहीं होंगी, क्योंकि यह बाद में इंजेक्ट की जाती हैं।

इस लिंक के अनुसार आपको इंजेक्शन लगाने के बजाय फैक्ट्री-एक्सटेंशन का उपयोग करना चाहिएIKernel (DI कंटेनर) ।

एक सॉफ्टवेयर सिस्टम में DI कंटेनर को नियोजित करने के लिए अनुशंसित दृष्टिकोण यह है कि आवेदन की संरचना रूट एकल स्थान है जहां कंटेनर को सीधे छुआ जाता है।

कैसे Ninject.Extensions.Factory इस्तेमाल किया जा सकता है भी यहाँ लाल हो सकता है


अच्छा तरीका। इस स्तर पर कभी भी निनजे का पता नहीं लगाया, लेकिन मैं देख सकता हूं कि मैं याद कर रहा हूं :)
Sondergard

@ कोई thx। अपने उत्तर के अंत में आपने बताया कि यदि किसी के पास बेहतर तरीका है, तो कृपया साझा करें। क्या आप इसे लिंक जोड़ सकते हैं?
लकीलकी

अगर किसी को इस पर उपयोग करने के बारे में दिलचस्पी है Ninject.Extensions.Factory, तो इसे टिप्पणियों में यहां बताएं और मैं कुछ और जानकारी जोड़ूंगा।
लकी लाईक

1
@LuckyLikey: मैं XAML के माध्यम से विंडो डाटाकनेक्ट पर एक ViewModel कैसे जोड़ पाऊंगा जिसमें कोई पैरामीटर रहित कंस्ट्रक्टर नहीं है? सर्विसलॉकर के साथ सोनडरगार्ड के समाधान के साथ यह स्थिति संभव होगी।
थॉमस गेउलेन

तो कृपया मुझे बताएं कि मुझे उन सेवाओं को कैसे पुनः प्राप्त करना है जो मुझे संलग्न गुणों में चाहिए? वे हमेशा स्थिर होते हैं, दोनों बैकिंग DependencyPropertyफ़ील्ड और इसके गेट और सेट के तरीके।
बसंत 22६

12

मैं एक "व्यू फर्स्ट" दृष्टिकोण के लिए जाता हूं, जहां मैं व्यू-मॉडल को व्यू के कंस्ट्रक्टर (इसके कोड-पीछे) में देता हूं, जो डेटा संदर्भ को सौंपा जाता है, उदा।

public class SomeView
{
    public SomeView(SomeViewModel viewModel)
    {
        InitializeComponent();

        DataContext = viewModel;
    }
}

यह आपके XAML- आधारित दृष्टिकोण को बदल देता है।

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

डीआई फ्रेमवर्क की पसंद बहुत अधिक अप्रासंगिक है क्योंकि वे सभी अनिवार्य रूप से एक ही काम करते हैं, यानी आप एक ठोस प्रकार के साथ-साथ एक इंटरफ़ेस (या एक प्रकार) पंजीकृत करते हैं जिसे आप उस इंटरफ़ेस पर निर्भरता मिलने पर फ्रेमवर्क को तुरंत चाहते हैं। रिकॉर्ड के लिए मैं कैसल विंडसर का उपयोग करता हूं।

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

वैकल्पिक रूप से MVVM फ्रेमवर्क जैसे MVVM लाइट में से एक पर एक नज़र डालें। मुझे इनमें से कोई अनुभव नहीं मिला है इसलिए वे इस पर टिप्पणी नहीं कर सकते कि वे क्या उपयोग करना चाहते हैं


1
आप बच्चे के विचारों के लिए निर्माता तर्क कैसे पारित करते हैं? मैंने इस दृष्टिकोण की कोशिश की है, लेकिन मुझे यह देखने के लिए कि माता-पिता के पास डिफ़ॉल्ट पैरामीटर-कम कंस्ट्रक्टर नहीं है, यह बताते हुए अपवाद प्राप्त करें
डॉक्टर जोन्स

10

MVVM लाइट स्थापित करें।

इंस्टॉलेशन का एक हिस्सा मॉडल लोकेटर बनाने के लिए है। यह एक वर्ग है जो आपके दृष्टिकोण को गुणों के रूप में उजागर करता है। इन संपत्तियों के प्राप्तकर्ता को आपके IOC इंजन से इंस्टेंस लौटाया जा सकता है। सौभाग्य से, MVVM लाइट में सिंपलियो सी फ्रेमवर्क भी शामिल है, लेकिन यदि आप चाहें तो आप दूसरों को तार दे सकते हैं।

सरल आईओसी के साथ आप एक प्रकार के खिलाफ कार्यान्वयन को पंजीकृत करते हैं ...

SimpleIOC.Default.Register<MyViewModel>(()=> new MyViewModel(new ServiceProvider()), true);

इस उदाहरण में, आपके व्यू मॉडल को उसके निर्माता के अनुसार एक सेवा प्रदाता ऑब्जेक्ट बनाया और पास किया गया है।

फिर आप एक संपत्ति बनाते हैं जो IOC से एक उदाहरण देता है।

public MyViewModel
{
    get { return SimpleIOC.Default.GetInstance<MyViewModel>; }
}

चतुर हिस्सा यह है कि दृश्य मॉडल लोकेटर तब app.xaml या डेटा स्रोत के बराबर में बनाया जाता है।

<local:ViewModelLocator x:key="Vml" />

अब आप अपने 'MyViewModel' प्रॉपर्टी को एक इंजेक्शन सेवा के साथ अपना व्यूमोडल प्राप्त करने के लिए बांध सकते हैं।

उम्मीद है की वो मदद करदे। किसी भी कोड की अशुद्धि के लिए माफी, एक iPad पर मेमोरी से कोडित।


आपके पास एप्लिकेशन के बूटस्ट्रैप के बाहर GetInstanceया resolveबाहर नहीं होना चाहिए । यह DI की बात है!
सोइल - मैथ्यू प्रेवोट

मैं मानता हूं कि आप स्टार्टअप के दौरान संपत्ति मूल्य निर्धारित कर सकते हैं, लेकिन यह सुझाव देना कि डीआई के खिलाफ आलसी तात्कालिकता का उपयोग करना गलत है।
किड्सशॉ

@kishw मैंने नहीं किया।
सोइल - मथिउ प्रेवोट

3

कैन्योनिक ड्रायोक केस

एक पुरानी पोस्ट का उत्तर देना, लेकिन DryIocमेरे साथ ऐसा करना और मुझे लगता है कि डीआई और इंटरफेस का एक अच्छा उपयोग है (कंक्रीट कक्षाओं का कम से कम उपयोग)।

  1. WPF ऐप का शुरुआती बिंदु है App.xaml, और वहां हम बताते हैं कि उपयोग करने के लिए इनबिल्ट क्या है; हम डिफ़ॉल्ट xaml के बजाय कोड के साथ ऐसा करते हैं:
  2. हटाने StartupUri="MainWindow.xaml"App.xaml में
  3. in codebehind (App.xaml.cs) इसे जोड़ें override OnStartup:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        DryContainer.Resolve<MainWindow>().Show();
    }

यह स्टार्टअप बिंदु है; यह भी एकमात्र जगह है जहाँ resolveबुलाया जाना चाहिए।

  1. कॉन्फ़िगरेशन रूट (मार्क सेमैन की पुस्तक के अनुसार। .NET में निर्भरता इंजेक्शन; केवल ठोस कक्षाओं का उल्लेख किया जाना चाहिए) एक ही कोडबेहैंड में, कंस्ट्रक्टर में होगा:

    public Container DryContainer { get; private set; }
    
    public App()
    {
        DryContainer = new Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient());
        DryContainer.Register<IDatabaseManager, DatabaseManager>();
        DryContainer.Register<IJConfigReader, JConfigReader>();
        DryContainer.Register<IMainWindowViewModel, MainWindowViewModel>(
            Made.Of(() => new MainWindowViewModel(Arg.Of<IDatabaseManager>(), Arg.Of<IJConfigReader>())));
        DryContainer.Register<MainWindow>();
    }

टिप्पणी और कुछ और विवरण

  • मैंने केवल दृश्य के साथ ठोस वर्ग का उपयोग किया MainWindow;
  • मुझे ViewModel के लिए किस कंट्रक्टर का उपयोग करना है (हमें ड्रायआईकॉक के साथ ऐसा करने की आवश्यकता है), क्योंकि डिफॉल्ट कंस्ट्रक्टर को XAML डिज़ाइनर के लिए मौजूद होना आवश्यक है, और इंजेक्शन वाला कंस्ट्रक्टर वास्तविक है जो एप्लिकेशन के लिए उपयोग किया जाता है।

DI के साथ ViewModel निर्माता:

public MainWindowViewModel(IDatabaseManager dbmgr, IJConfigReader jconfigReader)
{
    _dbMgr = dbmgr;
    _jconfigReader = jconfigReader;
}

डिजाइन के लिए ViewModel डिफ़ॉल्ट निर्माता:

public MainWindowViewModel()
{
}

दृश्य का कोडबिंदु:

public partial class MainWindow
{
    public MainWindow(IMainWindowViewModel vm)
    {
        InitializeComponent();
        ViewModel = vm;
    }

    public IViewModel ViewModel
    {
        get { return (IViewModel)DataContext; }
        set { DataContext = value; }
    }
}

और ViewModel के साथ एक डिजाइन उदाहरण प्राप्त करने के लिए (MainWindow.xaml) दृश्य में क्या आवश्यक है:

d:DataContext="{d:DesignInstance local:MainWindowViewModel, IsDesignTimeCreatable=True}"

निष्कर्ष

इसलिए हमें ड्रायआईओक कंटेनर और DI के साथ WPF एप्लिकेशन का बहुत साफ और न्यूनतम क्रियान्वयन मिला, जबकि विचारों और व्यूमोडल के डिजाइन उदाहरणों को संभव बनाए रखना।


2

प्रबंधित एक्स्टेंसिबिलिटी फ्रेमवर्क का उपयोग करें ।

[Export(typeof(IViewModel)]
public class SomeViewModel : IViewModel
{
    private IStorage _storage;

    [ImportingConstructor]
    public SomeViewModel(IStorage storage){
        _storage = storage;
    }

    public bool ProperlyInitialized { get { return _storage != null; } }
}

[Export(typeof(IStorage)]
public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

//Somewhere in your application bootstrapping...
public GetViewModel() {
     //Search all assemblies in the same directory where our dll/exe is
     string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
     var catalog = new DirectoryCatalog(currentPath);
     var container = new CompositionContainer(catalog);
     var viewModel = container.GetExport<IViewModel>();
     //Assert that MEF did as advertised
     Debug.Assert(viewModel is SomViewModel); 
     Debug.Assert(viewModel.ProperlyInitialized);
}

सामान्य तौर पर, आप क्या करेंगे एक स्थिर वर्ग होगा और आपको एक वैश्विक कंटेनर (कैश्ड, नैच) प्रदान करने के लिए फ़ैक्टरी पैटर्न का उपयोग करना होगा।

देखने के मॉडल को कैसे इंजेक्ट किया जाए, आप उन्हें उसी तरह इंजेक्ट करते हैं जिस तरह से आप बाकी सब चीजों को इंजेक्ट करते हैं। XAML फ़ाइल के कोड के पीछे एक इंपोर्ट कंस्ट्रक्टर (या किसी प्रॉपर्टी / फील्ड में इंपोर्ट स्टेटमेंट डालते हैं) बनाएं और इसे व्यू मॉडल इंपोर्ट करने के लिए कहें। फिर बाँध अपने Window's DataContextहै कि संपत्ति के लिए। आपकी जड़ वस्तुएं जिन्हें आप वास्तव में कंटेनर से बाहर खींचते हैं, वे आमतौर पर निर्मित Windowवस्तुएं हैं। बस विंडो कक्षाओं में इंटरफेस जोड़ें, और उन्हें निर्यात करें, फिर कैटलॉग से ऊपर के रूप में पकड़ो (App.xaml.cs में ... यह WPF बूटस्ट्रैप फ़ाइल है)।


आपको DI का एक महत्वपूर्ण बिंदु याद आ रहा है, जो किसी भी उदाहरण के निर्माण से बचने के लिए है new
सोइल - मैथ्यू प्रेवोट

0

मैं ViewModel का उपयोग करने का सुझाव दूंगा - पहला दृष्टिकोण https://github.com/Caliburn-Micro/Calibub.Micro

देखें: https://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions

Castle Windsorआईओसी कंटेनर के रूप में उपयोग करें ।

सभी सम्मेलनों के बारे में

Caliburn.Micro की मुख्य विशेषताओं में से एक है सम्मेलनों की एक श्रृंखला पर अभिनय करके बॉयलर प्लेट कोड की आवश्यकता को दूर करने की अपनी क्षमता में प्रकट होता है। कुछ लोग सम्मेलनों से प्यार करते हैं और कुछ उनसे नफरत करते हैं। यही कारण है कि सीएम के सम्मेलन पूरी तरह से अनुकूलन योग्य हैं और यहां तक ​​कि यदि वांछित नहीं है तो पूरी तरह से बंद भी हो सकते हैं। यदि आप सम्मेलनों का उपयोग करने जा रहे हैं, और चूंकि वे डिफ़ॉल्ट रूप से चालू हैं, तो यह जानना अच्छा है कि वे सम्मेलन क्या हैं और कैसे काम करते हैं। यही इस लेख का विषय है। संकल्प देखें (ViewModel-First)

मूल बातें

सीएम का उपयोग करते समय आपका पहला सम्मेलन आपके पास होने की संभावना है, यह संकल्प देखने से संबंधित है। यह सम्मेलन आपके आवेदन के किसी भी ViewModel-First क्षेत्रों को प्रभावित करता है। ViewModel-First में, हमारे पास एक मौजूदा ViewModel है जिसे हमें स्क्रीन पर रेंडर करने की आवश्यकता है। ऐसा करने के लिए, CM एक UserControl1 को खोजने के लिए एक सरल नामकरण पैटर्न का उपयोग करता है जिसे यह ViewModel और प्रदर्शन के साथ बाँधना चाहिए। तो, वह पैटर्न क्या है? आइए जानने के लिए बस ViewLocator.LocateForModelType पर एक नज़र डालें:

public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = (modelType, displayLocation, context) =>{
    var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
    if(context != null)
    {
        viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
        viewTypeName = viewTypeName + "." + context;
    }

    var viewType = (from assmebly in AssemblySource.Instance
                    from type in assmebly.GetExportedTypes()
                    where type.FullName == viewTypeName
                    select type).FirstOrDefault();

    return viewType == null
        ? new TextBlock { Text = string.Format("{0} not found.", viewTypeName) }
        : GetOrCreateViewType(viewType);
};

पहले "संदर्भ" चर को अनदेखा करते हैं। दृश्य को प्राप्त करने के लिए, हम एक धारणा बनाते हैं कि आप अपने वीएम के नामकरण में "ViewModel" पाठ का उपयोग कर रहे हैं, इसलिए हम बस "मॉडल" शब्द को हटाकर इसे हर जगह "व्यू" में बदल देते हैं। यह दोनों प्रकार के नाम और नाम स्थान बदलने का प्रभाव है। तो ViewModels.CustomerViewModel Views.CustomerView बन जाएगा। या यदि आप अपने एप्लिकेशन को सुविधा द्वारा व्यवस्थित कर रहे हैं: CustomerManagement.CustomerViewModel CustomerManagement.CustomerView बन जाता है। उम्मीद है, यह बहुत सीधे आगे है। एक बार जब हमारे पास नाम होता है, तो हम उस नाम के साथ प्रकारों की खोज करते हैं। हम किसी भी असेंबली की खोज करते हैं जिसे आप CM के लिए असेंबली के माध्यम से खोजे जा रहे हैं। Source .Instance.2 यदि हम टाइप करते हैं, तो हम एक उदाहरण बनाते हैं (या यदि यह पंजीकृत है तो IoC कंटेनर से एक प्राप्त करें) और इसे कॉलर को लौटा दें। अगर हमें टाइप नहीं मिला,

अब, उस "संदर्भ" मान पर वापस जाएं। इसी तरह सीएम एक ही ViewModel पर कई दृश्यों का समर्थन करता है। यदि एक संदर्भ (आमतौर पर एक स्ट्रिंग या एक एनम) प्रदान किया जाता है, तो हम उस मूल्य के आधार पर नाम का एक और परिवर्तन करते हैं। यह परिवर्तन प्रभावी रूप से मानता है कि आपके पास अंत से "दृश्य" शब्द को हटाकर और इसके बजाय संदर्भ को जोड़कर विभिन्न विचारों के लिए एक फ़ोल्डर (नाम स्थान) है। तो, "मास्टर" हमारे ViewModels.CustomerViewModel का एक संदर्भ दिया गया Views.Customer.Master बन जाएगा।


2
आपकी पूरी पोस्ट राय है।
जॉन पीटर्स

-1

स्टार्टअप uri को अपने app.xaml से हटा दें।

App.xaml.cs

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        IoC.Configure(true);

        StartupUri = new Uri("Views/MainWindowView.xaml", UriKind.Relative);

        base.OnStartup(e);
    }
}

अब आप उदाहरणों के निर्माण के लिए अपने IoC वर्ग का उपयोग कर सकते हैं।

MainWindowView.xaml.cs

public partial class MainWindowView
{
    public MainWindowView()
    {
        var mainWindowViewModel = IoC.GetInstance<IMainWindowViewModel>();

        //Do other configuration            

        DataContext = mainWindowViewModel;

        InitializeComponent();
    }

}

आपके पास बाहरी app.xaml.cs GetInstanceका कोई कंटेनर नहीं होना चाहिए resolve, आप DI का बिंदु खो रहे हैं। इसके अलावा, दृश्य के कोडबेहैंड में xaml दृश्य का उल्लेख करना एक तरह का दृढ़ संकल्प है। बस शुद्ध c # में दृश्य को कॉल करें, और कंटेनर के साथ ऐसा करें।
सोइल - मैथ्यू प्रेडोट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.