Jasica.Net

C#, .Net, SQL i nie tylko

Szybki sposób na generowanie klas z JSON i XML

Zapewne nie raz spotkaliście się z potrzebą stworzenia klas odzwierciadlających pewne dane XML lub JSON. W takich przypadkach najlepiej posłużyć się odpowiednimi generatorami, by zaoszczędzić sporo czasu na tworzeniu kodu.

Pomocny może być dodatek Web Essentials 2012. Dzięki niemu teraz zawsze będziemy mieli taki generator pod ręką- dosłownie. Wystarczy kliknąć prawym przyciskiem myszy odpowiednią opcje z menu kontekstowego i JSON ze schowa zamieni się w piękną klasę:

 

W analogiczny sposób można też obsłużyć XMLa.

Polecam zapoznanie się z pozostałymi możliwościami dodatku.

MSDeploy(aka WebDeploy) - problem po aktualizacji do .Net 4.5

Migracja do .Net 4.5, mimo iż miał być bezproblemowa, czasem stwarza problemy. Jednym z narzędzi jakie nie chciało od razu współpracować z .Net 4.5 jest MSDeploy(aka WebDeploy).

Po migracji projektu do .Net 4.5 MSDeploy podczas publikacji paczki na IIS, zgłaszał błąd:

Error Code: 51
More Information: The application pool that you are trying to use has the
'managedRuntimeVersion' property set to 'v4.0'. This application requires 'v4.5'.
Error count: 1.

Mało eleganckim obejściem jest oszukanie MSDeploy'a poprzez zmianę wersji .Net przed publikacją przy pomocy przełącznika preSync:

-preSync:runCommand="%systemroot%\system32\inetsrv\APPCMD stop apppool P  
 & %systemroot%\system32\inetsrv\APPCMD set apppool P /managedRuntimeVersion:v4.5"

Wykonanie tego polecenia wprowadzi zmiany w ustawieniach puli, a nawet nowa wersja frameworka będzie dostępna do wyboru na liście:

 

Oczywiście, tak ustawiona pula nie będzie działać poprawnie, więc należy przywrócić ustawienia pierwotne, prosząc MSDeploy'a by wykonał na zakończenie porządki:

-postSync:runCommand="%systemroot%\system32\inetsrv\APPCMD set apppool P /managedRuntimeVersion:v4.0  
 & %systemroot%\system32\inetsrv\APPCMD start apppool P

Zna ktoś inne rozwiązanie tego problemu?

SignalR - czyli jak łatwo przesłać informacje o zdarzeniu do klienta

Cykliczne odpytywanie serwera może być problematyczne i mało wydajne. Istnieje jednak biblioteka SignalR, która znacząco upraszcza przesyłanie informacji o zdarzeniach z serwera do przeglądarki.

Biblioteka bardzo dużo odwala za programistę- przykładowy czat można napisać używając zaledwie kilkudziesięciu lini kodu. Zachęcam do zapoznania się przykładami.

