Problem zwrotnych referencji w WCF

Podczas wielu batalii z WCF'em natknąłem się na konieczność umieszczenia w obiekcie będącej elementem jakiejś kolekcji referencji do obiektu posiadającego tą kolekcję. (Przyznam że konieczność ta była związana z faktem iż w przesyłanej klasie była zaszyta część logiki biznesowej a tak się nie powinno robić ale jak mus to mus). Tak więc posiadałem DataContract'y podobne do tych:

    [DataContract]
    public class Parent
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public List<Children> Childrens { get; set; }
    }

    [DataContract]
    public class Children
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public Parent Parent { get; set; }
    }

Próba pobrania obiektu Parent zakończyła się błędem:

WCFBlad1.png

Znalezienie przyczyny błędu było na tyle trudniejsze że pomiędzy buildem a napisaniem DataContractu robiłem całą masę innych rzeczy.

Błąd ten jest spowodowany wyjątkiem StackOverflow po stronie serwera, wynika on z faktu że przy próbie serializacji klasy Children mechanizm docierając do pola Parent nie orientuje się że już ten obiekt serializował albo właśnie go serializuje.

Rozwiązaniem tego problemu jakie znalazłem googlając było ustawienie właściwości IsReference w klasie rodzica i rzeczywiście w tak prostym przypadku rozwiązanie to działa jak należy.

    [DataContract(IsReference=true)]
    public class Children
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public Parent Parent { get; set; }
    }

Moja sytuacja była jednak trochę bardziej złożona. Po pierwsze zarówno Parent jak i Children dziedziczyło po jednym typie bazowym:

    [KnownType(typeof(ChildrenBP))]
    [KnownType(typeof(ParentBP))]
    [DataContract]
    public class SuperBP
    {
        [DataMember]
        public string Name { get; set; }
    }
    
    [DataContract(IsReference=true)]
    public class ParentBP : SuperBP
    {
        [DataMember]
        public List<ChildrenBP> Childrens { get; set; }
    }
	
	[DataContract]
    public class ChildrenBP : SuperBP
    {
        [DataMember]
        public ParentBP Parent { get; set; }
    }

Ustawienie znacznika IsReference na klasie ParentBP i pobranie referencji kończy się błędem:

pictures/WCFBlad2.png

W większości przypadków należy postąpić zgodnie z komunikatem błędu i ustawić IsReference na klasie bazowej. Jeżeli jednak z jakiś powodów nie chcemy tego robić możemy usunąć znacznik DataMember z pola Parent w klasie ChildrenBP i posiłkować się znacznikiem OnDeSerialized.

    [KnownType(typeof(ChildrenBP))]
    [KnownType(typeof(ParentBP))]
    [DataContract]
    public class SuperBP
    {
        [DataMember]
        public string Name { get; set; }
    }
	
    [DataContract]
    public class ParentBP : SuperBP
    {
        [OnDeserialized]
        public void OnDeserialized(StreamingContext context)
        {
            foreach (ChildrenBP child in Childrens)
            {
                child.Parent = this;
            }
        }

        [DataMember]
        public List<ChildrenBP> Childrens { get; set; }
    }
	[DataContract]
    public class ChildrenBP : SuperBP
    {

        public ParentBP Parent { get; set; }
    }

Słów kilka o krotkach.

Chciałem dzisiaj opisać trochę moje doświadczenia z krotkami.

Pomimo że ich konstrukcja nie jest złożona, a istnienie klasy o ich cechać było wyczekiwane dość długo to pojawiły się one w .Net dopiero w wersji 4.0, a ich pojawienie się zostało zdecydowanie zagłuszone przez inne nowinki w tej wersji.

Pierwszym narzucającym się zastosowaniem dla krotek jest wielotypowana tablica. Tablica taka przydaje się gdy musimy przerzucić listę kilkuelementowych paczek obiektów pomiędzy dwoma miejscami w kodzie. Przykładem takiego miejsca wywoływanie operacji na bazie dla każdego rekordu zwróconego przez obiekt implementujące IDataReader. Jak wiadomo przy próbie wywołania zapytania przed zamknięciem Readera następuje zgłoszenie wyjątku. Problem taki rozwiązywało się na kilka sposobów (oczywiście nie polegających na rezygnowaniu z Readera):

