Archive for the ‘.NET 4’ Category

Gondolatok a queue alapú kliens-szerviz kommunikációhoz

Thursday, June 16th, 2011

Az előző post kommentjei alapján (amit nagyon köszönök mindenkinek) nem kaptam sok bátorítást az aszinkron, queue alapú, kérés-választ különválasztó gazdag kliens - szerviz kommunikációhoz, úgy tűnik senki nem csinált ilyet, így nem akarok úttörő lenni a témában.
Ehhez még hozzájárul, hogy hétvégén méregettem az MSMQ teljesítményét. Azért ezt, mert a WCF is erre épít, és pl. az NServiceBus is.

A tesztkód 1.5kByteos üzeneteket rak át egyik sorból a másikba. Az ötlet innen jött, csak többszálasítottam.

A tesztkód:


using System;
using System.Diagnostics;
using System.Messaging;
using System.Threading;

namespace MsmqTran
{
    class Program
    {
        private const int NumberOfTests = 1000;
        private const int MaxDop = 10;
        private static readonly ManualResetEvent[] WaitForEmpty = new ManualResetEvent[MaxDop];

        static void Main()
        {
            var q1 = new MessageQueue(@".\private$\test_queue1");
            var q2 = new MessageQueue(@".\private$\test_queue2");

            Console.WriteLine("Filling source queue...");
            var b = new byte[1500];
            using (var msmqTx = new MessageQueueTransaction())
            {
                msmqTx.Begin();
                for (int i = 0; i < NumberOfTests; i++)
                {
                    q1.Send(b, msmqTx);
                }
                msmqTx.Commit();
            }

            q2.Purge();
            Console.WriteLine("Starting to move data from source queue to destination queue");

            var sp = Stopwatch.StartNew();

            for (int i = 0; i < MaxDop; i++)
            {
                WaitForEmpty[i] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(o => ProcessMsg(q1, q2, (ManualResetEvent)o), WaitForEmpty[i]);
            }

            WaitHandle.WaitAll(WaitForEmpty);

            Console.WriteLine("Duration: {0}ms, throughput: {1:F0} messages/s", sp.ElapsedMilliseconds, 1000.0 * NumberOfTests / sp.ElapsedMilliseconds);
        }

        private static void ProcessMsg(MessageQueue q1, MessageQueue q2, ManualResetEvent w)
        {
            while (true)
            {
                using (var msmqTx = new MessageQueueTransaction())
                {
                    msmqTx.Begin();

                    Message message;
                    try
                    {
                        message = q1.Receive(TimeSpan.FromMilliseconds(0), msmqTx);
                    }
                    catch (MessageQueueException e)
                    {
                        Console.WriteLine(e);
                        w.Set();
                        break;
                    }

                    q2.Send(message, msmqTx);

                    msmqTx.Commit();
                }
            }
        }
    }
}

A gépemen 50 tran/sec-kel megy 1 szálon, és 200 fölé nem nagyon megy. Jó, ez laptop, de relációs adatbáziskezelővel (sql server és oracle is fut a gépen) több ezer tran/seccel mennek a dolgok. Szóval ez elég gázosan lassú. Emellett csúnya leállásokról is írnak a blogokban, amikor beáll az msmq.

Marad a aszinkronított WCF egyelőre, csak a szerverről visszafelé hívásokat tervezem queue alapon megcsinálni, WCF msmq bindinggal. Így tudom értesíteni az appokat polling nélkül. Erre 3 okom van most:
1. Az offline (disconnected) pessimistic lock feloldódott, lehet szerkeszteni valamit.
2. Frissíteni kell a kliens cache-ben valamit.
3. Email jellegű üzenetküldés az appok között.

Köszönöm még egyszer az építő javaslatokat.

Érdekes .NET perf tapasztalat

Saturday, March 5th, 2011

Amikor profilerrel megnézünk egy .NET kódot sokszor megdöbbentő helyen lesz benne bottleneck.

Az alábbi kód 1% időt visz el egy nagyon processzorintenzív kódban:


if (bar.L == 0)

Ami ebben lassú, az a System.Decimal.op_Implicit(int32). A bar.L egy decimal. Érdekes, mi?
Mi a megoldás? A 0 legyen valóban decimal, de int, amit konvertálni kell:


if (bar.L == 0M)

1% kevés, de sok 1% már számít.

C# 4 Covariance

Tuesday, February 1st, 2011

