Soci (Soczó Zsolt) szakmai blogja

2015.05.08.

Task.Run Etiquette Examples: Don’t Use Task.Run in the Implementation

Filed under: .NET,.NET 4,.NET 4.5,ASP.NET,Szakmai élet — Soczó Zsolt @ 11:34

Az async – await dolgokkal fel lehet szabadítani pl. as ASP.NET által is használt ThreadPool szálakat, hogy míg egy hosszú ideig tartó nem CPU hanem IO intenzív folyamat fut, addig legyen szabad szál kiszolgálni a rendes, kicsi, gyors kéréseket.

De ha úgy aszinkronítunk egy blokkoló, IO intenzív kérést, hogy becsomagoljuk Task.Run-ba, akkor adtunk a sznak egy pofont, mert pont ugyanabból a ThreadPoolból vettünk el szálat, mint amit az ASP.NET is használ (feltételezve az alap TaskSchedulert használjuk). Ráadásul még context switch is lesz a szálak között, stb.

Az igazi aszinkron cuccosok (pl. .NET szerviz hívó osztályok és adatbázis kezelő osztályok) IO completion portot használnak, amivel sok blokkoló folyamatot tudnak monitorozni kevés szálon, nem minden egyes folyamathoz egy szálat használva, mint a Task.Run-os megoldás.

Bővebben a témáról itt.

2014.11.11.

Console.WriteLine blokkolás

Filed under: .NET,.NET 4,.NET 4.5,Szakmai élet — Soczó Zsolt @ 19:38

Ma megint láttam valami újat, kár, hogy bosszúság árán. Egy program logol fileba és konzolra is. VS debugger alatt futott, majd detacholtam. A detacs hatására a Console.WriteLine többet nem tért vissza, nem hibát adott, hanem blokkolta a hívást, ezzel teljen megállítva a program működését. Gondlom a VS átirányította a std outot magára, a detacs során viszont valami nem jött össze neki. Tanulság: ne tessék a debuggerel szórakozni, ha produkciós kódról is fontos futtatásról van szó.

2014.10.09.

Ha egy MVC site nem ad vissza semmit

Filed under: .NET,.NET 4,.NET 4.5,ASP.NET,mvc,Szakmai élet — Soczó Zsolt @ 18:34

De hibát se, akkor lehet egyszerűen nincs minden IIS modul feltelepítve, ami kell hozzá.

2014.07.28.

WCF channel és channelfactory lezárás

Filed under: .NET,.NET 4.5,Szakmai élet,WCF — Soczó Zsolt @ 12:59

A WCF channelek lezárása egyértelmű, ha rendben van a csatorna Close(), ha faulted, Abort().
De mi van a channelfactory-vel?
Amiért a kérdés előjött az az, hogy egy szerviz rendszeres használata során 2 perc utáni hívásoknál elszállt a hívás. A kliens ezt élte meg:
An existing connection was forcibly closed by the remote host

A szerveren meg egy belső exception volt, amit a WCF lenyelt, de jelezte, hogy valami nem ok:
The I/O operation has been aborted because of either a thread exit or an application request.

A figyelmem a channelfactory-re terelődött. Az lokális változóként volt létrehozva, és ebek harmincadjára szabadon engedve. Jaj, ez beteg. Lokális dispose nem jó rá, mert akkor lezárja az összes általa nyitott csatornát (legalábbis vélemények szerint).

Próbáltam egy statikus példányt letárolni, azzal létrehozni a csatornákat, de ez is lepukkant 2 perc után (doksi szerint thread safe, több szálból használtam).

Végül nem maradt más, mint minden csatornához saját factory instance-et létrehozni, és a csatorna használata után mindkettőt lezárni. Pedig elvileg meg lehet osztani a factory-t, teljesítmény okokból. Ebben az alkalmazásban ez megfelelő megoldás, de ha sok csatornát kellene nyitni, az nem tetszene. Valaki látott már ilyen esetet?

2014.07.26.

MemoryCache bug

Filed under: .NET,.NET 4,.NET 4.5,ASP.NET,Szakmai élet — Soczó Zsolt @ 16:53

A MemoryCache osztály ASP.NET hoszt alatt időnként Dispose-olja magát. Nagyon kedves. Mindezt úgy teszi, hogy nem dob semmiféle exceptiont, csak ha beleraksz valamit, nem marad benne.
4.5-ben fixálták, de 4.0-ra is van workaround vagy hotfix is. Részletek itt.

2014.04.15.

Entity Framework 6 null kezelés

Ha egy nullos oszlopon szűrünk, akkor előfordulhat, hogy a bemeneti paraméter is null.
where oszlop = @param

Ebben az esetben az SQL Server alapban az ansi nullságot alkalmazza, így a null != nullal, azaz nem jön vissza egy sor se. Ezért EF6-ban az SQL generátor más sqlt generál, az ilyen esetekben egy IS NULL-os ággal megoldja, hogy nullokra is menjen a keresés.
Viszont ennek drámai mellékhatásai lehetnek. Lássunk egy egyszerű példát:

using (var e = new AdventureWorks2012Entities())
{
var matthew = "Matthew";
e.Person.FirstOrDefault(p => p.LastName == matthew);
}

Ez esik ki az EF-ből:

declare @p__linq__0 nvarchar(4000) = N'Matthew'