1) Utworzenie kilku list dla każdego przepisywanego pola – powoduje konieczność stosowania pętli for zamiast foreach

2) Utworzenie listy tablic obiektów (listy słowników, lub innej wariacji generycznej) – fajne rozwiązanie dopuki nie potrzebna nam informacja o rzeczywistym typie elementu (wtedy trzeba rzutować).

3) Utworzenie klasy i przesyłanie listy obiektów tej klasy – C# to nie Java tutaj duża lisczba klas kuje w oczy ;]

4) Klasy anonimowe – byłyby idealne gdyby nie problem z nacechowaniem ich typem listy

5) Stosowanie elementu klasy stworzonej w innym celu (np. KeyValuePair<T1, T2>)

Zastosowanie krotek jest rozwiązaniem łączącym znaczną część zalet powyższych rozwiązań jednocześnie nie posiada większości ich wad:

List<Tuple<int, string>> tempList = new List<Tuple<int, string>>();
using(SqlCommand cmd = new SqlCommand())
{
	cmd.Connection = con;
	cmd.CommandText = "select id, nazwa from Tabela1";
	using(IDataReader reader = cmd.ExecuteReader())
	{
		while(reader.Read())
		{
			tempList.Add(new Tuple<int, string>());
		}
	}
}
using(SqlCommand cmd = new SqlCommand())
{
	cmd.Connection = con;
	cmd.CommandText = "insert into Tabela2(id, nazwa) values (@id, @nazwa)";
	foreach(var tuple in tempList)
	{
		cmd.AddWithValue("@id", tuple.Item1);
		cmd.AddWithValue("@nazwa", tuple.Item2);
		cmd.ExecuteNonQuery();
	}
}

Inną ciekawostką związaną z krotkami jest fakt iż mają solidnie zaprogramowane funkcje GetHashCode i Equals, przez co można je wykorzystać żeby ułatwić sobie implementację tych metod:

class ExampleClass
{
	public int IdPart1 { get; set; }
	public int IdPart2 { get; set; }

	public override int GetHashCode()
	{
		return new Tuple<int, int>(IdPart1, IdPart2).GetHashCode();
	}

	public override bool Equals(object obj)
	{
		if (obj is ExampleClass)
		{
			ExampleClass other = (ExampleClass)obj;
			return new Tuple<int, int>(IdPart1, IdPart2).Equals(new Tuple<int, int>(other.IdPart1, other.IdPart2));
		}
		return false;
	}
}

albo z wykorzystaniem LambdaEqualityComparer

class ExampleClass
{
	public int IdPart1 { get; set; }
	public int IdPart2 { get; set; }

	private LambdaEqualityComparer<ExampleClass, Tuple<int, int>> comparer = 
                     new LambdaEqualityComparer<ExampleClass, Tuple<int, int>>(ec => new Tuple<int, int>(ec.IdPart1, ec.IdPart2));

	public override int GetHashCode()
	{
		return comparer.GetHashCode(this);
	}

	public override bool Equals(object obj)
	{
		return comparer.Equals(this, obj as ExampleClass);
	}
}

Krotki implementują też interfejs IComparable co może być przydatne jeżeli chcielibyśmy zaimplementować ten interfejs w swoich klasach (prawie klasyczny przykład Dekoratora).

Kolejnymi interfejsami implementowanymi przez Krotki są IStructuralEquatable i IStructuralComparable jest to związane z silnym wykorzystywaniem ich w języku F# (niestety jeszcze do niego nie przysiadłem). Więcej informacji na ten temat można uzyskać od Marcina Najdera z firmy Comarch który silnie promuje ten język na konferencjach.

Ze względu na fakt iż krotki po zainicjowaniu nie zmieniają swojego stanu mają podobno zastosowanie w aplikacjach współbieżnych (podobno bo sam nie miałem okazji się o tym przekonać).

Na koniec chciałbym dodać kilka słów o wykrytych przeze mnie niedopracowaniach krotek.

Pierwsza niedogodność Nazwy Item1, Item2, Item3… często uniemożliwiają domyślenie się co jest co, jednak po pierwsze nie mam innego pomysłu na nazwy tych właściwości, po drugie z założenia powinny być one wykorzystywane jako tymczasowe pojemniki na obiekty.

