Silverlight 4 + WCF RIA - Рекомендации по проектированию услуг передачи данных - PullRequest
1 голос
/ 12 июня 2010

Привет всем. Я понимаю, что это довольно длинный вопрос, но я очень признателен за любую помощь от любого, кто имеет опыт работы с RIA. Спасибо!

Я работаю над приложением Silverlight 4, которое просматривает данные с сервера. Я относительно неопытен в RIA Services, поэтому работал над передачей данных, которые мне нужны, клиенту, но каждая новая часть, которую я добавляю в головоломку, кажется все более и более проблематичной. Я чувствую, что мне здесь не хватает некоторых базовых концепций, и мне кажется, что я просто «хакую» фрагменты, отнимающие много времени, каждый ломая предыдущие, когда я пытаюсь добавить их. Мне бы очень хотелось получить отзывы разработчиков, имеющих опыт работы с сервисами RIA, чтобы выяснить, какой способ сделать то, что я пытаюсь сделать. Позвольте мне изложить, что я пытаюсь сделать:

Сначала данные. Источником этих данных являются различные источники, в основном созданные общей библиотекой, которая считывает данные из нашей базы данных и представляет их как POCO (простые старые объекты CLR). Я создаю свои собственные POCO для представления различных типов данных, которые мне нужно передать между сервером и клиентом.

DataA - Это приложение предназначено для просмотра данных определенного типа, позволяет вызывать DataA практически в реальном времени. Каждые 3 минуты клиент должен извлекать данные с сервера всех новых DataA с момента последнего запроса данных.

DataB - пользователи могут просматривать объекты DataA в приложении и могут выбирать один из них из списка, в котором отображаются дополнительные сведения об этом DataA. Я передаю эти дополнительные данные с сервера как DataB.

DataC - Одна из вещей, которые содержит DataB, - это история пары важных значений с течением времени. Я называю каждую точку данных этой истории объектом DataC, а каждый объект DataB содержит много DataC.

Модель данных - На стороне сервера у меня есть один DomainService:

[EnableClientAccess]
public class MyDomainService : DomainService
{
    public IEnumerable<DataA> GetDataA(DateTime? startDate)
    {
        /*Pieces together the DataAs that have been created 
        since startDate, and returns them*/
    }

    public DataB GetDataB(int dataAID)
    {
        /*Looks up the extended info for that dataAID, 
        constructs a new DataB with that DataA's data, 
        plus the extended info (with multiple DataCs in a 
        List<DataC> property on the DataB), and returns it*/
    }

    //Not exactly sure why these are here, but I think it 
    //wouldn't compile without them for some reason? The data 
    //is entirely read-only, so I don't need to update.
    public void UpdateDataA(DataA dataA)
    {
        throw new NotSupportedException();
    }
    public void UpdateDataB(DataB dataB)
    {
        throw new NotSupportedException();
    }
}

Классы для DataA / B / C выглядят так:

[KnownType(typeof(DataB))]
public partial class DataA
{
    [Key]
    [DataMember]
    public int DataAID { get; set; }
    [DataMember]
    public decimal MyDecimalA { get; set; }
    [DataMember]
    public string MyStringA { get; set; }
    [DataMember]
    public DataTime MyDateTimeA { get; set; }
}

public partial class DataB : DataA
{
    [Key]
    [DataMember]
    public int DataAID { get; set; }
    [DataMember]
    public decimal MyDecimalB { get; set; }
    [DataMember]
    public string MyStringB { get; set; }
    [Include] //I don't know which of these, if any, I need?
    [Composition]
    [Association("DataAToC","DataAID","DataAID")]
    public List<DataC> DataCs { get; set; }
}

public partial class DataC
{
    [Key]
    [DataMember]
    public int DataAID { get; set; }
    [Key]
    [DataMember]
    public DateTime Timestamp { get; set; }
    [DataMember]
    public decimal MyHistoricDecimal { get; set; }
}

Полагаю, у меня большой вопрос: стоит ли мне использовать сущности вместо POCO? Правильно ли построены мои классы, чтобы можно было правильно передавать данные? Должен ли я использовать методы Invoke вместо методов Query (Get) на DomainService?