Warto też zauważyć, że biblioteka nie ogranicza nas co do stosowanych technologi- klientem nie musi być przeglądarka(może to być dowolna aplikacja .Net'owa), a serwer nie musi wykorzystywać Asp.Net'owych handlerów do działania- możemy hostować serwer w dowolnej aplikacji, choćby w konsoli.

Jedyną wadą biblioteki jest to, że nie wspiera standardowo skalowania na wiele węzłów, a tym samym zwiększonej niezawodności. Można to jednak uzyskać poprzez wykorzystanie Windows Azure Service Bus, przez co sam projekt wydaje się być obiecujący. Wsparcie biblioteki nie ogranicza się tylko do okolic platformy .Net. Na githubie można znaleźć biblioteki mający na celu dodanie wsparcia dla iOS i Mac, a także Androida.

Biblioteka posiada całkiem spore możliwości, a na dodatek całkiem silne wsparcie społeczności. Uważam, że z tych wszystkich powodów warto poświecić jej chwile i zapoznać się z nią.

Tworzenie obiektów bez wykonywania konstruktora

Tworzenie obiektów w .Net jest utożsamiane z zarezerwowaniem dla nich pewnej pamięci, a następnie wykonaniem konstruktora(domyślnego lub parametryzowanego). Jednak w pewnych przypadkach wykonywanie kodu zawartego w konstruktorze może być niepożądane. Istnieje jednak sposób by tego uniknąć.

Brak angażowania logiki w konstruktorze jest zazwyczaj pożądany podczas odtwarzania obiektu np. po odczycie danych z dysku twardego lub danych przesłanych przez sieć. Dodatkowo, jeśli obiekt posiada bezparametryczny konstruktor utworzenie obiektu danego typu przy pomocy refleksji nie jest trudne. Sprawa staje się bardziej skomplikowana jeśli obiekt wymaga do inicjalizacji innych danych. W takim przypadku z pomocą przychodzi statyczna metoda FormatterServices.GetUninitializedObject, która jako jedyny parametr przyjmuje typ obiektu jaki chcemy utworzyć. Należy uważać z jej użyciem, gdyż tworzy ona obiekt, którego przestrzeń w pamięci jest wypełniona samymi zerami. Może implikować stan obiektu, który nie jest dla niego dozwolony i może objawiać się niepożądanym działaniem . Taki obiekt należy jak najszybciej wypełnić danymi przed jego wykorzystaniem. Przykład użycia:

    public class Foo
    {
        private int bar;
        private int baz;

        public Foo(int bar)
        {
            this.bar = bar;
            this.baz = 8;
        }

        public override string ToString()
        {
            return string.Format("bar={0}, baz={1}", this.bar, baz);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var test = (Foo)FormatterServices.GetUninitializedObject(typeof(Foo));
            Console.WriteLine(test);
        } 
    }

Na ekranie, zgodnie z oczekiwaniami, zostanie wypisana informacja, z której wynika, że obiekt nie został zainicjalizowany- "bar=0, baz=0".

Samo działanie metody jest interesujące- jak obchodzi ona taki element języka jak wywołanie konstruktora? Dekompilując jej kod nie dowiemy się dużo:

    [SecurityCritical]
    public static object GetUninitializedObject(Type type)
    {
      if (type == null)
        throw new ArgumentNullException("type");
      if (type is RuntimeType)
        return FormatterServices.nativeGetUninitializedObject((RuntimeType) type);
      throw new SerializationException(Environment.GetResourceString(
        "Serialization_InvalidType", 
        new object[1] { (object) type.ToString() }));
    }

    [MethodImpl(MethodImplOptions.InternalCall)]
    [SecurityCritical]
    private static extern object nativeGetUninitializedObject(RuntimeType type);

Oznaczenie metody jako extern wraz z MethodImplOptions.InternalCall mówi, że metoda jest zaimplementowana wewnątrz CLR. Jeśli chcemy pogrzebać głębiej zawsze można zerknąć na kod SSCLI

FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE) {
    CONTRACTL {
        THROWS;
        DISABLED(GC_TRIGGERS);
        MODE_COOPERATIVE;
        SO_TOLERANT;
    }
    CONTRACTL_END;
    
    OBJECTREF           retVal  = NULL;
    REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF) objTypeUNSAFE;

    HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_NOPOLL(Frame::FRAME_ATTR_RETURNOBJ);

    if (objType == NULL) {
        COMPlusThrowArgumentNull(L"type", L"ArgumentNull_Type");
    }

    TypeHandle type = objType->GetType();

    // Don't allow arrays, pointers, byrefs or function pointers.
    if (!type.IsUnsharedMT())
        COMPlusThrow(kArgumentException, L"Argument_InvalidValue");

    MethodTable *pMT = type.GetMethodTable();
    PREFIX_ASSUME(pMT != NULL);

    //We don't allow unitialized strings.
    if (pMT == g_pStringClass) {
        COMPlusThrow(kArgumentException, L"Argument_NoUninitializedStrings");
    }

    // if this is an abstract class or an interface type then we will
    //  fail this
    if (pMT->IsAbstract()) {
        COMPlusThrow(kMemberAccessException,L"Acc_CreateAbst");
    }
    else if (pMT->ContainsGenericVariables()) {
        COMPlusThrow(kMemberAccessException,L"Acc_CreateGeneric");
    }

    // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired
    // transparent proxy or the jit will get confused.
    if (pMT->IsContextful())
        COMPlusThrow(kNotSupportedException, L"NotSupported_ManagedActivation");

    // If it is a nullable, return the underlying type instead.  
    if (Nullable::IsNullableType(pMT)) 
        pMT = pMT->GetInstantiation()[0].GetMethodTable();
 
    retVal = pMT->Allocate();

    HELPER_METHOD_FRAME_END();
    return OBJECTREFToObject(retVal);
}
FCIMPLEND

Jest tu sprawdzana cała masa warunków, aż ostatecznie alokowana jest po prostu pamięć - nie ma tu żadnego wołania konstruktora.