EntityFramework i wartość domyślna wyliczana na podstawie funkcji

Domyślne wartości bardzo łatwo ustawić za pomocą wizualnego edytora EntityFramework. Wystarczy ustawić właściwość Default w oknie properties:

Clipboard01

Co jednak gdy chcemy ustawić wartość wyliczoną na podstawie jakieś funkcji? Dla przykładu może być to DateTime.Now bądź też Guid.NewGuid()?Wpisując w te same okienko dostaniemy błąd podczas kompilacji:

Error    1    Error 54: Default value (System.Guid.NewGuid()) is not valid for GUID. The value must be enclosed in single quotes in the form 'dddddddd-dddd-dddd-dddd-dddddddddddd'.    Persons.edmx    446    11    Crm

Na szczęście rozwiązanie jest bardzo proste – wystarczy stworzyć klasę Partial i w konstruktorze ustawiać wartość domyślną:

public partial class Person
{
    public Person()
    {
        this.ID_PERSON = Guid.NewGuid();
    }
}

Takim sposobem możemy manipulować danymi w dowolny sposób. Wszelkie konwersje danych nie są teraz problemem.

ComboBox w DataGrid oraz brak wsparcia SelectedValue w Silverlight

Siliverlight niestety nie jeszcze tak potężną biblioteką jak WPF. Czasami proste rzeczy wymagają większego nakładu pracy niż w przypadku tworzenia aplikacji w WPF.

Rozważmy sytuację, w której mamy listę telefonów do danej osoby.Telefon definiujemy poprzez jego numer oraz przydzieloną kategorię(dom, praca itp). W DataGrid będą więc 2 kolumny: jedna typu TextBox dla numeru oraz druga typu ComboBox dla kategorii. DataGrid będzie przedstawiał listę telefonów opisanych klasą Phone:

class Phone
{
    public string Value{get;set;}
    public PhoneType PhoneType{get;set;}
}

gdzie PhoneType to:

class PhoneType
{
    public string Value{get;set;}// nazwa kategorii
    public Guid ID_PHONE_TYPE{get;set;}
}

Zrzut z gotowej kontrolki DataGrid:

Clipboard01

W wpf kod byłby bardzo prosty a mianowicie:

<data:DataGrid   Name="m_Phones" ItemsSource="{Binding Path=Contact.Phones}" AutoGenerateColumns="False">
    <data:DataGrid.Columns>
                           
        <data:DataGridTextColumn Header="Phone" Binding="{Binding Path=Value}"/>
                            
            <data:DataGridTemplateColumn Header="PhoneGroup">
                <data:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox SelectedValuePath="ID_PHONE_TYPE" SelectedValue="{Binding Path=ID_PHONE_TYPE"  DisplayMemberPath="Value" ItemsSource="{StaticResource AllPhoneTypes}">
                           </ComboBox>
                    </DataTemplate>
                </data:DataGridTemplateColumn.CellTemplate>

        </data:DataGridTemplateColumn>

    </data:DataGrid.Columns>
</data:DataGrid>

SelectedValuePath wskazuje na identyfikator item’a w ComboBox. SelectedValue służy do zaznaczania aktualnej kategorii.Wszystko w wpf ładnie będzie nam się odświeżać. Załadowane telefony automatycznie będą zaznaczać kategorię w ComboBox.

Niestety w Silverlight brakuje właściwości SelectedValue oraz SelectedValuePath.Wszystko co możemy zrobić za pomocą xaml to:

<data:DataGrid   Name="m_Phones" ItemsSource="{Binding Path=Contact.Phones}" AutoGenerateColumns="False">
    <data:DataGrid.Columns>
                           
        <data:DataGridTextColumn Header="Phone" Binding="{Binding Path=Value}"/>
                            
            <data:DataGridTemplateColumn Header="PhoneGroup">
                <data:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox DisplayMemberPath="Value"    ItemsSource="{StaticResource AllPhoneTypes}">
                           </ComboBox>
                    </DataTemplate>
                </data:DataGridTemplateColumn.CellTemplate>

        </data:DataGridTemplateColumn>

    </data:DataGrid.Columns>