.NET 4-es tanfolyamhoz írok egy prezentációt. A co és -contravarianciát próbálom érhetővé tenni. Kb. ez van a slideokon:

Variance annotations – covariance 1.


class Allat { }
class Kutya : Allat { }

interface IAllatGyar<T> where T : Allat
{
    T Create();
}
class AllatGyar<T> : IAllatGyar<T> where T : Allat
{
    public T Create()
    {
        return default(T);
    }
}
AllatGyar<Kutya> kutyagyar = new AllatGyar<Kutya>();
IAllatGyar<Allat> allatGyar = kutyagyar; //cast, jogos?
Allat a = allatGyar.Create(); //mit ad vissza, típusbiztos?

Type ordering
Kutya <: Allat - A Kutya az Allat osztály leszámazottja
k : Kutya - k a Kutya típus egy példánya
Ha k : Kutya és Kutya <: Allat, akkor k : Allat (Liskov féle helyettesítési elv)
Igaz-e, hogy ha Kutya <: Allat, akkor IAllatgyar< Kutya > <: IAllatgyar< Allat > ?
Egy Kutya példány implicit castolható Allattá. Az Allatgyar< Kutya > típusbiztosan castolható-e Allatgyar< Allat >-tá?
Ha csak kimeneti paraméterként vagy visszatérési értékként jön ki T, akkor igen, mert Kutya <: Allat, így minden kimeneti paraméternél a Allat referenciával biztonságosan elérhető egy leszármazott Kutya példány is.
Ha igen, akkor T covariant, mert a típusparaméterek type orderingje érvényes a típust használó generikus típusra is, ugyanabban a sorrendben (Kutya <: Allat ==> IAllatgyar< Kutya > <: IAllatgyar< Allat >).
C# 4-ben az interfész metódus vagy delegate szignatúrában az out kulcsszó jelzi ezt:


interface IAllatgyar<out T>.

Amikor ez nem megy:


class Allat { }
class Kutya : Allat { }

interface IAllatGyar<T> where T : Allat
{
    T Create();
    void Valami(T t); //T befelé megy!
}

AllatGyar<Kutya> kutyagyar = new AllatGyar<Kutya>();
IAllatGyar<Allat> allatGyar = kutyagyar;

kutyagyar.Valami(new Kutya()); //Ez nyilván ok

allatGyar.Valami(new Lo());    //És ez???

Ha bemeneti paraméter is T, akkor az IAllatgyar< Allat > statikus típuson, amely valójában a IAllatgyar< Kutya > dinamikus típusra mutat átadható lenne más állat is, pl. Ló, amely szintén Allat leszármazott, de az nyilvánvaló runtime hibát okozna. Ezért nem lehet covariant T, ha az interfészen bemenetként is szerepel T.

Érhető ez így? Bármilyen javaslatot szívesen fogadok. A delegatekhez is írok persze anyagot, ez csak az eleje.

Csúnya multithreading hiba

Friday, January 21st, 2011

Double checked lockingnak indult, de bug lett belőle. Mit rontottam el?


private static volatile Dictionary<DateTime, Pair<TimeSpan>> holidayDays;
private static readonly object staticLock = new object();

private Dictionary<DateTime, Pair<TimeSpan>> GetHolidayDays()
{
    if (holidayDays == null)
    {
        lock (staticLock)
        {
            if (holidayDays == null)
            {
                holidayDays = new Dictionary<DateTime, Pair<TimeSpan>>();
                FillTradingHours(holidayDays, "HOL");
            }
        }
    }
    return holidayDays;
}

Dynamic sebesség újra

Monday, May 17th, 2010

Hogy ne a levegőbe beszéljek:


using System;
using System.Diagnostics;
using System.Reflection;

namespace DynamicTypeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            object target = "alma";
            object arg = "m";

            string a2 = (string)arg;

            Stopwatch w = Stopwatch.StartNew();

            const int callNumber = 1000 * 1000;
            for (int i = 0; i < callNumber; i++)
            {
                Type[] argTypes = new Type[] { typeof(string) };
                MethodInfo mi = target.GetType().GetMethod("Contains", argTypes);
                object[] oa = new object[] { a2 };
                bool b = (bool)mi.Invoke(target, oa);
            }

            w.Stop();
            Console.WriteLine("Reflection hívási idő: {0}", w.Elapsed);
            double elapsedTickForReflection = w.ElapsedTicks;

            w.Restart();

            for (int i = 0; i < callNumber; i++)
            {
                bool b = ((dynamic)target).Contains(a2);
            }

            w.Stop();
            Console.WriteLine("Dynamic hívási idő: {0}", w.Elapsed);

            Console.WriteLine("A dynamic {0}x gyorsabb volt.", elapsedTickForReflection / w.ElapsedTicks);
        }
    }
}