SELECT TOP (1) 
    [Extent1].[BusinessEntityID] AS [BusinessEntityID], 
    [Extent1].[PersonType] AS [PersonType], 
    [Extent1].[NameStyle] AS [NameStyle], 
    [Extent1].[Title] AS [Title], 
    [Extent1].[FirstName] AS [FirstName], 
    [Extent1].[MiddleName] AS [MiddleName], 
    [Extent1].[LastName] AS [LastName], 
    [Extent1].[Suffix] AS [Suffix], 
    [Extent1].[EmailPromotion] AS [EmailPromotion], 
    [Extent1].[AdditionalContactInfo] AS [AdditionalContactInfo], 
    [Extent1].[Demographics] AS [Demographics], 
    [Extent1].[rowguid] AS [rowguid], 
    [Extent1].[ModifiedDate] AS [ModifiedDate]
    FROM [Person].[Person] AS [Extent1]
    WHERE (([Extent1].[LastName] = @p__linq__0) 
	AND ( NOT ([Extent1].[LastName] IS NULL OR @p__linq__0 IS NULL))) 
	OR (([Extent1].[LastName] IS NULL) AND (@p__linq__0 IS NULL))

Látható, hogy bool algebrával összehozták, hogy ha a paraméter null és az oszlop is null (is nullal), akkor lejönnek szépen a sorok. Viszont az ilyen query-ket utálja az sql server, nehezen tudja őket optimalizálni. option(recompile) segítene rajta, de ezt meg nem lehet kiadni EF-en keresztül.

Szerencsére vissza lehet állítani a régi kódgenerátort is:

using (var e = new AdventureWorks2012Entities())
{
    e.Configuration.UseDatabaseNullSemantics = true;
    var matthew = "Matthew";
    e.Person.FirstOrDefault(p => p.LastName == matthew);
}

Generált SQL:

	SELECT TOP (1) 
    [Extent1].[BusinessEntityID] AS [BusinessEntityID], 
    [Extent1].[PersonType] AS [PersonType], 
    [Extent1].[NameStyle] AS [NameStyle], 
    [Extent1].[Title] AS [Title], 
    [Extent1].[FirstName] AS [FirstName], 
    [Extent1].[MiddleName] AS [MiddleName], 
    [Extent1].[LastName] AS [LastName], 
    [Extent1].[Suffix] AS [Suffix], 
    [Extent1].[EmailPromotion] AS [EmailPromotion], 
    [Extent1].[AdditionalContactInfo] AS [AdditionalContactInfo], 
    [Extent1].[Demographics] AS [Demographics], 
    [Extent1].[rowguid] AS [rowguid], 
    [Extent1].[ModifiedDate] AS [ModifiedDate]
    FROM [Person].[Person] AS [Extent1]
    WHERE [Extent1].[LastName] = @p__linq__0

Így már egy teljesen tiszta szűrést kapunk, de ez NEM menne null bemeneti paraméterre. Ha nem is kell, hogy menjen, akkor viszont ez sokkal gyorsabb lehet, mint az első, megfelelő indexek esetén.

A két lekérdezés tervét egymás mellé rakva jól látható a különbség:
EFUseDatabaseNullSemanticsDiff

Nagyobb tábláknál a hatás sokkal radikálisabb lenne, érdemes szem előtt tartani ezt.

2014.02.10.

Rétegek közötti kommunikáció

Filed under: .NET,.NET 4,.NET 4.5,ASP.NET,C#,mvc,Szakmai élet — Soczó Zsolt @ 23:27

Tegyük fel emailt akarok küldeni egy weboldalról, amiben linkekeket kell elhelyezni, amik hivatkoznak a weboldal urljeire.
Az email generálás a business logic jellegű komponensekben van. Ezeknek nem illik kinyúlkálni HttpContextekbe meg mindenféle webes cuccokba, mert ők nem webes cuccok. De ebben az esetben mégis csak fel kéne dinamikusan deríteni, hol fut a webapp, mert urleket kell generálni.

Ha kézzel passzolom át a szükséges infót, így néz ki a hívási lánc:

ArticleController.ArticleEdit => (innentől BBL, nem web) IArticleManager.UpdateArticle => INotificationManager.ArticleModified => INotificationFormatter.SendNotification

Na, a NotificationFormatternek már tudni kell urleket gyártani, a felette futó website intrinsic property-jei alapján.
Ezt kézzel átpasszolni a teljes láncon ronda. Mi jöhet még szóba? Statikus változók, amit a webapp indulásakor pl. a global.asaxben feltöltök, és a BLL-ben kiolvasom? Nem tetszik, de egyszerű. Utálom a statikusokat, szétborítanak minden tesztet.
Létrehozni egy IWebEnvironment interfészt, amit a tesztekben fake-elek, élőben pedig szintén a web indulásakor betárazok a DI containerbe? Kicsit körmönfont, de ez egy fokkal jobban tetszik.
Ötlet?

2014.01.30.

MVC project indulás lassú

Filed under: .NET,.NET 4,.NET 4.5,ASP.NET,mvc,Szakmai élet — Soczó Zsolt @ 17:36

Normális, hogy minden egyes fordítás után fél percet kell várni, mire az mvc projekt elindul? Vagy csak nálam ilyen tetű lassú? Nem debuggerben, nem symbol issue, csak simán ránézve böngészőből. Cassinivel és iissel is. Hm?

2013.02.14.

WCF perf

Filed under: .NET,.NET 4.5,Szakmai élet — Soczó Zsolt @ 22:11

Ma sikerült 700 MBits/seccel adatokat átvinni titkosítva, tömörítve WCF-fel egy erős pc és egy szerver között. :)
Ehhez egyszerre 3 szerverre uploadoltunk fájlokat párhuzamosan, netTcpBindinggel, SSL-lel, a csatornán Deflate tömörítéssel (.NET 4.5).

2013.01.25.

Compression a 4.5-ös WCF-ben

Filed under: .NET,.NET 4.5,Szakmai élet — Soczó Zsolt @ 00:28

Hoppá, már nem házilag kell megoldani. Jövő héten kipróbáljuk.

Powered by WordPress