</data:DataGrid>

W SL do dyspozycji mamy wyłącznie właściwość SelectedItem jeśli chodzi o zaznaczanie pozycji w ComboBox. Niestety ItemsSource dla ComboBox pochodzi z innego źródła niż aktualnie wybrana kategoria. Nie możemy zatem użyć poniższego kodu:

<ComboBox SelectedItem="{Binding Path=PhoneType}" DisplayMemberPath="Value"  ItemsSource="{StaticResource AllPhoneTypes}"/>

Musimy powiązać SelectedItem za pomocą ID_PHONE_TYPE a nie instancji klasy PhoneType.Problem polega na tym, że SelectedItem spodziewa się typu PhoneType a nie GUID. Co więc możemy zrobić?

Na szczęście  SilverLight wspiera konwertery IValueConverter. Umożliwiają one podczas bindingu zamianę jednej wartości na drugą. W naszym przypadku zamienimy GUID na klasę PhoneType. Zatem do dzieła, zacznijmy od implementacji tego konwertera:

public class PhoneTypeConverter : IValueConverter
{
    public ObservableCollection<PhoneType> ItemsSource { get; set; }
    
    public object Convert(object value,
    System.Type targetType,
    object parameter,
    System.Globalization.CultureInfo culture)
    {
        Guid phoneTypeId = (Guid)value;
        PhoneType phoneType = null;
        
        foreach (PhoneType item in ItemsSource)
        {
            if (item.ID_PHONE_TYPE == phoneTypeId)
            {
                phoneType = item;
            }
        }
        return phoneType;
    }
        
    public object ConvertBack(object value,
    System.Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
    {
        PhoneType phoneType = value as PhoneType;
        Guid phoneTypeId = Guid.Empty;
        phoneTypeId = phoneType.ID_PHONE_TYPE;
        return phoneTypeId;
    }
}

Konwerter zawiera dwie metody Convert(Guid –> PhoneType) oraz ConvertBack(PhoneType –> Guid).Teraz możemy już użyć SelectedItem:

<ComboBox SelectedItem="{Binding Path=ID_PHONE_TYPE,Converter={StaticResource PhoneTypeConverter}}" DisplayMemberPath="Value"  ItemsSource="{StaticResource AllPhoneTypes}"/>

Konwerter zamieni Guid(ID_PHONE_TYPE) na odpowiednią  instancję PhoneType, która jest już podłączona do ItemsSource. Ostatnią sprawą jest zdefiniowanie zasobu PhoneTypeConverter:

<UserControl.Resources>
   <local:PhoneTypeConverter x:Key="PhoneTypeConverter" ItemsSource={StaticResource AllPhoneTypes} />        
</UserControl.Resources>

Takim to sposobem poradziliśmy sobie z brakiem wsparcia dla SelectedValue w Silverlight. Mam nadzieję, że oszczędziłem komuś męczarni z tym – samemu sporo sie nakombinowałem zanim udało mi się osiągnąć zamierzony  efekt.Dodam, że SilverLight 4 Beta posiada już właściwość SelectedValue więc takie scenariusze będą łatwiejsze w implementacji.

Prawidłowe definiowanie wyjątków w c#

Programując własne biblioteki, często potrzebujemy zdefiniować własny typ wyjątku. Przeglądając różnego rodzaju kody źródłowe nierzadko spotykam błędną deklarację własnych wyjątków:

 public class MyException : Exception
 {
    // specyfikacja
 }

Co prawa kompilator nie zgłosi błędu ale już np. CodeAnalysis zwróci nam uwagę o błędnej deklaracji. Najprościej korzystać w Visual Studio z tzw. snippet’ów czyli gotowych fragmentów kodu. Naciskając klawisze ctrl+space pojawi nam się lista dostępnych snippetów w VS:

snippetsWybieramy oczywiście Exception a automatycznie zostanie wstawiony poniższy fragment kodu:

