Újabb crash hotfix a VS 2010 RC1-hez

March 5th, 2010

Itt.
Remélem ez már segít, mert már kezd az agyamra menni a a napi 10 crash.

ViewModelek kérdésköre

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

A holokauszt törvényhez

February 24th, 2010

Felmdmár András (http://www.scribd.com/doc/9647942/Beszelgetesek-Feldmar-Andrassal):
“Nagyon hosszú életem során arra a következtetésre jutottam, hogy fontos különbséget tennünk az emberek cselekedetei, valamint gondolatai, érzelmei között. Én csak olyan világban szeretnék élni, ahol nincs gondolatrendőrség, ahol nincsen érzelemrendőrség, de egyáltalán nem bánom azt, ha van cselekedetrendőrség. Mert az, hogy én mit gondolok és mit érzek, az én dolgom, ahhoz senkinek semmi köze. Nincs jogom azonban olyasvalamit csinálni, ami megnehezíti azt, amit te akarsz tenni. Cselekedeteimmel nem szabad belevágnom más emberek életébe. A gondolkodásom az más. Ha nem tetszik, amit gondolok, akkor kimehetsz az ajtón, tehát a gondolataimmal nem csinálok semmi bajt. Ha abszolút bizarr dolgokat mondanék – amit persze én soha nem mondok –, akkor se lenne jogotok becsukni engem egy diliházba vagy egy börtönbe.”
Feldmár -akit nagyon szeretek- zsidó ember. Szerintem, ennek ellenére ellenezne egy ilyen ostoba törvényt.

VS 2010 RC elszállás - hotfix

February 20th, 2010

Ez igencsak bosszantó volt, remélem a fix megoldja.
Nekem nem tablet pcm van, mégis előjön.
https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=26662

Enterprise Modeling Anti-Patterns

February 1st, 2010

Link.

Most tervezek egy cégnek egy nagyobb architektúrát, és közben próbálom szem előtt tartani ezt a listát, könnyű beleesni a benne szereplő hibákba.

EF SSDL alapú felhasználói bemenet ellenőrzés

February 1st, 2010

A következőn töröm a fejem. Az Entity Framework SSDL-jében definiálva vannak az entitás property-k alapvető jellemzői: nullázhatóság, max hossz. Ezeket a GUI-n ki kell kényszeríteni. Nyilván vannak összetettebb validálási szabályok, de most koncentráljunk ezekre az elemiekre.
Utálok minden redundanciát egy rendszerben, ezért azt gondoltam, a szabályokat kiolvasom az EF sémájából, és ebből táplálom meg a validáló részeket, így nem kell törődni az egyszerű validálásokkal, automatikusan működni fognak.

A következő kis kódocska mutatja meg a metaadatok használatát:


o.ForceLoadingSchemas();

var sspaceEntitySets = o.MetadataWorkspace
       .GetItems<EntityContainer>(DataSpace.SSpace)
       .First().BaseEntitySets.OfType<EntitySet>();

foreach (EntitySet es in sspaceEntitySets)
{
    foreach (EdmProperty p in es.ElementType.Properties)
    {
        ReadOnlyMetadataCollection<Facet> facets = p.TypeUsage.Facets;
        Debug.WriteLine("{0} is {1} nullable", p.Name, (bool)facets["Nullable"].Value ? "" : "not");
        if (facets.Contains("MaxLength"))
        {
            Debug.WriteLine("{0} MaxLenght is {1}", p.Name, (int)facets["MaxLength"].Value);
        }
        Debug.WriteLine("{0} is {1} nullable", p.Name, (bool)facets["Nullable"].Value ? "" : "not");
    }
}

A ForceLoadingSchemas az ObjectContext partial classában van:


public void ForceLoadingSchemas()
{
    CreateQuery<BusinessEntity>("AdventureWorks2008Entities3.BusinessEntities").ToTraceString();
}

Csinált már valaki ilyet? Van benne valami csapda, amit most nem látok?

SQLCLR deplyment hiba

January 27th, 2010

Error: Incorrect syntax near valami.
Akkor jön elő, ha az SQLCLR assemblyt és benne a függvényeket akarja az VS deployolni. Több oka lehet, most az volt, hogy egy .NET oldalon double-t visszaadó függvény véletlenül így lett deklarálva:


[SqlFunction(..., TableDefinition = "Datum datetime, Szazalek double")]

Mi a hiba benne? SQL Serverben nincs double, csak real és float. Ráadásul a C# float az az SQL real és a C# double az SQL Server float (kb.). :)

Az előbbi helyesen:


[SqlFunction(..., TableDefinition = "Datum datetime, Szazalek float")]

Miért kellett SQLCLR függvényt írni? A futó aggregálások (én legalábbis nem tudok jobbat kurzor nélkül) o(n2)-es algoritmusok, ezt CLR-ben könnyen meg lehet írni o(n)-re. Pl:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public class UserDefinedFunctions
{
    internal class Result
    {
        public long RowId;
        public double CumDollarGain;
        public double TopDollarGain;
        public double DollarDrawDown;
    }

    //Input table: create table #trades(RowId long, DollarGain money, other columns possible)
    [SqlFunction(FillRowMethodName = "FillRow", DataAccess = DataAccessKind.Read,
        TableDefinition = "RowId bigint, CumDollarGain float, TopDollarGain float, DollarDrawDown float")]
    public static IEnumerable Cumul()
    {
        using (var conn = new SqlConnection("Context connection=true"))
        {
            using (var cmd = new SqlCommand("select RowID, DollarGain from #trades", conn))
            {
                var res = new List<Result>();
                conn.Open();
                double cumulPrice = 0, topPrice = 0, drawDawn = 0;

                using (SqlDataReader r = cmd.ExecuteReader())
                {
                    int idCol = r.GetOrdinal("RowID");
                    int gainCol = r.GetOrdinal("DollarGain");

                    while (r.Read())
                    {
                        var price = r.GetDouble(gainCol);

                        cumulPrice += price;
                        topPrice = Math.Max(price, topPrice);

                        drawDawn += price;
                        drawDawn = Math.Min(drawDawn, 0);

                        res.Add(new Result
                                    {
                                        RowId = r.GetInt64(idCol),
                                        CumDollarGain = cumulPrice,
                                        TopDollarGain = topPrice,
                                        DollarDrawDown = drawDawn
                                    });
                    }
                    return res;
                }
            }
        }
    }
    public static void FillRow(object obj,
    out long id,
    out double cumDollarGain,
    out double topDollarGain,
    out double dollarDrawDown)
    {
        var r = (Result)obj;
        id = r.RowId;
        cumDollarGain = r.CumDollarGain;
        topDollarGain = r.TopDollarGain;
        dollarDrawDown = r.DollarDrawDown;
    }
};

Sajnos nem lehet átpasszolni a megnyitott SqlDataReadert a két metódus között, ezért kénytelen az ember letárolni az eredményhalmazt. Persze pár ezer sornál ez nem gond.

Parallel.ForEach in action

January 21st, 2010

Imádom, nagyon ügyes dolog. Csak azért nem hajtotta ki jobban a procikat, mert már nem győzték a diszkek.
Érdekes problémaként még az jött elő, hogy több mint 100 szálat indított a Parallel, mert úgy érezte ez jó lesz, de az Sql Server connection poolja alapban max. 100 kapcsolatot engedélyez, így egyes szálak bedugultak. Lejjebb lehet venni a szálak számát, vagy feljebb a poolt. Nekem 80 szál elég volt, úgyse győzte már az SQL Server az adatokat.

Jó Entity Framework architektúra cikkek

January 19th, 2010

Hogyan építsünk EF-re többrétegű appot?
1. Anti-Patterns To Avoid In N-Tier Applications
2. N-Tier Application Patterns
3. Building N-Tier Apps with EF4

Jó cikkek.

Entity Framework gyors lett

January 6th, 2010

Na, ez aztán a furcsa. Pár napja írtam, mennyire lassan indul az EF. Nem tudom mitől, talán attól, hogy felraktam a Microsoft ADO.NET Entity Framework Feature Community Technology Preview 2-t, ami lecserélte a bugos .NET 4 Beta2-es EF assemblyket, amelyek nem használták ki az előfordított viewkat? Nem tudom, nem nyomoztam utána, mert most jó. :)
Korábban annyira felbosszantott, hogy elkezdtem nézni az NHibernate-et, de ahhoz meg a LINQ támogatás jár még gyerekcipőben, nekem meg tele van azzal a programom. Így az kiesett.
Marad az EF, így már nagyon szeretem.

