2 Stimmen

Refaktorierung mehrerer Schnittstellen zu einer gemeinsamen Schnittstelle mit MVVM, MEF und Silverlight4

Ich bin gerade lernen MVVM mit MEF und sehen bereits die Vorteile, aber ich bin ein wenig verwirrt über einige Details der Implementierung. Die App, die ich baue, hat mehrere Modelle, die das gleiche mit verschiedenen Entitäten tun (WCF RIA Services, die ein Entity-Framework-Objekt aussetzen), und ich möchte vermeiden, eine ähnliche Schnittstelle/Modell für jede Ansicht zu implementieren, die ich benötige, und das Folgende ist, was ich mit gekommen bin, obwohl es derzeit nicht funktioniert.

Die gemeinsame Schnittstelle hat ein neues abgeschlossenes Ereignis für jedes Modell, das das Basismodell implementiert. Dies war der einfachste Weg, wie ich eine gemeinsame Klasse implementieren konnte, da der Compiler kein Casting von einem Kind zum Basistyp mochte.

Der Code, wie es derzeit sitzt kompiliert und läuft, aber die ist ein null IModel in die [ImportingConstructor] für die FaqViewModel-Klasse übergeben.

Ich habe eine gemeinsame Schnittstelle (vereinfacht für Posting) wie folgt definiert, sollte dies vertraut zu denen, die Shawn Wildermuths RIAXboxGames Beispiel gesehen haben.

public interface IModel
{
    void GetItemsAsync();
    event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
}

Eine Basismethode, die die Schnittstelle implementiert

public class ModelBase : IModel
{
    public virtual void GetItemsAsync() { }
    public virtual event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
    protected void PerformQuery<T>(EntityQuery<T> qry, EventHandler<EntityResultsArgs<T>> evt) where T : Entity
    {
        Context.Load(qry, r =>
        {
            if (evt == null) return;
            try
            {
                if (r.HasError)
                {
                    evt(this, new EntityResultsArgs<T>(r.Error));
                }
                else if (r.Entities.Count() > 0)
                {
                    evt(this, new EntityResultsArgs<T>(r.Entities));
                }
            }
            catch (Exception ex)
            {
                evt(this, new EntityResultsArgs<T>(ex));
            }
        }, null);
    }

    private DomainContext _domainContext;
    protected DomainContext Context
    {
        get
        {
            if (_domainContext == null)
            {
                _domainContext = new DomainContext();
                _domainContext.PropertyChanged += DomainContext_PropertyChanged;
            }

            return _domainContext;
        }
    }

    void DomainContext_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "IsLoading":
                AppMessages.IsBusyMessage.Send(_domainContext.IsLoading);
                break;
            case "IsSubmitting":
                AppMessages.IsBusyMessage.Send(_domainContext.IsSubmitting);
                break;
        }
    }
}

Ein Modell, das das Basismodell implementiert

[Export(ViewModelTypes.FaqViewModel, typeof(IModel))]
public class FaqModel : ModelBase
{
    public override void GetItemsAsync()
    {
        PerformQuery(Context.GetFaqsQuery(), GetFaqsComplete);
    }

    public override event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
}

Ein Ansichtsmodell

[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.FaqViewModel)]
public class FaqViewModel : MyViewModelBase
{
    private readonly IModel _model;

    [ImportingConstructor]
    public FaqViewModel(IModel model)
    {
        _model = model;
        _model.GetFaqsComplete += Model_GetFaqsComplete;

        _model.GetItemsAsync(); // Load FAQS on creation
    }

    private IEnumerable<faq> _faqs;
    public IEnumerable<faq> Faqs
    {
        get { return _faqs; }
        private set
        {
            if (value == _faqs) return;

            _faqs = value;
            RaisePropertyChanged("Faqs");
        }
    }

    private faq _currentFaq;
    public faq CurrentFaq
    {
        get { return _currentFaq; }
        set
        {
            if (value == _currentFaq) return;

            _currentFaq = value;
            RaisePropertyChanged("CurrentFaq");
        }
    }

    public void GetFaqsAsync()
    {
        _model.GetItemsAsync();
    }

    void Model_GetFaqsComplete(object sender, EntityResultsArgs<faq> e)
    {
        if (e.Error != null)
        {
            ErrorMessage = e.Error.Message;
        }
        else
        {
            Faqs = e.Results;
        }
    }
}

Und dann schließlich die Silverlight-Ansicht selbst

public partial class FrequentlyAskedQuestions
{
    public FrequentlyAskedQuestions()
    {
        InitializeComponent();
        if (!ViewModelBase.IsInDesignModeStatic)
        {
            // Use MEF To load the View Model
            CompositionInitializer.SatisfyImports(this);
        }
    }

    [Import(ViewModelTypes.FaqViewModel)]
    public object ViewModel
    {
        set
        {
            DataContext = value;
        }
    }
}

0voto

Brian Punkte 68

Es scheint, als ob ich den falschen Weg mit dem Versuch, in mehrere Modelle refactor gehen. Wie hier zu sehen, http://msdn.microsoft.com/en-us/magazine/dd458800.aspx#id0090019 wäre es am besten, das Modell als eine Instanz der EDMX-Klasse zu betrachten, auf die über RIA Services verwiesen wird. Als solches sollte das Modell alle Methoden und Ereignishandler enthalten, die für den Zugriff auf den DomainContext erforderlich sind.

Wenn jemand andere Gedanken hat, bin ich für sie offen.

0voto

Dave Punkte 14156

Als Neuling fange ich gerade an, mit MEF zu spielen, und ich glaube, ich habe ein mögliches Problem mit Ihrem Code gefunden. Aus Ihrer Frage, es klingt wie Ihr Hauptproblem ist die null IModel Referenz.

Versuchen Sie, dies zu ändern:

private readonly IModel _model;

dazu:

[Import]
public IModel _model { get; set; }

Ich habe noch nicht damit gespielt, wie MEF private und schreibgeschützte Eigenschaften mag, also versuchen Sie es mit der Einstellung public und überprüfen Sie dann, dass _model nicht null ist, wenn Sie es zum ersten Mal verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X