Reflection hívási idő: 00:00:02.5393161
Dynamic hívási idő: 00:00:00.2049100
A dynamic 12.3923444658829x gyorsabb volt.

Ha ügyesebbek vagyunk, és kivesszük a ciklusból a reflection előkészítését:


using System;
using System.Diagnostics;
using System.Reflection;

namespace DynamicTypeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            object target = "alma";
            object arg = "m";

            string a2 = (string)arg;

            Stopwatch w = Stopwatch.StartNew();

            Type[] argTypes = new Type[] { typeof(string) };
            MethodInfo mi = target.GetType().GetMethod("Contains", argTypes);
            object[] oa = new object[] { a2 };

            const int callNumber = 1000 * 1000;
            for (int i = 0; i < callNumber; i++)
            {
                bool b = (bool)mi.Invoke(target, oa);
            }

            w.Stop();
            Console.WriteLine("Reflection hívási idő: {0}", w.Elapsed);
            double elapsedTickForReflection = w.ElapsedTicks;

            w.Restart();

            for (int i = 0; i < callNumber; i++)
            {
                bool b = ((dynamic)target).Contains(a2);
            }

            w.Stop();
            Console.WriteLine("Dynamic hívási idő: {0}", w.Elapsed);

            Console.WriteLine("A dynamic {0}x gyorsabb volt.", elapsedTickForReflection / w.ElapsedTicks);
        }
    }
}

Reflection hívási idő: 00:00:01.2058747
Dynamic hívási idő: 00:00:00.2044023
A dynamic 5.89951388765798x gyorsabb volt.

Jó ez, szeretjük, pedig nem is COMolunk.

Miért szeretem a dynamic típust reflection helyett?

Monday, May 17th, 2010

Ezért:


var allEntities = (IEnumerable)reposType.GetMethod("GetAll", new Type[] { typeof(string[]) }).Invoke(repos, new object[] { includes });

vs.


var allEntities = (IEnumerable)repos.GetAll(includes);

Emellett a dynamic vagy 10x gyorsabb, még akkor is, ha a reflectionnél cachelem a típusleírókat.

Parallel Stacks a VS 2010-ben

Thursday, May 13th, 2010

Jó ez a VS 2010. Belefutottam egy problémába, amiben a Parallel stack segítsége jól jött.

Amint a képen látható az egyik szál bejár egy Dictionary-t serializálás miatt, miközben a másik beleenyúl, ettől aztán elszakad a cérna az iterátornál. Látható a képen, hogy két SaveSettings egyszerre fut, de csak az egyik seralizál, a másik, a jobbról látható vár, mert raktam be lockot.
Igen, ám, de rossz helyre! A kollekciót engedem módosítani (this[string].set), mert az a lockoláson kívül esik. Azaz a probléma a rosszul megválasztott zárolási szint (lock scope). Egy metódushívással kijjebb kell rakni, az indexerbe.

Walkthrough: Profiling With Automated Tests

Sunday, May 9th, 2010

Egyszerű, ha tudod hol kell keresni.

Entity Framework 4 többrétegű appokban

Tuesday, April 20th, 2010

Játszottam kicsit az EF4-gyel. Az alábbi kód egy n rétegű app adatmozgását szimulálja a WCF xml szerializálóját használva. Mindhárom template-tel kipróbáltam, alább láthatóak az adatmozgások.
A tesztkód messze nem korrekt, de kiindulópontként további vizsgálatokhoz elfogatható:


using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;