Újra MVP vagyok :)

January 6th, 2010

Talán 7. éve, már nem is emlékszem az elejére. Köszönet az MS-nek a megtiszteltetésért.

Entity Framework teljesítmény-optimalizálás

January 3rd, 2010

Boldog Új Évet.

Ez az Entity Framework egy nehezen betörhető ló. A run-time teljesítménye jó, de az indulása, az katasztrófa.
Pár hasznos link a témában.

Alapelvek 1, 2.

Hol találhatunk fogást rajta?.
Viewk előre generálása:

Régi módon.
Eszköz hozzá: EdmGen.

Új módon:
How to use a T4 template for View Generation

Nagy modellek esetén hogyan ne őrüljünk meg (meg fogunk) 1, 2, gyakorlati példa.

Boldog Karácsonyt!

December 23rd, 2009

Nem küldök push jellegű üdvözlő emaileket, úgyis mindenkinek tele lesz a postaládája, inkább így, pull módon kívánok mindenkinek boldog, békés Karácsonyt. Gyerekeseknek meg betegségmenteset, a Karácsony sok gyereknél betegségeket hoz elő, mi eddig megúsztuk. :)

Szűrés opcionális paraméterekre LINQ-val

December 8th, 2009

A feladvány, hogy van sok szűrési feltétel, ezekre szűrni kell, ha van érvényes értékük, vagy kihagyni a szűrésből, ha nincs.
A feladatra SQL Server esetén bool algebrás megoldást írnék:


