Újabb crash hotfix a VS 2010 RC1-hez
March 5th, 2010Itt.
Remélem ez már segít, mert már kezd az agyamra menni a a napi 10 crash.
Itt.
Remélem ez már segít, mert már kezd az agyamra menni a a napi 10 crash.
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
Egyféle Validation megközelítés.
Using a ViewModel to Provide Meaningful Validation Error Messages
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.
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
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.
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?
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.
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.
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.
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.
Talán 7. éve, már nem is emlékszem az elejére. Köszönet az MS-nek a megtiszteltetésért.
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.
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. :)
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.
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.
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
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.
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.
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á.
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.
A galamblelkűek, a diszkrimináltak. A forum.index.hu lehalt, az index.hu is akadozik.