Druga fakt iż nie da się ich łatwo zserializować i deserializować (co też nie jest ich poprawnym zastosowaniem).

W komentarzach proszę o umieszczanie pomysłów na inne zastosowanie dla krotek.

Komunikacja PHP i C Sharp

Ostatnio musiałem napisać aplikację w której aplikacja .Net współpracuje z aplikacją napisaną w PHP za pośrednictwem gołego http.

Sprawa nie jest szczególnie złożona jednak jak się później okazało nie jest taka prosta jak myślałem. Fragmenty kodu zawarte w Blogu pochodzą z przykładowego programu dostępnego do pobrania na końcu postu.

To co było proste:

Komunikacja PHP z dowolną inną platformą odbywa się za pomocą protokołu HTTP który jest ogólnie dobrze opisany np. tutaj. Dodatkowo w większości platform istnieją dedykowane biblioteki obsługujące w mniejszym lub większym stopniu komunikację przez http, a biblioteka standardowa .Net nie jest pod tym względem wyjątkiem(tfu!).

Komunikację za pomocą http ułatwiają przynajmniej dwie klasy HttpWebRequest i WebClient. Klasa WebClient ma w moim mniemaniu znacznie bardziej intuicyjny interfejs i to jej będę się starał używać.

Wywołanie GET:

Wywołanie GET jest odwołaniem się do strony www równoważnym z wpisaniem adresu w przeglądarce internetowej, parametry do serwera wysyła się dopisując po adresie strony znak zapytania i wartości tych parametrów w formacie parametr1=wartosc1&parametr2=wartosc2&parametr3=wartosc3. Ograniczeniem tej metody jest fakt iż długość adresu strony wraz z wartościami parametrów nie może przekraczać 256znaków.

Metody tej nie powinno się także stosować na stronach w celu przekazywania danych wrażliwych (jak np. login i hasło użytkownika).

Do zbudowania łańcucha połączeniowego można użyć klasy UriBuilder, jest to klasa która na podstawie fragmentów adresu potrafi złożyć pełną ścieżkę do pliku/katalogu także na serwerze www.

Klasą która się przydaje jest także HttpUtility posiadająca metodę UrlEncode zwalniającą programistę z wstawiania odpowiednich znaków ucieczki w wysyłanym zapytaniu do serwera, niestety klasa ta znajduje się w przestrzeni nazw System.Web w bibliotece o tej samej nazwie, biblioteka ta zaś nie znajduje się w Wersji Client Profile platformy .Net. Tak więc jeżeli klasy tej chcemy użyć w projekcie Windows Forms lub WPF musimy dokonać następujących zmian w projekcie:

1) Zapisanie wszystkich niezapisanych plików.

2) W Solution Explorze klikamy prawym przyciskiem myszy na projekt i wybieramy Properties

3) W zakładce Application znajdujemy Combobox Target platform i wybieramy w nim .Net Framework 4.0

4) Zapisujemy wszysto i zamykamy właściwości projektu

5) Klikamy prawym przyciskiem w katalog refereces w projekcie i wybieramy AddReference

6) Przechodzimy na zakładkę .Net i wybieramy System.Web

7) Zatwierdzamy dialog i możemy używać klasy System.Web.HttpUtility

Najprostszy kod pobierający treść strony www może wyglądać tak.

WebClient client = new WebClient();
// Przy niektórych serwerach aplikacja powinna się przedstawić 
//client.Headers.Add("user-agent", "PHP and dotNet");

UriBuilder queryBuilder = new UriBuilder(tbServer.Text + "helloGet.php");
queryBuilder.Query = string.Format("name={0}", HttpUtility.UrlEncode(tbName.Text));

string result = client.DownloadString(queryBuilder.Uri);
MessageBox.Show(this, result);

plik helloGet.php zawiera chyba najprostrzą możliwą obsługę zapytania GET:

<?php
echo 'Witaj '.$_GET['name'];
?>

Pobranie plików z serwera:

Zadanie trochę mniej związane z PHP, aczkolwiek pojawiające się w pracy z http to pobranie pliku z serwera http. Tutaj klasa WebClient oferuje dwie możliwości.

