Luc

Csőhalmadár!

FileSystemWatch​­er alkönyvtár figyelése

Mi a különbség a két kódrészlet között?
            importWatcher.IncludeSubdirectories = true;
            importWatcher.Path = ConfigurationManager.AppSettings["ImportFolder"];
            importWatcher.Path = ConfigurationManager.AppSettings["ImportFolder"];
            importWatcher.IncludeSubdirectories = true;
Az, hogy az első leginkább csúnya szavakat generál. Addig ne nagyon próbálkozzunk alkönyvtárfigyelést beállítani, amíg nincs beállítva maga az útvonal, amit figyeltetni szeretnénk.

T-SQL és a reguláris kifejezések

T-SQL-ben is használhatjuk a reguláris kifejezéseket, csak némi hegesztést igényel a dolog.
 Hozzunk létre egy Class Library-t, valami ilyesmi tartalommal:
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;

namespace LunirSqlExtensions
{
    
    public class SqlRegex
    {
        [SqlFunction(IsDeterministic=true, IsPrecise=true)]
        public static bool IsMatch(string inputfield, string pattern)
        {
            return Regex.IsMatch(inputfield, pattern);
        }
    }
}
Fontos, hogy 3.5 framework-re buildeljünk, különben az SQL szerver beszól, legalább is a 2008 R2. Ebből lesz egy DLL, amit használni tudunk SQL-ből, némi előkészítés után:
sp_configure 'clr enabled', 1
go
reconfigure
go
create ASSEMBLY LunirSqlExtensions from 'x:\SqlExt\LunirSqlExtensions.dll' with PERMISSION_SET = SAFE
go
create function [dbo].[LunirRegexIsMatch] (@inputfield [nvarchar](MAX), @pattern [nvarchar](255)) RETURNS [BIT] with execute as caller AS EXTERNAL NAME LunirSqlExtensions.[LunirSqlExtensions.SqlRegex].[IsMatch]
go
Ez pedig a használata:
select dbo.LunirRegexIsMatch('a','a')
Tudom, hogy nem a legvilágrengetőbb az 'a' stringen alkalmazni az 'a' reguláris kifejezést, de példának megfelel :)

Teljes hónap lekérdezése

Ha egy táblából szeretnénk egy teljes hónap eseményeit megszerezni, de úgy, hogy az eseménytelen napok is kerüljenek kilistázásra, arra itt egy módszer:
with nums (i) as ( select i = 0 union all select i + 1 from nums where i < 100 )
    select a.dte, COUNT (d.[Date])
    from
        (select dte = dateadd(dd,nums.i,@StartDate) from nums) a
    left  join
        [valamilyentabla] d
    on
        DATEADD(DAY, 0, DATEDIFF(DAY, 0, d.[Date])) = DATEADD(DAY, 0, DATEDIFF(DAY, 0, a.dte))
    where a.dte <= @EndDate
    group by a.dte
    order by a.dte
Természetesen ez nem csak egyetlen hónapra alkalmas, illetve nem csak napokra, hanem akár órákra, vagy hónapokra is lehet bontani a kívánt időszakot. A JOIN feltételben ekkor nem DAY intervallumot kell használni, hanem azt, ami éppen tetszik, YEAR, MINUTE, akármi.
A megoldás ötletét innen vettem: http://www.sqlteam.com/

Visual Studio 2010 és a dezertőr solution node

Ha a Solution explorerben nem látszik a solution node, csak egyetlen egy árva projekt, akkor itt lehet bekapcsolni a solution node megjelenítését: Tools - Options - Projects and solutions és kapcsoljuk be az Always show solution jelölőnégyzetet.

Application pool identity használata

Az application pool identity használata GUI-ból elég nagy szívás, legalább is Windows 2008 R2 alatt. Történetesen nem igazán lehet akár file rendszerhez, akár adatbázishoz kötni őket. Parancssorból viszont egyszerű. A folder1-re adhatunk mindenféle jogokat ezzel:
icacls c:\inetpub\wwwroot\webapp\folder1 /grant "IIS AppPool\valamilyenappoolnev":(OI)(CI)(IO)(R,W,D,DC,X)
Az (OI)(CI)(IO) paraméterek azt mondják meg, hogy nem a kérdéses könyvtárra adunk jogokat, hanem az összes alatta lévő könyvtárra és állományra. A többi paraméter az írás, olvasás, törlés, és gyermekelem törlése engedélyek. Ha épp nem ezek a jogok kellenek, akkor a help megmondja a többit, viszont ez után a parancs után már GUI-ból is tudjuk a jogokat osztogatni.
Az SQL (2008 R2) adatbázishoz pedig így tudunk logint létrehozni:
create login [IIS AppPool\valamilyenappoolnev] from windows with default_database=[adatbazisnev] use [adatbazisnev] create user [IIS AppPool\valamilyenappoolnev] for login [IIS AppPool\valamilyenappoolnev]
Ez utóbbi, ha minden igaz mehet GUI-ból is, csak ne nyomjunk "check names"-t. Nem, nem mehet... Viszont a Windows 2012-ben már nem okoz ez fejfájást, simán fel fogja ismerni az AppPool-os azonosítókat.

