Рефакторинг нескольких интерфейсов в общий интерфейс с использованием MVVM, MEF и Silverlight4 - PullRequest
2 голосов
/ 25 марта 2010

Я только изучаю MVVM с MEF и уже вижу преимущества, но меня немного смущают некоторые детали реализации. У приложения, которое я создаю, есть несколько Моделей, которые делают то же самое с разными сущностями (службы WCF RIA, представляющие объект инфраструктуры Entity), и я хотел бы избежать реализации аналогичного интерфейса / модели для каждого нужного мне представления, и вот что у меня есть придумать, хотя в настоящее время это не работает.

В общем интерфейсе есть новое завершенное событие для каждой модели, реализующей базовую модель, это был самый простой способ реализовать общий класс, поскольку компилятору не нравилось приведение дочернего к базовому типу.

Код в текущем состоянии компилируется и выполняется, но это пустая IModel, передаваемая в [ImportingConstructor] для класса FaqViewModel.

У меня есть общий интерфейс (упрощенный для публикации), определяемый следующим образом, он должен выглядеть знакомым для тех, кто видел образец RIAXboxGames Шона Вильдермута.

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

Базовый метод, который реализует интерфейс

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

Модель, реализующая базовую модель

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

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

Вид модели

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

И, наконец, само представление Silverlight

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

Ответы [ 2 ]

0 голосов
/ 20 апреля 2010

Как товарищ по нубу, я только начинаю играть с MEF и думаю, что обнаружил возможную проблему с вашим кодом. От вашего вопроса, похоже, что ваша основная проблема - нулевая ссылка IModel.

Попробуйте изменить это:

private readonly IModel _model;

к этому:

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

Я еще не играл с тем, как MEF нравится закрытые и доступные только для чтения свойства, поэтому попробуйте установить значение public и затем убедитесь, что _model не имеет значение null, когда вы впервые пытаетесь его использовать.

0 голосов
/ 01 апреля 2010

Кажется, что я иду по неверному пути, пытаясь провести рефакторинг на несколько моделей. Как видно здесь, http://msdn.microsoft.com/en-us/magazine/dd458800.aspx#id0090019 если бы казалось, что лучше всего было бы думать о модели как об экземпляре класса EDMX, на который ссылаются RIA Services. Таким образом, модель должна содержать все методы и обработчики событий, необходимые для доступа к DomainContext.

Если у кого-то есть другие мысли, я буду к ним открыт.

...