Metoda DownloadFile podaje się adres pliku i nazwę pliku nic prostszego

using (SaveFileDialog sfd = new SaveFileDialog())
{
    sfd.Filter = "Pliki jpeg|*.jpg";
    if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        WebClient client = new WebClient();
        client.DownloadFile("http://www.programy.witraze-plocin.pl/wp-content/uploads/2010/04/cycki226jo5.jpg", sfd.FileName);
    }
}

Metoda DownloadData zwraca tablicę bajtów składających się na plik metoda może być przydatna gdy pobrane dane chcemy tylko obrobić bez zapisywania ich na dysku. Np. wyświetlić obrazek w pictureBoxie.

WebClient client = new WebClient();
byte[] buffer = client.DownloadData("http://www.programy.witraze-plocin.pl/wp-content/uploads/2010/04/cycki226jo5.jpg");

MemoryStream stream = new MemoryStream(buffer);

// Kwestie techniczne
if (pbCycki.Image != null)
{
    Image oldImage = pbCycki.Image;
    pbCycki.Image = null;
    oldImage.Dispose();
}

pbCycki.Image = Image.FromStream(stream);

Wadą tej metody jest spore zajęcie pamięci ponieważ cały plik jest wgrywany do RAMu, dodatkowo aplikacja musi pobrać cały plik i dopiero wtedy go obrobić. Przypadkiem gdy żadna z powyższych metod nie zastosowania jest pobranie dużego pliku z serwera i zapisanie go do strumienia nie będącego plikiem(np. wysłać portem COM do jakiegoś urządzenia). W tym przypadku należy wykorzystać metodę WebRequest.

private void FromHttpToStream(Stream outStream, string url)
{
    WebClient client = new WebClient();
    client.OpenRead(url).CopyTo(outStream);
}

Wywołanie POST:

Wysłanie zapytania POST do serwera http przy pomocy klasy HttpClient jest dosyć niskopoziomowe. Ponieważ należy wykonać następujące kroki:

1) Dodać do nagłówka wiadomości informację o rodzaju przesyłanej treści "content-type: application/x-www-form-urlencoded"

2) Sformatować treść wysyłanej wiadomości przy UrlEncode

3) Zakodować informację przy pomocy kodowania ASCII i wysłać na serwer.

4) Rozkodować przesłaną wiadomość przy pomocy kodowania użytego na stronie.

WebClient client = new WebClient();
// Przy niektórych serwerach aplikacja powinna się przedstawić 
//client.Headers.Add("user-agent", "PHP and dotNet");

client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

byte[] buffer = Encoding.ASCII.GetBytes(string.Format("name={0}", HttpUtility.UrlEncode(tbName.Text)));
buffer = client.UploadData(tbServer.Text + "helloPost.php", "POST", buffer);
string result = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
MessageBox.Show(this, result);

Upload plików:

Upload plików też jest dość prosty klasa WebClient posiada metodę UploadFile, która jako parametry pobiera nazwę wysyłanego pliku i adres pliku do którego wysyłana jest zawartość pliku.

using (OpenFileDialog ofd = new OpenFileDialog())
{
    ofd.Filter = "Pliki jpeg|*jpg";
    if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        WebClient client = new WebClient();
        client.UploadFile(tbServer.Text + "uploadFile.php", ofd.FileName);
    }
}

Po stronie serwera wysyłany plik jest opisany w tabeli $_FILES w polu 'file' obsługa pliku. Przykładowa obsługa może wyglądać następująco:

<?php
$tempFile = $_FILES['file']['tmp_name'];
copy($tempFile, 'uploaded.jpg');
unlink($tempFile);
?>

Pliki z kodem przykładowego programu są do pobrania tutaj.

Zastosowanie LambdaEqualityComparer

Jakiś czas temu Maciej Aniserowicz na swoim blogu opublikował kod klasy LambdaEqualityComarer