select * from tabla
where
(@p1 is null) or (@p1 = col1)
and
(@p2 is null) or (@p2 = col2)
...
option(recompile)

Az option hint azért kell, mert így a nullos paraméterek teljesen kiesnek a tervből, csak a valódi szűrésekre készül terv. Ez szerintem óriási dolog, érdemes észben tartani.

Ugyanezt a logikát linqval is el lehet játszani, ref típusokkal kb. így:


var x = from z in Valami
where (p1 == null) || (p1 = z.col1)
&&
(p2 == null) || (p2 = z.col2)

Egy másik megoldásban azt használjuk ki, hogy késleltetett módon értékelődnek ki a kifejezések, így lehet őket láncolni. Itt látható ez a megoldás, és még más megközelítések is.

A harmadik megoldásban írhatunk egy saját szűrő operátort is erre a célra. Az előbbi címről:


public static IQueryable<TSource> WhereIf<TSource>(
    this IQueryable<TSource> source, bool condition,
    Expression<Func<TSource, bool>> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

Nehéz megindokolni, melyik megoldás a jobb.

Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4

December 1st, 2009

120 oldalas kis olvasmány, alig várom már, hogy legyen egy kis időm elolvasni.

Az tetszik a 4.0-ban, hogy végre megint hozzányúltak az alapokhoz, mint pl. a threadinghez.

Managed memory leak nyomozás WinDBG-vel

November 30th, 2009

Imádom a WinDbg-t, mondtam már? Az egyik kis programom módszeresen eszegette a memóriát. Ciklusban végez feldolgozást, rettentő sok adattal, és ezek egy része szépen bennragadt a memóriában. .NET-ben nincs memory leak a klasszikus értelemben, de van oly módon, hogy a rootokból marad referencia egyes objektumokra, így azok élve maradnak, szándékaink ellenére. Néha azonban nem triviális kinyomozni, melyik root miatt ragadt be valami a memóriába.
A VSTS profiler segítségével odáig el lehet jutni, hogy ki ragad be a memóriába. Az Object LifeTime nézetben az Instances alive at end oszlopra rendeztetve láthatjuk, kik maradnak élve a program végén. Én beraktam egy force-olt GC-zést a program végére, így aki ezek után még benn maradt, azok egy része szándékaim ellenére tette ezt, ezért azt leaknek tekintem, és meg kell szüntetni.
Jöhet a WinDbg. File, Open Executable, F5. A program végén a GC után raktam még egy Console.ReadLine()-t. Amikor ide eljut, a debuggerben CTRL-Breakkel megállítom a program futását (várakozását). Betöltöm az sos-t:
.load C:\Windows\Microsoft.NET\Framework64\v4.0.21006\sos.dll

Kilistáztatom a GC heapen levő ojjektumokat:
!DumpHeap -stat


000007ff005c2278     2766       221280 System.Data.Metadata.Edm.TypeUsage
000007fef1b90d00      370       222136 System.Byte[]
000007fef1b89be0    13379      1330896 System.String
000007fef1b8c8a8     1125      1447552 System.Int32[]
000007fef1b3bef0     9298      3224392 System.Object[]
000007ff00274988   245954     29514480 ATS.Bar

Akinek nem szabadna már a memóriában lenni, az az ATS.Bar objektumok, 245954 darab, 29514480 bájt méretben. Nézzük megy a példányokat belőle:

!DumpHeap -type ATS.Bar


0000000004952c60 000007ff00274988      120
0000000004952cd8 000007ff00274988      120
0000000004952d50 000007ff00274988      120
0000000004952dc8 000007ff00274988      120
0000000004952e40 000007ff00274988      120
0000000004952eb8 000007ff00274988      120
total 0 objects
Statistics:
              MT    Count    TotalSize Class Name
000007ff00fb38a0        1           24 System.Collections.Generic.GenericEqualityComparer`1[[ATS.BarCollectionDescriptor, Common]]
000007ff00271050        1           24 ATS.BarDAL
000007ff00270e68        1           48 ATS.BarFactory
000007ff00fb31b8        1           88 System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]]
000007ff00fb27a8        1           88 System.Collections.Generic.Dictionary`2[[ATS.BarCollectionDescriptor, Common],[ATS.BarFromTickFactory, Common]]
000007ff00275378        1           88 System.Collections.Generic.Dictionary`2[[ATS.Symbol, Common],[ATS.BarCollectionByInterval, Common]]
000007ff00fb3ee0        1           96 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]][]
000007ff00fb3b10        1           96 System.Collections.Generic.Dictionary`2+Entry[[ATS.BarCollectionDescriptor, Common],[ATS.BarFromTickFactory, Common]][]
000007ff00fb0498        1           96 System.Collections.Generic.Dictionary`2+Entry[[ATS.Symbol, Common],[ATS.BarCollectionByInterval, Common]][]
000007ff00fb2d30        3          120 System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]]
000007ff00790990        3          120 ATS.BarCollectionDescriptor
000007ff00fb1568        7          168 System.Collections.ObjectModel.ObservableCollection`1+SimpleMonitor[[ATS.Bar, Common]]
000007ff00886530        3          240 ATS.BarFromTickFactory
000007ff00fb1648        7          280 System.Collections.Generic.List`1[[ATS.Bar, Common]]
000007ff00273788        7          952 ATS.BarCollection
000007ff00274988   245954     29514480 ATS.Bar
Total 245993 objects

A kis 120 bájtos izék a problémásak (a végén az összefoglaló azért tartalmaz több típust is, mert substring szűrést csinál a -type). Az első oszlop az egyedi objektumok címe, a második a típus metódusleíró táblája.
És most jön a lényeg. Ki miatt érhető el a rootokból mondjuk az utolsó Bar példány?

!GCRoot 0000000004952eb8


DOMAIN(00000000003CF530):HANDLE(Pinned):1217d8:Root:  0000000012657048(System.Object[])->
  0000000002bf7f28(System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]])->
  0000000002bf94b8(System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]][])->
  0000000002f06ca0(System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]])->
  0000000002f06cc8(System.Object[])->
  0000000002f05860(ATS.BarFromTickFactory)->
  0000000004952eb8(ATS.Bar)

Ebben az látszik, az első bejegyzésből, amit még a CLR startup hozzott létre hogyan lehet eljutni a beragadt objektumunkhoz. A CLR generikus neveket C#-ra visszafordítva az látszik, hogy egy Dictionary> hivatkozik egy Dictionary.Entry>-re, az a dictionary belső tárolóeleme. Ez rámutat egy List-re, ami továbbmutat egy ATS.BarFromTickFactory-ra, ami hivatkozik a Bar-ra.

Ez alapján már rekonstruálható a probléma forrása. A probléma elemi oka, hogy túlzásba vittem a statikusok használatát, és nem figyeltem a takarításukra.


static readonly Dictionary<BarCollectionDescriptor, BarFromTickFactory> FactoriesByDesc =
    new Dictionary<BarCollectionDescriptor, BarFromTickFactory>();

Ebből rendesen kiszedtem a tárolt BarFromTickFactory példányt, ha már nem volt rá szükség. De elfeledkeztem róla, hogy volt egy másik kollekció is:


public static readonly Dictionary<int, List<BarFromTickFactory>> FactoryListBySymbolId =
    new Dictionary<int, List<BarFromTickFactory>>();

Ebből viszont nem szedtem ki a hivatkozás az adott BarFromTickFactory-ra, így az szépen beragadt a memóriába.

Tanulságok:
1. Szeretjük a WinDbt-t.
2. Kerüljük a statikusokat. Ha a BarFromTickFactory példányokat egy mások osztály példányai tárolnák, akkor ha azok kifutnak a szkópból, automatikusan a GC martalékai lesznek. A sok statikus sok odafigyelést igényel, kár erőltetni őket.

A Windows 7 megmentette a laptopomat a leégéstől

November 28th, 2009

Két hete vettem a fáradtságot, és felraktam az eddigi Windows 2008 helyére a Windows 7-et. (Kicsit nehezen szántam rá magam, mert minden OS váltás után vagy egy hét mire minden újra a helyére kerül.)
Hamarosan észrevettem, hogy nagyon belassul a gép. A Resource Monitor CPU fülén a Processes fejlécen látható egy érték, % Maximum Frequency. Ez azt jelzi, mennyire vette vissza a Windows (vagy a BIOS?, nem tudom) a proci frekvenciáját. A belassulások idején ez kb. 20%-on állt, és nem akar feljebb menni. Bosszantó, leszabályozza a Windows a proci sebességemet. Mivel nem elégedtem meg egy egyszerű k. Windows beszólással (másképp mehetnék HUP tagnak), utánanéztem a dolognak. Azt írják, ha a gép hűtésével van gond, akkor történhet ilyen. Volt, pl., akinek leállt a proci ventilátora.
Tesztként kikapcsoltam a BIOS-ban a SpeedStepet, ettől mindig 100%-on volt a sebesség, de egész nap süvített a gép, és lesütötte a t.met is.
Végül bekapcsoltam a kompresszort, és alaposan kifújattam a port a gépből. Azóta hideg a gép, mégis, ha kell, felhúzza a Windows 100%-ra a procisebességet (a BIOS-ban újra engedélyeztem a SpeedStepet).
Terjesszétek a jó hírt, ha belassul a gép, tessék alaposan kiporolni.

Apró objektumok szemetelése

November 28th, 2009

Az előző bejegyzéshez kapcsolódik még az alábbi. Sok felesleges memóriaallokállást és aztán GC-zést okoztam az alábbi kóddal:


...
OnBarArrived(new BarArrivedEventArgs(bar));
...

        private void OnBarArrived(BarArrivedEventArgs e)
        {
            if (BarArrived != null)
            {
                BarArrived(this, e);
            }
        }

Ha a BarArrived event null, azaz nem iratkozott fel senki az eventre, akkor feleslegesen hozok létre egy BarArrivedEventArgs-ot. A javított verzió így néz ki:


private void OnBarArrived(Bar bar)
{
    if (BarArrived != null)
    {
        BarArrived(this, new BarArrivedEventArgs(bar));
    }
}

A bar objektum már úgyis kész van, az eventargot viszont csak akkor hozom létre, hogy tényleg szükség van rá.

Naív ojjektumhasználat - sok szemét

November 27th, 2009

Két hete elkezdtem intenzíven használni a Visual Studio 2010-et, mivel szükségem volt a MemoryMappedFiles-ra a 4.0-s fwből. Ezt shared memoryként használom, amivel a laptopon kb. 1GByte/sec-kel tudok adatokat másolni két processz között, így kiváló cache-t tudtam építeni a segítségével. De most nem erről lesz szó.
A profilert is jelentősen továbbfejlesztették, van benne pl. rendes memória allokálás követés (lehet, hogy a régi is tudta ezt, akkor pardon).
Nézzük az alábbi képet:

A SecondInterval.Day property-t közel 6 milliószor hívtam meg, minden híváskor előállítva egy új ojjektumot, 140MByte-nyi szemetet hagyva magam után.

A naív implementáció így nézett ki:


public static SecondInterval Day
{
  get { return new SecondInterval(86400); }
}

Maga a típus egy egyszerű Whole Value pattern implementáció, egy sima immutable objektum, ráadásul class, így referenciális típus.

Nem memóriapazarló módon így néz ki a property:


private static readonly SecondInterval aDay = new SecondInterval(86400);
public static SecondInterval Day
{
    get { return aDay ; }
}

A readonly fontos, hogy nehogy valaki kiüsse a referenciámat, és berakjon egy sunyi másik időt reprezentáló ojjektumot a napi helyére. Az ojjektum immutable, ez fontos, másként semmit nem érne a readonly, a gyomrát lehetne piszkálgatni.
Amin még el lehetne gondolkodni, hogy struktúrává, value type-pá átalakítani a típust, csak akkor meg minden helyen ahol használom, másolni kellene az értékét. Mivel ez most belül egy 32 bites int, és 64 bit alatt futtatom, ahol a referenciák 64 bitesek, a másolással még mindig jobban járok. Úgyhogy lehet, hogy struct lesz, de előbb megnézem classként hogy muzsuikál.

“Holnapra megölünk minden magyart!”

November 15th, 2009

A galamblelkűek, a diszkrimináltak. A forum.index.hu lehalt, az index.hu is akadozik.