На стороне клиента у меня есть ряд проблем. Удивительно, но одна из моих самых больших работ была нарезкой. Я не ожидал, что с MyDomainContext возникнет столько проблем с многопоточностью. Что я узнал, так это то, что вы, кажется, способны создавать объекты MyDomainContextObject только в потоке пользовательского интерфейса, все запросы, которые вы можете сделать, выполняются только асинхронно, и что если вы попытаетесь подделать это синхронно, блокируя вызывающий поток до тех пор, пока LoadOperation завершается, вы должны сделать это в фоновом потоке, так как он использует поток пользовательского интерфейса для выполнения запроса. Итак, вот что у меня так далеко.

Приложение должно отображать поток объектов DataA, распределяя каждый их 3-минутный блок в течение следующих 3 минут (чтобы они в конечном итоге отображались через 3 минуты после произошедшего, выглядя как непрерывный поток, но их нужно загружать только за 3 минуты) ). Для этого инициализируется основная форма, создается личный MyDomainContext и запускается фоновый рабочий, который непрерывно зацикливается некоторое время (true). В каждом цикле он проверяет, есть ли у него какие-либо данные как оставшиеся для отображения. Если это так, он отображает эти Данные и Thread.Sleep () до тех пор, пока не будет отображен следующий DataA. Если данных нет, он запрашивает больше, используя следующие методы:

public DataA[] GetDataAs(DateTime? startDate)
{
    _loadOperationGetDataACompletion = new AutoResetEvent(false);
    LoadOperation<DataA> loadOperationGetDataA = null;

    loadOperationGetDataA = 
        _context.Load(_context.GetDataAQuery(startDate),
        System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent, false);
    loadOperationGetDataA.Completed += new
        EventHandler(loadOperationGetDataA_Completed);

    _loadOperationGetDataACompletion.WaitOne();
    List<DataA> dataAs = new List<DataA>();
    foreach (var dataA in loadOperationGetDataA.Entities)
        dataAs.Add(dataA);

    return dataAs.ToArray();
}

private static AutoResetEvent _loadOperationGetDataACompletion;
private static void loadOperationGetDataA_Completed(object sender, EventArgs e)
{
    _loadOperationGetDataACompletion.Set();
}

Кажется, неуклюже пытаться заставить его быть синхронным, но так как это уже находится в фоновом потоке, я думаю, что это нормально? Пока что все на самом деле работает, так много взлома, как кажется. Важно отметить, что если я попытаюсь запустить этот код в потоке пользовательского интерфейса, он будет заблокирован, потому что он всегда ждет WaitOne (), блокируя поток, поэтому он не может выполнить запрос на загрузку на сервер.

Таким образом, когда данные отображаются, пользователи могут щелкнуть по одному, чтобы заполнить область сведений полными данными DataB об этом объекте.Для этого у меня есть пользовательский элемент управления панели сведений, подписывающийся на настраиваемое событие выбора, которое запускается при изменении выбора (в потоке пользовательского интерфейса).Я использую подобную технику там, чтобы получить объект DataB:

void SelectionService_SelectedDataAChanged(object sender, EventArgs e)
{
    DataA dataA = /*Get the selected DataA*/;

    MyDomainContext context = new MyDomainContext();
    var loadOperationGetDataB = 
        context.Load(context.GetDataBQuery(dataA.DataAID),
        System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent, false);
    loadOperationGetDataB.Completed += new
        EventHandler(loadOperationGetDataB_Completed);          
}

private void loadOperationGetDataB_Completed(object sender, EventArgs e)
{
    this.DataContext = 
        ((LoadOperation<DataB>)sender).Entities.SingleOrDefault();  
}

Опять-таки, это выглядит немного странно, но работает ... за исключением загружаемого DataB, список DataCs пуст.Я перепробовал там разные вещи, и я не вижу, что я делаю неправильно, чтобы позволить DataC получить DataB.Я почти готов сделать третий запрос для DataC, но мне это кажется еще более хакерским.

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

Спасибо!

1 Ответ

0 голосов
/ 02 июля 2010

Я должен сказать, что это кажется слишком сложным.

Если вы используете платформу сущностей (которая с версией 4 может генерировать POCO, если вам нужно) и Ria / LINQ Вы можете делать все это неявно, используя ленивую загрузку, операторы «расширения» и таблицу для каждого типа наследования.

...