Bardzo często chcielibyśmy aby równość elementów jakiejś klasy zależała tylko od jakiegoś pola (najczęściej od ID), aby to osiągnąć należy nadpisać metody GetHashcode i Equals tej klasy jest to jednak praca dosyć żmudna i powtarzalna, dodatkowo okazuje się że kod ten należy często pisać ponieważ wiele klas wywołuje te metody w celu porównania elementów, przykładem takich klas są kontrolki WPF zawierające Listy jak np. ComboBox. Aby pracę tą trochę skrócić można użyć następującego fragmentu kodu:

    class User
    {
        /// <summary>
        /// Identyfikator
        /// </summary>
        public int ID { get; set; }

        // Inne pola

        private LambdaEqualityComparer<User, int> _comparer = new LambdaEqualityComparer<User, int>(u => u.ID);

        public override int GetHashCode()
        {
            return _comparer.GetHashCode(this);
        }

        public override bool Equals(object obj)
        {
            return _comparer.Equals(this, obj as User);
        }
    }

Aby ten kod działał w 100% poprawnie należy jeszcze zmodyfikować kod Maćka tak aby uwzględniała fakt iż któryś z parametrów metody Equals może być nullem.

class LambdaEqualityComparer<T, TValue> : IEqualityComparer<T> 
        { 
            #region Private members 
            private Func<T, TValue> converter; 
            #endregion Private members 

            #region Constructors 
            public LambdaEqualityComparer(Func<T, TValue> converter) 
            { 
                this.converter = converter; 
            } 
            #endregion Constructors 

            #region IEqualityComparer<T> Members 
            public bool Equals(T x, T y) 
            { 
                if (x == null && y == null)
                {
                      return true;
                 } 
                  else if (x == null || y == null)
                 {
                         return false;
                 }
                  else
                  {
                         return converter(x).Equals(converter(y)); 
                  }
            }
            public int GetHashCode(T obj) 
            {
                if (obj == null)
               {
                       return 0;
                }
                else
                { 
                        return converter(obj).GetHashCode(); 
                 }
            }
            #endregion 
        } 

Na koniec dodam że implementację tą można rozbudować zgodnie z wzorcem dekoratora, co daje nam możliwość wykorzystania istniejących implementacji IEqualityComparer'a.

class LambdaEqualityComparer<T, TValue> : IEqualityComparer<T> 
        { 
            #region Private members 
            private Func<T, TValue> converter; 
            private IEqualityComparer<TValue> realComparer;
            #endregion Private members 

            #region Constructors 
           public LambdaEqualityComparer(Func<T, TValue> converter) : this(converter, null) { }

            public LambdaEqualityComparer(Func<T, TValue> converter, IEqualityComarer<TValue> realComparer) 
            { 
                this.converter = converter; 
                this.realComparer = realComparer; 
            } 
            #endregion Constructors 

            #region IEqualityComparer<T> Members 
            public bool Equals(T x, T y) 
            { 
                if (x == null && y == null)
                {
                      return true;
                 } 
                  else if (x == null || y == null)
                 {
                         return false;
                 }
                  else
                  {
                         if  (realComparer == null)
                         {
                                  return converter(x).Equals(converter(y)); 
                          }
                          else
                          {
                                   return realComparer.Equals(converter(x), converter(y));
                           }
                  }
            }
            public int GetHashCode(T obj) 
            {
                if (obj == null)
               {
                       return 0;
                }
                else
                {
                       if (realComparer == null)
                       { 
                              return converter(obj).GetHashCode(); 
                        }
                         else
                         {
                                  return realComparer.GetHashCode(converter(obj));
                          }
                 }
            }
            #endregion 
        } 

Niestandardowe generowanie kluczy głównych w NHibernate

Robiąc ostatnio mały projekt postanowiłem w końcu wykorzystać NHibernate, na temat tego ORM'a jest w necie masa informacji więc nie będę się na jego temat rozpisywał. Podobnie nie będę opisywał konfiguratora Fluent NHibernate, który jest znacznie bardziej intuicyjny niż tradycyjne pliki hbm (oparte na XML).

Problemem przed jakim stanąłem był fakt że moja aplikacja miała współpracować z bazą na której działała inna aplikacja, posiadająca dosyć charakterystyczną politykę generowania kluczy głównych. Która nijak nie pasowała do żadnej standardowej polityki zawartej standardowo w Bibliotece hibernate'a.