namespace POCO1
{
    class Program
    {
        static void Main()
        {
            Department d;
            using (var e = new SchoolEntities())
            {
                e.ContextOptions.ProxyCreationEnabled = false;
                e.ContextOptions.LazyLoadingEnabled = false;
                d = e.Departments.Include("Courses").Single(dep => dep.DepartmentID == 1);
                Console.WriteLine("{0}", d.Name);
                Console.WriteLine("---------------------");
                foreach (Course c in d.Courses)
                {
                    Console.WriteLine("{0}", c.Title);
                }
                e.Detach(d);
            }

            var ser = new DataContractSerializer(d.GetType());
            //var ser = new DataContractSerializer(d.GetType(),
            //null, 50000, true, true, null, new ProxyDataContractResolver());

            using (var s2c = new FileStream(@"c:\temp\Server2Client.xml", FileMode.Create, FileAccess.ReadWrite))
            {
                //1. Server küld kliensre
                ser.WriteObject(s2c, d);
                s2c.Position = 0;
                //2. Kliens deserializál
                var clientSideDep = (Department)ser.ReadObject(s2c);

                //Csak ST
                //bool ce = clientSideDep.ChangeTracker.ChangeTrackingEnabled;

                //3. Kliens módosít
                clientSideDep.Name += "a";

                using (var c2s = new FileStream(@"c:\temp\Client2Server.xml", FileMode.Create, FileAccess.ReadWrite))
                {
                    //4. Kliens visszaküld
                    ser.WriteObject(c2s, clientSideDep);
                    c2s.Position = 0;

                    //5.Server deserializál
                    var sentBackDepartment = (Department)ser.ReadObject(c2s);
                    using (var e = new SchoolEntities())
                    {
                        //6. Server visszamódosít

                        //Normál entitás
                        e.Departments.Attach(sentBackDepartment);
                        e.ObjectStateManager.GetObjectStateEntry(sentBackDepartment).SetModified();
                        e.ObjectStateManager.GetObjectStateEntry(sentBackDepartment).SetModifiedProperty("Name");

                        //POCO
                        //e.Departments.Include("Courses").Single(dep => dep.DepartmentID == 1);
                        //e.Departments.ApplyCurrentValues(sentBackDepartment);

                        //Self-tracking entity
                        //e.Departments.ApplyChanges(sentBackDepartment);

                        e.SaveChanges();
                    }
                }
            }
        }
    }
}

EF alapobjektumok szerviz => kliens:


<Department z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/POCO1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <EntityKey z:Id="i2" xmlns="http://schemas.datacontract.org/2004/07/System.Data.Objects.DataClasses" xmlns:a="http://schemas.datacontract.org/2004/07/System.Data">
    <a:EntityContainerName>SchoolEntities</a:EntityContainerName>
    <a:EntityKeyValues>
      <a:EntityKeyMember>
        <a:Key>DepartmentID</a:Key>
        <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">1</a:Value>
      </a:EntityKeyMember>
    </a:EntityKeyValues>
    <a:EntitySetName>Departments</a:EntitySetName>
  </EntityKey>
  <Administrator>2</Administrator>
  <Budget>350000.0000</Budget>
  <Courses/>
  <DepartmentID>1</DepartmentID>
  <Name>Engineering</Name>
  <StartDate>2007-09-01T00:00:00</StartDate>
</Department>

EF alapobjektumok kliens => szerviz:


<Department z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/POCO1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <EntityKey z:Id="i2" xmlns="http://schemas.datacontract.org/2004/07/System.Data.Objects.DataClasses" xmlns:a="http://schemas.datacontract.org/2004/07/System.Data">
    <a:EntityContainerName>SchoolEntities</a:EntityContainerName>
    <a:EntityKeyValues>
      <a:EntityKeyMember>
        <a:Key>DepartmentID</a:Key>
        <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">1</a:Value>
      </a:EntityKeyMember>
    </a:EntityKeyValues>
    <a:EntitySetName>Departments</a:EntitySetName>
  </EntityKey>
  <Administrator>2</Administrator>
  <Budget>350000.0000</Budget>
  <Courses/>
  <DepartmentID>1</DepartmentID>
  <Name>Engineeringa</Name>
  <StartDate>2007-09-01T00:00:00</StartDate>
</Department>

POCO szerviz => kliens:


<Department xmlns="http://schemas.datacontract.org/2004/07/POCO1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Administrator>2</Administrator>
  <Budget>350000.0000</Budget>
  <Courses/>
  <DepartmentID>1</DepartmentID>
  <Name>Engineeringa</Name>
  <StartDate>2007-09-01T00:00:00</StartDate>
</Department>

POCO kliens => szerviz:


<Department xmlns="http://schemas.datacontract.org/2004/07/POCO1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Administrator>2</Administrator>
  <Budget>350000.0000</Budget>
  <Courses/>
  <DepartmentID>1</DepartmentID>
  <Name>Engineeringaa</Name>
  <StartDate>2007-09-01T00:00:00</StartDate>