    [Serializable]
    public class MyException : Exception
    {
        public MyException() { }
        public MyException(string message) : base(message) { }
        public MyException(string message, Exception inner) : base(message, inner) { }
        protected MyException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

Jak widać deklaracja jest bardziej złożona ponieważ prawidłowa implementacja wyjątku wymaga uwzględnienia mechanizmu serializacji czyli zapisu klasy np. do pliku XML. Potrzebne jest to do wysyłania klasy zdalnie przez sieć.Wyobraźmy sobie sytuacje w której nasza biblioteka zostaje wyeksponowana za pomocą WCF. W takim przypadku jeśli chcemy zwracać wyjątki do klienta muszą one być zapisane do jakiegoś formatu(np. XML) i zwrócone zdalnie klientowi.

Seria webcastów o Visual Studio 2010

Zapraszam wszystkich na webcasty poświecone VSTS. Rejestracja jest darmowa więc tym bardziej warto skorzystać z okazji. Oto lista najbliższych spotkań:

Styczeń 26, 2010: What’s New in VS2010 (Webcast) 
Luty 23, 2010: VSS to TFS: Strategies for Migration 
Marzec 23, 2010: What’s New in VS2010 
Kwiecień 20, 2010: VS2010 Quality Tools for Developers 
Maj 18, 2010: Lab Manager – The Ultimate "No More No Repro" Tool 
Czerwiec 14, 2010: Full Testing Experience: Professional QA with VS2010

Więcej informacji znajdziecie tutaj. Podziękowania także dla Anorak’a za informacje o tych wydarzeniach.

Silverlight i brak wsparcia dla x:Static

Programiści WPF z pewnością kojarzą makro x:Static służące do bindowania m.in. stałych. Bardzo przydatne jest to w przypadku gdy mamy klasę stałych, którą chcemy przypisać pewnym właściościom kontrolek. W PRISM, wykorzystuje się to do definiowania nazw regionów:

<ItemsControl regions:RegionManager.RegionName="{x:Static slbase:RegionNames.NoteView}">

Dzięki takiemu rozwiązaniu nie musze martwić się o aktualziacje nazwy regionu w pliku xaml.

Ostatnio piszę projekt w Silverlight i spotkało mnie niemiłe zaskoczenie gdy okazało się, że SL nie wspiera takiego makra. W przykładach dołączonych do frameworku PRISM autorzy na sztywno podają nazwy. Znalazłem jednak lepsze moim zdaniem rozwiązanie. Możemy uzyć zasobów a mianowicie:

<controls:TabControl cal:RegionManager.RegionName="{Binding Note, Source={StaticResource Regions}}">

W powyższym kodzie bindujemy właściwość Note do RegionName. Wszystko realizowane jest również w sposób statyczny. Wystarczy, że zdefiniujemy jeszcze w ResourceDictionary następujący znacznik:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:slbase="clr-namespace:SlBase.Mvvm;assembly=SlBase">

    <slbase:Regions x:Key="RegionsBase"/>
</ResourceDictionary>

Nie jest to rozwiązanie idealne ale wystarczające. Zasoby możemy zdefiniować w osobnym pliku i  dołączać je w ramach potrzeb co redukuje ilość kodu.

Startujemy

Witam,

Po długim namyśle postanowiłem zacząć prowadzić blog techniczny. Blog poświecony jest głównie szeroko pojętej technologii .NET. Z pewnością nie zabraknie jednak informacji o wzorcach projektowych oraz o pisaniu porządnego kodu. Będę również starał się prezentować frameworki, które ułatwiają pisanie aplikacji klasy enterprise. Planuję także opisywać techniki optymalizacji kodu c# ponieważ uważam, że informację takie  zbyt często są omijane w publikacjach.

Zachęcam więc do regularnego czytania!