Category Archives: RIA Services

Jak pobrać adres IP serwera RIA Services z poziomu aplikacji klienckiej Silverlight?

Dzisiaj tylko króciutka notka ale mam nadzieję, że komuś się przyda. Ostatnio pisząc aplikację w Silverlight + RIA Services musiałem pobrać (w aplikacji SL) adres IP serwera na którym znajduje się RIA Service. Zrealizowałem to następująco:

System.Windows.Ria.Services.WebDomainClient<Services.DataContext.IDataServiceContract> domain;
domain = m_DataContext.DomainClient as System.Windows.Ria.Services.WebDomainClient<Services.DataContext.IDataServiceContract>;
string url = string.Format("http://{0}:{1}/",domain.ServiceUri.Host,domain.ServiceUri.Port));

Z okazji Wielkanocy –  życzę wszystkiego najlepszego ! 🙂

RIA Services i eager loading.

Domyślnie kolekcje i referencje do innych encji używają opóźnionego ładowania (lazy loading). Wszystkie encje podlegające temu mechanizmowi są ładowane w tle(w sposób niewidoczny dla programisty) w momencie kiedy wymagany jest do nich dostęp. Załóżmy, że mamy encję Invoice, która zawiera kolekcję produktów:

class Invoice
{
    EntityCollection<Product> Products{get;set;}
    public string ReceiverName{get;set;}
    public string IssuerName{get;set;}
}

Gdy użyjemy zapytania odczytującego fakturę, zostaną zwrócone wyłącznie właściwości ReceiverName oraz IssuerName – bez kolekcji Products.

W większości przypadków mechanizm opóźnionego ładowania sprawdza się, jednak czasami chcemy mieć od razu zarówno wszystkie właściwości jak i kolekcje danych – bez potrzeby wysyłania dodatkowego zapytania o kolekcje w drugim zapytaniu. W takim przypadku musimy skorzystać z eager loading(ładowanie zachłanne).

Użycie eager loading w RIA Services składa się z dwóch etapów: skonfigurowania metadanych oraz wywołania metody include przy odczytywaniu danych.

Metadane konfigurujemy za pomocą klas częściowych w bibliotece w której posiadamy model encji(ADO .NET Entity Data Model):

[MetadataType(typeof(Invoice.InvoiceMetadata))]
public partial class Invoice
{
   internal sealed class InvoiceMetadata
   {
       [Include]
       public EntityCollection<Product> Products;
   }

}

Następnie dopisujemy metodę w DomainService:

public IQueryable<Invoice> GetInvoiceWithProducts()
{
  return this.ObjectContext.InvoiceSet.Include("Products");
}

Zwróćcie uwagę na metodę Include – to ona jest odpowiedzialna za eager loading. Teraz po stronie klienta możemy załadować wszystkie faktury wraz z produktami za pomocą poniższego kodu:

m_DataContext.Load<Entities.Invoice>(m_DataContext.GetInvoiceWithProducts());

Azure i RIA Services

Azure to potężna platforma umożliwiająca przetwarzanie w chmurze. Po zainstalowaniu SDK szybko okaże się jednak , że brakuje tam szablonów wspomagających tworzenie aplikacji opartych o RIA Services. W poście przedstawię więc krok po kroku jak można stworzyć projekt RIA Services współpracujący z Azure.

Zaczynamy od stworzenia podstawowego projektu “Windows Cloud Service”.

image

Jako WebRole wybieramy ASP .NET.

image

Po utworzeniu, okno “Solution Explorer” powinno wyglądać mniej więcej tak:

image

Następnie dodajemy projekt aplikacji Silverlight.

image

Przy tworzeniu należy pamiętać aby zaznaczyć opcje hostowania Silverlight na wcześniej utworzonym Web Role. Ponadto należy również zaznaczyć opcję “Enable .NET RIA Services”.

image

W tej chwili mamy już wszystkie projekty utworzone. Następnym zadaniem jest dodanie referencji do niezbędnych bibliotek DLL oraz modyfikacja plików konfiguracyjnych.

W projekcie  WebRole dodajemy  referencje do 3 bibliotek: System.ComponentModel.DataAnnotations, System.Web.DomainServices oraz System.Web.Ria.

image

Następnie w pliku Web.config dopisujemy następujący HTTP handler:

<add name="DataService" verb="GET,POST" path="DataService.axd" type="System.Web.Ria.DataServiceFactory, System.Web.Ria, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

Kolejnym krokiem jest modyfikacja projektu Azure. Otwieramy plik ServiceDefinition.csdef a następnie ustawiamy atrybut enableNativeCodeExecution na true dla elementu WebRole:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="CloudService1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="WebRole1" enableNativeCodeExecution="true">
    <InputEndpoints>
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
    </InputEndpoints>
    <ConfigurationSettings>
      <Setting name="DiagnosticsConnectionString" />
    </ConfigurationSettings>
  </WebRole>
</ServiceDefinition>

W tej chwili mamy już gotowy projekt RIA Service wykorzystujący platformę Azure. Możemy teraz stworzyć sobie ADO .NET Entity Data Model a następnie Domain Service w celu przetestowania stworzonego projektu. Krótki przegląd możliwości RIA Services znajdziecie tutaj.

Jak dostać się do InnerException w RIA Services

Dziś postanowiłem wyjaśnić problem, który zauważyłem jest często poruszany na forach. Załóżmy, że wywołujemy metodę SubmitChanges aby zatwierdzić wprowadzone modyfikacje. Wykonanie metody jednak nie powodzi się i wyrzucany jest wyjątek. Scenariusz wygląda typowo jednak często otrzymujemy mało mówiący komunikat typu:

Submit operation failed. Exception has been thrown by the target of an invocation.

lub

Submit operation failed. An error occurred while updating the entries. See the InnerException for details.

Co gorsza, jeśli zajrzymy do InnerException zobaczymy wartość NULL. Oczywiście mówimy ciągle o stronie klienta. Pytanie brzmi więc: W jaki sposób możemy podejrzeć co tak naprawdę stało się?

Rozwiązaniem jest przeładowanie metody Submit(DomainService) lub OnError(DomainService) oraz obsłużenie tam ewentualnych wyjątków:

public override bool Submit(ChangeSet changeSet)
{
  try
  {
      return base.Submit(changeSet);
  }
  catch (Exception e)
  {
      throw;
  }
}

Jeśli zajrzymy do InnerException zobaczymy, że wyjątek jest tam zapisany i możemy przeczytać dokładniejszą informację np.

Violation of PRIMARY KEY constraint 'PK_Table.Tests'. Cannot insert duplicate key in object 'TestModule.Tests'.
The statement has been terminated.

Rozwiązanie jest być może niezbyt eleganckie ale działa bez zarzutu.