Visual Studio 2010 project wizard

Ha sokszor kell ugyanazon modulokból, de nem mindig ugyanazokat alkalmazni a munkáink során, akkor meggyorsíthatja a fejlesztést, ha a project létrehozásakor nem nekünk kell hozzáadogatni a modulokat és esetleg refaktorálni, hanem ezt egy varázsló segítségével oldjuk meg. Példaképp hozzunk létre egy konzol alkalmazást és írjunk bele egy sort, mondjuk ezt: Console.WriteLine("$probaszoveg$"); A $probaszoveg$ lesz az a blokk, amit majd a konkrét project létrehozásakor szeretnénk lecserélni a megfelelő szövegre. Ugyanekkor szeretnénk, ha két osztálydefiníció közül egy bekerülne a projectbe.
 Ebből készítsünk project template-et a file menü Export template menüpontjával. Itt válasszuk a project template lehetőséget és jelöljük ki a konzol projectet. Ezután adhatunk a template-nek nevet, leírást, ikont. Hagyjuk bekapcsolva az importálást VS-be. A template helyét láthatjuk a wizard utolsó lépésében. Ezzel megvan a példánkban létrehozandó majd testreszabandó projectünk template-je.
 Hagyjuk most hűlni a template-t és kezdjünk bele a varázslóba. Hozzunk létre - akár a fenti konzol projectünk solution-jén belül - egy Class Library projectet. Maradjon a neve a felettébb fantáziadús ClassLibrary1. Adjuk hozzá a projecthez következő referenciákat:
1.Microsoft.VisualStudio.TemplateWizardInterface
2.EnvDTE
3.System.Windows.Forms

Adjunk a projecthez egy Windows Form elemet. Ezen helyezzünk el egy TextBox-ot és pár ComboBox-ot - ez utóbbiban legyen valahány elem - valamint egy Button-t. A vezérlők értékeit tegyük elérhetővé publikus property-ken keresztül.
private void button1_Click(object sender, EventArgs e)
{
   Probaszoveg = tbSzoveg.Text;
   Osztalynev = cbClass.SelectedItem.ToString();
   Proptipus = cbPropType.SelectedItem.ToString();
   Propnev = cbPropName.SelectedItem.ToString();

   this.Dispose();

}

public string Probaszoveg { get; private set; }
public string Osztalynev { get; private set; }
public string Proptipus { get; private set; }
public string Propnev { get; private set; }
A ClassLibrary1.cs-be húzzuk be a szükséges névtereket, azaz:
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
Hozzunk létre egy osztályt az IWizard interface alapján.
public class WizardClass1 : IWizard
{
   public void BeforeOpeningFile(ProjectItem projectItem)
   { }

   public void ProjectFinishedGenerating(Project project)
   { }

   public void RunFinished()
   { }

   public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
   { }

   public bool ShouldAddProjectItem(string filePath)
   {
      return true;
   }

   public void ProjectItemFinishedGenerating(ProjectItem projectItem)
   { }

}
A ShouldAddProjectItem és a ProjectItemFinishedGenerating metódusok csak item template-ekhez kellenek, de mivel az IWizard interface jajjgat ha nem impelemtáljuk őket, hát itt vannak.

A fenti WizardClass1-en belül hozzunk létre a varázslónk egy példányát és a property-ket, amikben a varázslónk segítségével lekérdezett értékeket tároljuk.
public class WizardClass1 : IWizard
{
    private WizardForm1 newForm;

    public String Osztalynev { get; private set; }
    public string Proptipus { get; private set; }
    public string Propnev { get; private set; }
}
A RunStarted metódus akkor fut le, ha már a project template alapján létrejött a projectünk. Persze az még nem a végleges formában van, mert szeretnénk ha a varázslónk összegyűjtötte adatok szerint nézne ki. Ehhez meg kell jelenítenünk a varázslót és beolvasni a kiválasztott értékeket, azaz írjuk a RunStarted metódusba az alábbiakat.
newForm = new WizardForm1();
newForm.ShowDialog();

Osztalynev = newForm.Osztalynev;
Proptipus = newForm.Proptipus;
Propnev = newForm.Propnev;