Na szczęście NHibernate jest przygotowany na taką ewentualność, jego API zawiera interfejs IIDentifierGenerator, który umożliwia pisanie własnych polityk generowania kluczy. Interfejs ten zawiera jedną metodę Generate.

public class MyGenerator:IIDentiferGenerator
{
    public object Generate(ISessionImplementor session, Object obj)
   {
        return Guid.NewGuid()
    }
}

Pierwszy parametr zapewnia dostęp do obiektów związanych z konfiguracją połączenia z bazą danych oraz stanem tego połączenia, drugim parametrem jest obiekt dla którego generowany jest klucz. Przykład powyżej dla każdego obiektu zwróci wartość statycznej metody NewGuid która nadaje się na klucz główny ponieważ z definicji jest to Global Unique Identifier. Jednak generowanie nowego Guida nie jest niczym nadzwyczajnym po za tym biblioteka NHibernate'a już ją zawiera.

Częstym sposobem generowania kluczy głównych jest procedura składowana (lub fragment zapytania SQL) która zwraca najniższą nieużywaną wartość którą aplikacja może wykorzystać jako klucz dla danej tabeli.

Aby uruchomić takie zapytanie pod kontrolą NHibernate należy wykorzystać właściwości Connection i Batcher parametru session przekazanego do metody generate.

public class MyGenerator:IIDentiferGenerator
{
    public object Generate(ISessionImplementor session, Object obj)
   {
        using(IDbCommand command = session.Connection.GenerateCommand())
        {
            command.CommandText = "exec GetNextId()";
            using(IDataReader  reader = session.Batcher.ExecuteReader())
            {
                if (reader.Read())
                {
                     return reader.GetInt(0);
                }
                else
                {
                      throw new Exception ("Nie da się wygenerować klucza.");
                }
            }
        }
    }
}

Klasa przedstawiona powyżej jedną poważną wadę: jest bardzo sztywna tj. trudno byłoby ją wykorzystać do generowania klucza dla dowolnej encji. Informację na temat tabeli w której należy sprawdzać "Zajętość" danego identyfikatora można uzyskać tylko przez weryfikację typu parametru obj metody Generate. Wykorzystanie tego faktu sprawiłoby to że programista musiałby dla każdego obsługiwanego typu dopisywać kilka linijek kodu w tej metodzie co generowałoby masę powtarzalnego kodu.

Znacznie lepszym rozwiązaniem jest Implementacja Interfejsu IConfigurable zawierającego metodę Configure która przyjmuje jako parametry następujące wartości:

type – typ dla jakiego jest konfigurowana klasa

params – Słownik klucz <=> wartość przechowujący dodatkowe dane konfiguracyjne.

d – informacja na temat silnika bazodanowego przechowującego dane.

Parametrem nas interesującym jest params zawiera on dane na temat tabeli w której zapisywany jest dany typ oraz kolumny będącej kluczem głównym. Nie jestem pewien czy dla każdego silnika bazodanowego nazwy kluczy są takie same dlatego nie podaję tutaj konkretnego fragmentu kodu.

Pozostaje jeszcze tylko kwestia jak powiedzieć NHibernate'owi żeby dla danej klasy używał naszego generatora. We FluentHibernate wymaga to wpisania jednej linijki w klasie Mapującej:

Id(obj => obj.ID).GeneratedBy().Custom<MyGenerator>()

Edit: 02-10-2010

Podczas prac w NHibernate zauważyłem że zachowuje się on dosyć specyficznie podczas zapisu więcej niż jednego obiektu jedego typu w ciągu jednej tranzakcji. Otóż najpierw generuje on klucze główne dla wszystkich nowych obiektów, a następnie dokonuje ich zapisu. Jeżeli teraz oprzemy nasz mechanizm zapisu na zapytaniu do bazy to musimy się liczyć z faktem iż za każdym razem generowanie klucza będzie się odbywać na niezmienionej bazie. Więc zapytanie wywołane za każdym razem zwróci tą samą wartość. Należy to uwzględnić w kodzie, najczęściej przez duplikację części logiki zapytania, co jest oczywistą wadą tego rozwiązania, zaletą jego zaś jest mniejsza ilość zapytań co zazwyczaj poprawia aplikację pod względem wydajnosciowym.