</Department>

Self-tracking entity, szerviz => kliens:


<Department z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/POCO1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <Administrator>2</Administrator>
  <Budget>350000.0000</Budget>
  <ChangeTracker z:Id="i2">
    <ExtendedProperties/>
    <ObjectsAddedToCollectionProperties/>
    <ObjectsRemovedFromCollectionProperties/>
    <OriginalValues/>
    <State>Unchanged</State>
  </ChangeTracker>
  <Courses/>
  <DepartmentID>1</DepartmentID>
  <Name>Engineeringaaaaaaaa</Name>
  <StartDate>2007-09-01T00:00:00</StartDate>
</Department>

Self-tracking entity, kliens => szerviz:


<Department z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/POCO1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <Administrator>2</Administrator>
  <Budget>350000.0000</Budget>
  <ChangeTracker z:Id="i2">
    <ExtendedProperties/>
    <ObjectsAddedToCollectionProperties/>
    <ObjectsRemovedFromCollectionProperties/>
    <OriginalValues/>
    <State>Modified</State>
  </ChangeTracker>
  <Courses/>
  <DepartmentID>1</DepartmentID>
  <Name>Engineeringaaaaaaaaa</Name>
  <StartDate>2007-09-01T00:00:00</StartDate>
</Department>

Érdekes, hogy a módosítás ténye csak a ST-ben látszik, még az eredeti entity-sben sem. A POCO-tól nem is vártuk persze.
Később még majd foglalkozok bővebben a témával. Aki játszani akar vele, hozza létre a School EF példaadatbázist, azon lehet futtatni.

VS 2010 RTM elérhető az MSDN-en

Tuesday, April 13th, 2010

És végre gyorsan jön, de lehet, hogy csak azért, mert alszanak még Amerikában.

Modernizált Design Patterns tanfolyam - 2010. május 4-7

Monday, March 22nd, 2010

Szétestem az utóbbi két hónapban, annyi munkám volt (elnézést kis családomtól így nyilvánosan is), de most végre kicsit fellélegzek. Most sok időm lesz, elvégre a munkák mellé csak 6 MCP vizsgát raktam be áprilisra (.NET 4 béták). :)

Májusban viszont újra lesz Design Patterns tanfolyam, ahol az eddigi a Gof patternek mellett Unityről és általában a Dependency Injection frameworkökről, ViewModelről, Composite Application Block for WPF-ről, T4 template alapú kódgenerálásról, partial method alapú kibővíthetőségről és Entity Framework Portable Extensible Metadatáról (egyfajta DSL) is szó lesz.
Várok mindenkit szeretettel.

Aki a NetAcademiánál jelentkezéskor az alábbi C# kód által kiírt stringet bemutatja, 50e Ft kedvezményt kap az árból.


using System;

class Program
{
    static void Main(string[] args)
    {
        Allat a = new Emlos();
        a.Mozog();
    }
}

class Allat
{
    public virtual void Mozog()
    {
        Console.WriteLine("Állat vagyok, és mozgok");
    }
}

class Emlos : Allat
{
    public virtual void Mozog()
    {
        Console.WriteLine("Emlős vagyok, és mozgok");
    }
}

ViewModelek kérdésköre

Wednesday, March 3rd, 2010

Mostanában oktatok és prototípust írok, közben ezer design kérdést tisztázok a fejemben. Az egyik ilyen pl., hogy a ViewModel DependencyProperty vagy INotifyPropertyChanged módon közvetítse a változásokat a GUI-ra?
Én az INotifyPropertyChanged-re szavaznék, mert így a ViewModel és nem függ a GUI technológiától, eleve nekem fura a modellben pl. WPF fogalmakat látni.

Infók a kérdéskörben.

Általános áttekintés:
INotifyPropertyChanged vs. DependencyProperty in ViewModel

Egy POCO szavazat:
View Models: POCOs versus DependencyObjects

Én Expressionnel oldom meg, hogy ne legyenek property name stringek a kódban, ő nem, de az objektum pool ötlet tetszik benne, átveszem, de szemeteljünk.
A base class which implements INotifyPropertyChanged

Nagyon dicsérik a videót, még nem volt időm megnézni.
Jason Dolinger on Model-View-ViewModel

Jó kép az MVVM rétegekről.

Egyféle Validation megközelítés.
Using a ViewModel to Provide Meaningful Validation Error Messages