replacementsDictionary.Add("$probaszoveg$", newForm.Probaszoveg);
Ezután a $$ jelek közé rakott szövegek lecserélődnek a kívánt tartalomra. Ezzel kész is van a template alapján létrehozott project, de mi még tovább szeretnénk menni és mást is hozzáadni az új projecthez. A ProjectFinishedGenerating metódusban ez fusson:
string dir = Path.GetDirectoryName(project.FullName);
string code =
    "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\n\r\n" +
    "namespace WizardGenerated\r\n{\r\n\tpublic class " +
    Osztalynev +
    "\r\n\t{\r\n\tpublic " +
    Proptipus + " " + Propnev+ ";\r\n\t}\r\n}";
StreamWriter sw = new StreamWriter(dir + "\\" + Osztalynev + ".cs");
sw.Write(code);
sw.Close();
project.ProjectItems.AddFromFile(dir + "\\" + Osztalynev + ".cs");
Ezzel tulajdonképpen el is készült némileg egyszerűre sikeredett varázslónk. Természetesen a fentieket érdemes ellenőrzésekkel, tyr-catch blokkokkal és MessageBox-okkal jól telerakni, de az olvashatóság miatt ezeket kihagytam.

Következő dolgunk, a Visual Studio tudomására hozni, hogy használja is az alkotásunkat.

Ehhez a gacutil.exe és az sn.exe programokat kell futtatnunk, amik általában a "c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\NETFX 4.0 Tools\x64\" könyvtárban találhatók. De ha nem szeretnénk folyton parancsorba írogatni és erre pazarolni az értékes időt, akkor a wizard projectünkön jobb gomb, properties és a Build Events fülön a Post-build event command line-hoz írjuk be az alábbiakat:
"c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\NETFX 4.0 Tools\x64\gacutil.exe" -u "$(ProjectName)"
"c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\NETFX 4.0 Tools\x64\gacutil.exe" -i "$(TargetPath)"
"c:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\NETFX 4.0 Tools\x64\sn.exe" -T "$(TargetPath)"
Érdemes egyébként nem csak simán bemásolni a fentieket, hanem meggyőződni róla, hogy tényleg ott van-e az említett két segédprogram. Figyelmetekbe ajánlom az Edit Post-build gombot, találhattok benne még makrókat. A fenti sorok hatására bekerül a GAC-ba a varázslónk dll-je. Feltéve, ha adminisztrátorként futtatuk a Visual Studio-t. Ha van valami rendszergazdai vénával megáldott ember, aki meg tudja mondani, hol és milyen jogosultságot kell adni az usernek, hogy ne csak rendszergazdaként tudja hatásosan futtatni a fentieket, az ne habozzon, ossza meg velem. Az utolsó sor visszaadja a PublicKeyToken-t, amit mindjárt fel is használunk. Ezt az Output konzolon (Ctrl+W, O) nézhetjük meg.

Már csak a fent létrehozott project template-t kell szerkeszteni. Keressük meg és csomagoljuk ki a .vstemplate kiterjesztésű állományt. Ehhez kell hozzáírnunk a </TemplateContent> tag után az alábbi kódblokkot. (Nem tudom miért utána kell, mindenhol így írták, én meg lusta voltam máshogy kipróbálni.)
<WizardExtension>
   <Assembly>ClassLibrary1, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=***************</Assembly>
   <FullClassName>ClassLibrary1.WizardClass1</FullClassName>
</WizardExtension>
A beírandó PublicKeyToken-t az előbb tudtuk meg az sn.exe jóvoltából. Dobjuk be a .vstemplate állományt a template ZIP-be. Innentől, ha a varázslón bármit változtatni szeretnénk, csak újra kell buildelni és már használható is. Kicsit bonyolultabb a dolog, ha a project templaten szeretnénk változtatni, de az sem vészes. Az apróbbakat akár a template-ben is elintézhetjük kézzel, de ha komolyabb változtatásokat kell végezni, akkor célszerű újragenerálni a template-t, az viszont törli a fenti <WizadExtension> tag-et, így azt újra hozzá kell adnunk.

Ha mindent jól csináltunk és én sem felejtettem el leírni semmit sem, akkor a build után használható lesz az új project template + a varázslónk. Fontos, hogy a gacutil.exe lefutása után csak akkor tudjuk használni az új wizardot, ha új Visual Studio-t indítottunk.

SQL tábla mezőinek feltöltése növekvő értékekkel

Ha egy utólag hozzáadott mezőt szeretnék feltölteni növekményes értékekkel, de valami miatt nem akarok constraint-t létrehozni, akkor T-SQL esetében a legegyszerűbb megoldás a következő.
DECLARE @sorszam INT = 0;
UPDATE [firg] SET @sorszam = [ujmezo] = @sorszam + 10;