Pokazywanie postów oznaczonych etykietą visual studio. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą visual studio. Pokaż wszystkie posty

niedziela, 26 lipca 2015

Jednolinijkowiec do czyszczenia projektu

Bierzesz Clean w VS i nie działa?
Nie wszystko usuwasz z binów.
Przyczyn może być wiele,
np. interakcja z inną aplikacją lub jak nasza aplikacja tworzy dodatkowe pliki w folderze bin.
Wtedy musisz usunąć wszystkie pliki w tym folderze.

Tutaj sprawdza się nasz jednolinijkowiec:
$path = "path_to_project"

gci -path $path -Recurse  
 | ? {$_.FullName.Contains("\bin\Debug\") 
   -or $_.FullName.Contains("\bin\Release\") } 
 | Remove-Item​

sobota, 9 maja 2015

For loop dla msbuild

Jak będziesz chciał uruchomić kilka razy dany proces to możesz wykorzystać polecenia for. Ja tak muszę z budowaniem projektów w .NETcie. Musze 2 razy uruchomić msbuild'a. Niektóre projekty nie maja referencji do innych projektów w solucji, ale do zbudowanych bibliotek w specjalnym folderze. Problem też może być jak jedno budowanie projektu potrzebuje dużo czasu (dajmy na to 25 minut) i w każdym momencie może wystąpić błąd.

Polecenie do dwukrotnego budowania jest poniżej:
for /L %n in (1,1,2) do ( msbuild myProject.sln /m )​

Takie proste, a tak upraszcza życie :)

poniedziałek, 28 lipca 2014

Produktywne makra Visual Studio

Chcę Ci przedstawić 2 makra, które pomagają mi w pisaniu kodu. Pierwsze makro dodaje nawiasy okrągłe do zaznaczonego tekstu w edytorze. Super się sprawdza makro dla kodu w PowerShell czy dla FSharp. Poniższy kod wystarczy zapisać w makrze VS:
Public Module BracketIt
    Public Sub AddBrackets()
        Dim s As Object = DTE.ActiveWindow.Selection()
        If s.Text.StartsWith("(") And s.Text.EndsWith(")") Then
            s.Text = s.Text.Substring(1, s.Text.Length - 2)
        Else
            s.Text = "(" + s.Text + ")"
        End If
    End Sub
End Module
Skrótem klawiszowym do wywołania tego makra mam ustawione na:
Shift+CTRL+A, Shift+CTRL+B,
Równie dobrze można było dodać nawiasy ostrokątne do edycji plików xml.
Drugim makrem jest asercja sprawdzająca czy wartość jest nullem:
Public Module AddAssert
    Public Sub IsNotNull()
        Dim s As Object = DTE.ActiveWindow.Selection()
        Dim assertStr As String = "Assert.IsNotNull"

        If s.Text.Length > 0 Then
            s.Text = assertStr + "(" + s.Text + ")"
        Else
            s.Text = assertStr
        End If
    End Sub
End Module
A skrótem klawiszowym jest:
Shift+CTRL+A, Shift+CTRL+A
Te 2 makra u mnie się sprawdzają. Jeśli masz swój pomysł na makro to podziel się nim.

sobota, 19 lipca 2014

Reinstalacja pakietów nuget

Pewnie miałeś taką potrzebę reinstalacji pakietu nuget z poziomu Visual Studio. Domyślnie, nuget nie ma możliwości reinstalacji pakietu w jednej komendzie. Poniżej zamieszczam skrypt do wykonania tego zadania:
function Reinstall-Package {
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Id,

        [Parameter(Mandatory = $true)]
        [string]
        $Version,

        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $ProjectName,

        [switch]
        $Force
    )

    if (-not $ProjectName) {
        $ProjectName = (get-project).ProjectName
    }
    Uninstall-Package -ProjectName $ProjectName -Id $Id -Force:$Force
    Install-Package -ProjectName $ProjectName -Id $Id -Version $Version
}

Przykład wywołania dla reinstalacji danego pakietu dla wszystkich projektów w solucji jest poniżej:
Get-Project -All |
     ?{ $_ | Get-Package | ?{ $_.Id -eq 'bla.bla.bla' } } |
         %{ $_ | Reinstall-Package -Id 'bla.bla.bla' -version 1.0.0 -Force }

Nie będziesz mógł załadować modułów, więc będziesz musiał wywołać skrypt Reinstall-Package z nugetowej konsoli lub możesz dodać tą funkcję do pliku z profilem:
notepad $Profile​

piątek, 28 lutego 2014

Generowanie start.bat dla projektu w VS

Czy kiedykolwiek musiałeś uruchomić aplikację z linii poleceń, która miała tak dużo linijek z argumentami, że nie mieściła się w oknie cmd? Ja miałe taką przyjemność i ten post jest właśnie o tym jak rozwiązać taki problem. Problem można rozwiązać za pomocą programu wsadowego (na ogół nazywam go start.bat). W nim zawarte jest polecenie startujące aplikację z argumentami. Aby debugować taką aplikację z poziomy VS to należy dodać parametry do właściwości programu. Poniżej są przedstawione 2 argumenty:
Napisałem skrypt w PS, aby wygenerować start.bat, który będzie mógł być wystartowany z tymi argumentami:
function New-StartBat
{
    param(
        [string] $csprojFilePath,
        [string] $outputDir = "." 
    )

    $userFileExtension = ".user"
    $startBatFileName = "start.bat"

    if(!(test-path $csprojFilePath))
    {
        throw "Wrong path to csproj file: $csprojFilePath"
    }

    [xml]$csprojContent = gc $csprojFilePath 
    [string]$outputType = ([string]$csprojContent.Project.PropertyGroup.OutputType)
               .Trim();

    if( -not($outputType -match  "Exe"))
    {
        throw "Output type is not exe"
    } 

    $assemblyName = ([string]$csprojContent.Project.PropertyGroup.AssemblyName)
                  .Trim()
    $startbarContent = "start /B $assemblyName.$outputType"
    $userFile = $csprojFilePath + $userFileExtension

    if(!(test-path $userFile))
    {
        Write-host "No user file for $csprojFilePath"
        $outputConditionStartBat  = join-path  $outputDir  $startBatFileName
        Write-Host "Genereting start.bat. Output is  $outputConditionStartBat"
        
        $startbarContent |
             out-file  $outputConditionStartBat  -Encoding ascii
    }
    else 
    {
        #region For each condition in user file
        [xml]$userFileContent = gc $userFile
        $userCondition = $userFileContent.Project.PropertyGroup.Condition

        $csProjConditions = $csprojContent.Project.PropertyGroup.Condition
        foreach($csprojCondition in $csProjConditions)
        {
            if($csprojCondition -eq $null)
            {
                continue;
            }

            [string]$conditionString =  ([string]$csprojCondition)
                                  .Trim()


            $indexOfequals = $conditionString.LastIndexOf("==")+2;
            if($indexOfequals -gt 2)
            {
                $conditionSimpleString = $conditionString
                     .Substring($indexOfequals)
                     .Trim()
                     .Replace("'","")
                     .Replace("|","_")

                if($contains =  $userCondition -contains $conditionString)
                {
                   $propertyGroup =  $userFileContent.Project.PropertyGroup | 
                                ?{$_.Condition -eq $conditionString }

                   $startArguments = $propertyGroup.StartArguments

                   $startBatContentWithArgs = $startbarContent+ " " + $startArguments 
           
                   $outputConditionStartBat  = join-path  $outputDir  ($conditionSimpleString + "_" +$startBatFileName) 

                   Write-Host "Genereting start.bat for $conditionSimpleString. Output file is $outputConditionStartBat"

                   $startBatContentWithArgs |
                    out-file  $outputConditionStartBat  -Encoding ascii
                }
            }

        }
        #endregion
    }
}
Dla różnej konfiguracji (Platform Configuration) możemy mieć różne parametry. Wygenerowany start.bat możemy uzyskać poprzez wywołanie polecenia:
New-StartBat -csprojFilePath "SuperProjectName.csproj" -outputDir ".\bin\Debug"

niedziela, 16 lutego 2014

Sprawdzanie sln'a

Często dostaje się błąd solucji z wiadomością:
"Error MSB4025: The project file could not be loaded. Data at the root level is invalid. Line 2, position 1."

Jest to błąd przy kompilowaniu solucji Visual Studio. Jest to problem z plikiem sln, najprawdopodobniej jest on związany z mergowaniem z innego brancha. Nie może zależ guid projektu lub guid jest z duplikowany. Aby sprawdzić czy wszystko jest ok, wystarczy uruchomić skrypt sprawdzający identyfikatory projektów:

function Test-Sln
{
    Param
    (
        [string] $slnPath
    )
    Write-Host "Testing sln in $slnPath"
    if(! (Test-Path($slnPath)))
    {
        throw "No file for $slnPath"
    }
    else
    {
        $slnContent = gc $slnPath
        $regex_id = '(\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\})'
        $regex_id_proj = '(\"{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}")'
        $matches_id =  [regex]::Matches($slnContent,$regex_id)
        $matches_id_proj =  [regex]::Matches($slnContent,$regex_id_proj)
        $dict_id =   Count-List $matches_id 
        $dict_id_proj = Count-List $matches_id_proj
        $returnValue = $true
        $keyChecked = 0
        $dict_id.Keys | foreach {
          $key=  $_
          $count = $dict_id[$key]
          $key_proj = '"' +  $key + '"'

           if( $dict_id_proj.Keys -contains $key_proj)
           {
                $count_proj = $dict_id_proj[$key_proj]
                if(($count -ne $count_proj) -and ($count_proj -gt 1))
                {
                     Write-Error "Wrong amount of reference for id $key. $count refers to $count_proj projects"
                     $returnValue=$false
                } 
                else
                {
                    Write-Host "Project with id $key is ok"
                }
           }
           else
           {
                Write-Error "Problem on id $key. There are $count refers in sln"
                $returnValue=$false
           }
           $keyChecked+=1
        }
        Write-host "Checked $keyChecked projects"
        return $returnValue
    }
}

Do tego potrzebujemy funkcję zliczającą ilość elementów w liście:
function Count-List ($List) #not explicitly list type 
{
$dic = @{}
$List| Foreach {
$id = $_.ToString()
    if($dic.ContainsKey($id))
    {
      $counter =  $dic.Get_Item($id);
      $counter +=1
      $dic.Set_Item($id,$counter);
    }
    else
    {
      $dic.Add($id,1)
    }
}
return $dic
}

Przykład wywołania jest poniżej:
if(!(Test-Sln "SuperSolution.sln"))
{
   throw "Errors in sln file" 
}

sobota, 15 lutego 2014

Skróty klawiszowe do VS

Jak wiadomo skróty klawiszowe są bardzo przydatne bo polepszają naszą produktywność, więc dobrze jest mieć skróty dopasowane do własnych potrzeb. Z indywidualnymi skrótami klawiszowymi jest jeden problem - osoba, która nie zna twoich skrótów może mieć problemy z obsługą środowiska programistycznego, już nie mówiąc o takim dziwnym tworze jak Ping-Pong Pair Programming. Ja mam skróty dopasowane do siebie i osoby na moim komputerze nie wiedzą jak ja mogę w ogóle kodować :)

Poniżej jest tabelka ze skrótami. Wywołanie polecenia składa się z kombinacji skrótów. Pierwszy skrót oznacza grupę, a drugi skrót już konkretne polecenie. Jeżeli chcesz odpalić polecenie debugowania to korzystasz z grupy RUN (CTRL + R) i polecenie Debug.Start (CTRL + D). Jeżeli bez chcesz uruchomić bez debagowania to pod tej samej grupie jest polecenie Debug.StartWithoutDebugging (CTRL + SHIFT + D).
Niektóre skróty już coś oznaczają (CTRL + F czy CTRL + A), więc musiałem dołożyć dodatkowy klawisz.



Plik z ustawieniami importu w Visual Studio jest poniżej:


Moje skróty ewoluują, Wcześniej miałem skrót do zen codingu, czy do GhostDoc'a. Czasami skróty są zależne od projektu.

niedziela, 19 stycznia 2014

Troszkę więcej niż VS Clean za pomocą PS

Komenda Clean w Visual Studio jest do czyszczenia zawartości ostatnio kompilowanego projektu. Nie oznacza to usunięcie całej zawartości folderów bin lub obj (lub subfolderów Debug/Releas), ale usunięcie tylko execów, dllki itp plików. Jest to problem kiedy chcemy mieć pewność, że wszystko zostało wyczyszczone lub kiedy musimy co jakiś czas archiwizować buildy. Nie wiem czy jest taka opcja w Visual Studio, aby za pomocą jednego polecenia dało się usunąć całą zawartość folderów bin i obj.

Standardowo musiałem ręcznie usuwać zawartość tych katalogów, ale pomyślałem sobie czy nie lepiej jest napisać skrypt w PowerShell, który mógłby tą czynność wykonywać za mnie.
function Remove-CsprojBuildFiles
{
    param(
    [string]$dirPath = "."
    )

    $buildDirName = "bin","obj"
    
    Write-Host "Deleting all `"$buildDirName`" dirs from $dirPath"

    $dirsThatAreContainers = get-childitem  $dirPath -Recurse | 
        ? { $_.PSIsContainer}  

     $dirsThatAreBuildNames =    $dirsThatAreContainers |
        ? {$buildDirName -contains $_.Name  }

        $dirsWhichContainsCsprojFile = $dirsThatAreBuildNames |
        ? { ((gci  -path $_.Parent.FullName) |?{$_.FullName.Contains("csproj") } | Test-Any )}


       $dirRemovedCounter= 0
       foreach($dir in $dirsWhichContainsCsprojFile)
       {
            $dirFullPath  = $dir.FullName
            if(Test-path $dirFullPath )
            {
                Remove-Item -Path $dirFullPath -Recurse
                $dirRemovedCounter+=1
            }
            else 
            {
                Write-Error "Dir $dirFullPath not exists"
            }
       }

    Write-Host "$dirRemovedCounter dirs removed from $dirPath"
}

Przydatna będzie funkcja test-any (taki Any z Linq), którą zapożyczyłem stąd.
function Test-Any() {
    begin {
        $any = $false
    }
    process {
        $any = $true
    }
    end {
        $any
    }
}

Wywołanie polecenie do usuwania katalogów jest następująca:
Remove-CsprojBuildFiles "C:\code\'folder_to_solution'"

sobota, 16 listopada 2013

Szukanie referencji w projektach

Na moje szczęście programuje w solucji (.sln), która posiada ponad 100 projektów (.csproj). Czasami zdarza się szukać projektu, który korzysta ze specyficznej biblioteki lub może być nie spójne w referencjonowaniu (jeden projekt jest zależny od paczki nuget, a drugi od biblioteki zapisanej na dysku).

Poniżej jest skrypt w powershellu, który dla danej wzorca nazw projektów przeszukuje wszystkie csproje i zwraca wszystkie referencje jakie są w tych plikach. Przeszukiwanie rozpoczyna się od ścieżki uruchomienia skryptu.
function Get-Reference
{
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] 
     [string] $projectNamePattern
)

BEGIN {
     Write-Host "Getting project References" 
     $projectFileExtension = ".csproj"
}

PROCESS{
    $searchingProjectPattern = $projectNamePattern + $projectFileExtension 
    $projectFullNames = Get-ChildItem  -Filter $searchingProjectPattern -Recurse | %{$_.FullName}

    foreach ( $projectFullName in $projectFullNames)
    {

        if( Test-Path $projectFullName)
        {       
            $projectName = [System.IO.Path]::GetFileNameWithoutExtension($projectFullName)
            [xml]$projectXml = Get-Content $projectFullName
            $itemGroups = $projectXml.Project.ItemGroup
            
            foreach ( $itemGroup in $itemGroups)
            {
                if($itemGroup.Reference -ne $null)
                {
                    foreach( $ref in $itemGroup.Reference)
                    {
                      $Object = New-Object PSObject -Property @{
                           RefPath = $ref.HintPath 
                           Name = $ref.Include
                           ProjectPath = $projectFullName
                           ProjectContent = $projectXml 
                           ProjectName = $projectName
                      }
                      $Object
                    }
                }
                
                if($itemGroup.ProjectReference -ne $null)
                {
                    foreach( $ref in $itemGroup.ProjectReference)
                    {
                      $Object = New-Object PSObject -Property @{
                           Name = $ref.Name 
                           RefPath = $ref.Include
                           ProjectPath = $projectFullname
                           ProjectContent = $projectXml 
                           ProjectName = $projectName
                      }
                      $Object
                    }
                }
            }
        }
    }
}

END{    
Write-host "`n`nHintPath => RefPath and Include => Name FOR Reference"
Write-host "Include => RefPath and Name => Name FOR ProjectReference"
}
}

A poniżej jest przykład użycia. Dajmy na to, że chcesz znaleźć referencje, które w ścieżce zawierają słowo Unity we wszystkich projektach, których nazwa zaczyna się od słowa ProjectA:
$projectNamePattern=  "ProjectA*"
$projectsPattern = @($projectNamePattern)
$projectsPattern  | Get-Reference | ? {$_.RefPath -ne $null} | ?{ $_.RefPath.Contains("Unity") } 



sobota, 26 października 2013

Statyczne analizy kodu w czasie

Tak się zastanawiam, co by było gdybyśmy wiedzieli jak z biegiem czasu zmieniały się nazwy klas, metod, słowa-klucze używane w naszych projektach. Co jakiś czas kod źródłowy byłby skanowany i informacje byłyby zapisywane do bazy. Z tego można by było przedstawić jaka klasa z biegiem czasu/releasów jest najbardziej popularna, czy nazwy obiektów są zależne od nazwy klas, ile jest komentarzy, jakie jest kodowanie tekstów itp. Mamy narzędzia, aby dokonać statycznej analizy kodu, ale jeszcze nie ma możliwości analizy tych wyników dla okresów czasowych.

Taki pomysł przyszedł mi do głowy przy oglądaniu prezentacji o n-gram. Bardzo zapraszam do obejrzenia:



niedziela, 20 października 2013

Fluent notation

Osoby, które kodują razem ze mną to od razu wiedzą, że jestem autorem danej części kodu. Pisany kod jest w stylu Fluent notation ( również zwany Fluent interface, gdyż przygotowujesz całe API). Co prawda wielu osobom się to nie podoba, ale chciałbym Ci przedstawić mój sposób widzenia:

Zapis LINQ w tradycyjny jednolinijkowy sposób pisania kod:
var firstNumberSelector = GetNumbers().Where(x => x > 0).
    ToLookup(x => x.ToString().First()).Select(x => new{x.Key, Items = x,Count = x.Distinct().Count()}).ToList();

A tutaj mój sposób pisania kodu:
var firstNumberSelector = GetNumbers()
                .Where(x => x > 0)
                .ToLookup(x => x.ToString()
                                .First())
                .Select(x=>new {x.Key, 
                                Items=x, 
                                Count=x.Distinct()
                                        .Count()})
                .ToList();
Projekt Fluent NHibernate korzysta z takiego zapisu i kod może wyglądać tak:
_SessionFactory = Fluently 
                .Configure() 
                .Database(MsSqlCeConfiguration 
                              .Standard 
                              .ConnectionString("Data Source=MyDb.sdf") 
                              .ShowSql()
                               )
                .Mappings(m => m.FluentMappings 
                           .AddFromAssemblyOf<NHibernateHelper>()) 
                .ExposeConfiguration(cfg => new SchemaExport(cfg) 
                                        .Create(true, true)   //create table in db 
                                        ) 
                .Cache(c=>c 
                    .UseSecondLevelCache() 
                    .UseQueryCache() 
                    .ProviderClass<HashtableCacheProvider>() 
                    ) 
                .BuildSessionFactory();
Dla mnie jest to kod bardziej czytelny i łatwiej mi jest określić jaka metoda jest uruchamiana dla danego obiektu.
Może Tobie też spodoba się taki sposób pisania kodu.

środa, 16 października 2013

Kolorowanie składni w Visual Studio

Edytor środowiska programistycznego (IDE) jest narzędziem, przy którym programista spędza 99% swojego czasu. Nie będę Ci mówił, że kolorowa składnia jest bardzo ważna. Celem tego wpisu jest przesłanie Ci ciekawych rozwiązań zestawu kolorów.

Już dawno temu Atwood, Hanselman czy Restrepo pisali o kolorowaniu środowiska programistycznego.

Na stronie Studio Style można pobrać style do edytora Visual Studio. Oprócz tego sam możesz stworzyć własny styl dopasowany do Twojego gustu. Oczywiście w każdym momencie można wrócić do domyślnego stylu.

Dla Visual Studio 2013 (i innych wersji od VS2010) jest jeszcze dodatek Color Theme Editor, który zmieniasz menu VS.

niedziela, 30 grudnia 2012

Ugly strings

Mam super pomysł jak ograniczyć pisanie kodu z duża ilością rozrzuconych tekstów. Wystarczy ustawić kolor czcionki w tekst edytorze na string tak, aby od razu tekst rzucał się w oczy :)


Efekt takiej zmiany wygląda okropnie :)



Taka czcionka wymusza na użytkowniku tworzenie regionów ze stałymi stringami w jednym miejscu albo wydzielenie tego tekstu do osobnego zasobu (klasy albo resourca).


czwartek, 13 grudnia 2012

Achievements w Visual Studio

Gry, aby były bardziej atrakcyjniejsze dla gracza, wprowadziło się termin achievementy, czyli osiągnięcia. Za każdą wykonaną specjalną rzecz, odblokowuje się pewne osiągnięcie. Później taki gracz może pochwalić się takim osiągnięciem przed kolegami czy na Facebooku. Osiągnięcia mają już minecraft, WoW czy Team Fortress (razem ze statystyką osiągnięć ). Możesz też zdobywać osiągnięcia na XBox360. Mimo tego, że kilka lat temu każdy potępiał ten pomysł, dziś dla większości graczy stanowią nieodłączną część zabawy. Do tego stopnia, że niektórzy z nich katują godzinami tytuły, na które nigdy by nawet nie spojrzeli, a wszystko po to, aby być na wyższym poziomie w rankingu.

Takie osiągnięcia są też dla Visual Studio. Powstał specjalny plugin do VS. Tak jak w grach komputerowych, za wykonaną czynność dostajemy osiągnięcie. VS Achievements nie tylko bawi, ale również uczy. Wykonując pewne osiągnięcia, możesz się nauczyć pewnych narzędzi, technologii czy sposobów pisania kodu. Jest tez ranking użytkowników VS.



Achievementy w grach wprowadzono po to, aby w prosty sposób mogły wydłużyć spędzanie czasu wolnego przed grą i zwiększyły więzi między graczem a grą. To samo dzieje się z VS. Dodatek do VS2012 można pobrać tutaj.

Nie mam wielkich osiągnięć, ale raz na jakiś czas będę się starał, aby wykonać jakieś 'głupie' osiągnięcie. Poniżej jest moja statystyka.

wtorek, 26 czerwca 2012

Co jest w Microsoft.VisualBasic

Microsoft.VisualBasic jest niedocenianą biblioteką. Ma bardzo dużo mechanizmów jakich standardowo potrzebuję. W tym poście postaram przedstawić ci te najciekawsze informację o niej.

W Microsoft.VisualBasic znajdują się typy związane z datą. To między innymi FirstDayOfWeek czy DateInterval. Już kilka razy pisałem enum z dniami tygodnia. Inne typy to m.in. TriState

Statyczne klasy Information oraz Interaction mają bardzo przydatne metody.
var isArray1= Information.IsArray(new int[2]); //true
var isArray2 = Information.IsArray(new ArrayList()); //false

var isDBNull = Information.IsDBNull( null); //false

var isReference1 = 
    Information.IsReference(new Int32()); //false

var isReference2 = 
    Information.IsReference(new string('a', 2)); //true

var getRGB = Information.RGB(255, 127, 0); //32767

var isNumeric1 = Information.IsNumeric("123.4"); //true
var isNumeric2 = Information.IsNumeric("123.4 num"); //false

var getTypeName = Information.TypeName(
                 new FieldAccessException());
                 //FieldAccessException

Interaction.Beep();
Interaction.InputBox("Info", "Title");

string decadeRange = 
        Interaction.Partition(80, 0, 99, 10); //" 80: 89"

Zawsze potrzebowałem metody, która sprawdzi mi czy dana zmienna jest DBNull. Interesowało mnie czy jest jakieś okienko z textboxem do wpisania tekstu. Teraz już wiem, że można skorzystać z Interaction.InputBox. Nietypowa metoda jest Interaction.Partition, która zwraca przedział.

Mamy możliwość operacji na stringu, gdzie standardowo w BCL nie było takiej możliwości. Możemy m.in. usuwać wolną przestrzeń tekstu z dowolnej strony.
string text = " te st ";
var r1 = Strings.LTrim(text);
var r2 = Strings.Trim(text);
var r3 = Strings.RTrim(text);

var rightChars = Strings.Right(text,3);
Co ciekawe, w tej biblioteczce znajduje się klasa odpowiedzialna za parsowanie pliku tekstowe. Jest nią TextFiledParser. Za pomocą tej klasy można w prosty sposób parsować plik CSV. Jeżeli będziesz chciał skopiować katalog do innego folderu to będziesz mógł wykorzystać metodę FileSystem.CopyDirectory albo FileSystem.DeleteDirectory, aby usunąć katalog. W tej bibliotece jest jeszcze wiele innych interesujące możliwości, choćby nawet możemy pobrać informacje o komputerze (Devices.ComputerInfo().TotalPhysicalMemory) czy możemy pingować (Network.Ping).
Ta klasa ma jeszcze jedna, bardzo interesująca możliwość. Możemy uruchomić aplikację w postaci singletona.

Testy dla DateTime.Now

Załóżmy, że mamy klasę, w której podajemy dowolną datę i w której jest metoda zwracająca różnicę w ilościach dni między tą datą, a dzisiejszą datą. W implementacji takiego zadania stosujemy statyczną właściwość DateTime.Now. To pole przy każdym wywołaniu ma inną wartość. Problem może pojawić się kiedy będziemy musieli przetestować metodę zwracającą ilość dni (GetDaysFromNow), która w różnych momentach zwraca inny wynik.
    public class NowDateTimeCalculator
    {
        public  DateTime dateTime { get; private set; }
        public NowDateTimeCalculator(DateTime dateTime)
        {
            this.dateTime = dateTime;
        }
 
        public int GetDaysFromNow()
        {
            var timeSpanDiff = DateTime.Now - dateTime;
            return (int)timeSpanDiff.TotalDays;
        }
    }
Mamy kilka sposobów, aby mieć możliwość przetestowania tej klasy. Pierwsza możliwość to zastosowanie pośredniej klasy (interfejsu), która mogłaby dostarczyć informacji o aktualnej dacie. Tworzymy interfejs zwracający datę (Now) oraz klasę implementującą to pole.
    public interface INowDateTime
    {
        DateTime Now { get; }
    }
    public class NowDateTime : INowDateTime
    {
        public DateTime Now
        {
            get { return DateTime.Now; }
        }
   }

Jeszcze będziemy musieli zmienić naszą klasę NowDateTimeCalculator. Parametr z interfejsem będziemy wprowadzać przez konstruktor. To nam uprości pisanie testów.
public class NowDateTimeCalculator
{
    public  DateTime dateTime { get; private set; }
    private INowDateTime nowDateTime;

    public NowDateTimeCalculator(
                               DateTime dateTime,
                               INowDateTime iNowDateTime)
    {
        this.dateTime = dateTime;
        nowDateTime = iNowDateTime;
    }
 
    public int DaysFromNow2()
    {
        DateTime now = nowDateTime.Now;
        var timeSpanDiff = now  - dateTime;
        return (int)timeSpanDiff.TotalDays;
    }
}
Wywołanie wygląda następująco:
var dateTimeToCheck= new DateTime(2012, 06, 26);

var calc = new NowDateTimeCalculator(
dateTimeToCheck, new NowDateTime());

var fromNow= calc.GetDaysFromNow();
W łatwy sposób będziemy mogli zamokować interfejs INowDateTime. Test może wyglądać następująco:
[Test]
public void Check_if_GetDaysFromNow_Work_With_Mock()
{
    DateTime date_26_06_2012 = new DateTime(2012, 06, 26);
    var nowDateTimeMock = new Mock<INowDateTime>();

    nowDateTimeMock
               .Setup(x => x.Now)
               .Returns(date_26_06_2012);

    var calcToday =
 new NowDateTimeCalculator(date_26_06_2012,
                           nowDateTimeMock.Object);
 
   Assert.AreEqual(0, calcToday.GetDaysFromNow());

    var calcTomorrow =
 new NowDateTimeCalculator(date_26_06_2012.AddDays(1),
                           nowDateTimeMock.Object);
 
   Assert.AreEqual(-1, calcTomorrow.GetDaysFromNow());

    var calcYesterday =
 new NowDateTimeCalculator(date_26_06_2012.AddDays(-1),
                           nowDateTimeMock.Object);

    Assert.AreEqual(1, calcYesterday.GetDaysFromNow());
}
Takie rozwiązanie jest łatwe w implementacji i w testowaniu, ale ma 2 ważne wady - tworzymy zależności od interfejsu INowDateTime oraz takie rozwiązanie musi być znane dla całego zespołu.


Innym rozwiązanie jest zastosowanie statycznej klasy.
internal static class SystemTime
{
    internal static Func<DateTime> 
                SetDateTimeNow = () => DateTime.Now;
 
    internal static DateTime Now
    {
        get { return SetDateTimeNow();}
    }
}


public class NowDateTimeCalculator
{
    public  DateTime dateTime { get; private set; }

    public NowDateTimeCalculator(DateTime dateTime)
    {
        this.dateTime = dateTime;
    }
 
    public int GetDaysFromNow()
    {
        DateTime now = SystemTime.Now;
        var timeSpanDiff = now  - dateTime;
        return (int)timeSpanDiff.TotalDays;
    }
}
Testy wyglądają następująco:
[Test]
public void Check_if_GetDaysFromNow_Work_With_SystemTime()
{
    DateTime date_26_06_2012 = new DateTime(2012, 06, 26);

    SystemTime.SetDateTimeNow =
               () => date_26_06_2012;
    var calcToday = 
        new NowDateTimeCalculator(date_26_06_2012);
    Assert.AreEqual(0, calcToday.GetDaysFromNow());


    SystemTime.SetDateTimeNow = 
              () => date_26_06_2012.AddDays(2);
    var calcTomorrow =
    new NowDateTimeCalculator(date_26_06_2012.AddDays(1));

    Assert.AreEqual(1, calcTomorrow.GetDaysFromNow());


    SystemTime.SetDateTimeNow = 
              () => date_26_06_2012.AddDays(-2);
    var calcYesterday =
    new NowDateTimeCalculator(date_26_06_2012.AddDays(-1));

    Assert.AreEqual(-1, calcYesterday.GetDaysFromNow());
}
Takie rozwiązanie pozwala na przyjemniejsze testowanie naszej klasy. Podoba mi się łatwa zmiana aktualnej daty poprzez wyrażenie lambda. 'Wadą' może być to, że korzystamy z globalnej i statycznej klasy, która musi być znana dla całego zespołu.


Jest jeszcze jedno rozwiązanie tego problemu. To rozwiązanie jest znane tylko dla osoby, która pisze testy. Zastosujemy porównywanie wartości uwzględniając przy tym błąd(margines) w wartościach. Do tego potrzebny nam będzie interfejs z informacją o marginesie.
    public interface IMarginComparer<T> 
    {
        T CompareMargin { get; }
    }
Dla naszych testów będziemy musieli zaimplementować dwie porównywające klasy. Porównamy wartości dla typu DateTime oraz int.
public class DateTimeComparer : 
             IComparer<DateTime>, 
             IMarginComparer<TimeSpan>
{
    public DateTimeComparer(TimeSpan marginOfDateTime)
    {
        this.CompareMargin = marginOfDateTime;
    }
 
    public int Compare(DateTime x, DateTime y)
    {
        var margin = x - y;
        if (margin <= CompareMargin)
        {
            return 0;
        }
        return new Comparer(CultureInfo.CurrentUICulture)
                                       .Compare(x, y);
    }
 
    public TimeSpan CompareMargin
    {
        get; private set; 
    }
}
 



public class IntComparer : 
             IComparer<int>, 
             IMarginComparer<int>
{
    public IntComparer(int margin)
    {
        CompareMargin = margin;
 
    }
    public int Compare(int x, int y)
    {
        var diff = x - y;
        if (diff <= CompareMargin)
        {
            return 0;
        }
        return new Comparer(CultureInfo.CurrentUICulture)
                                       .Compare(x, y);
    }
    public int CompareMargin { get; private set; }
} 
Mając margines możemy określić jaka może być różnica między aktualną, a oczekiwaną wartością. Aby obliczyć ten margines będziemy musieli znać datę pisania testu (dateTimeCodeWritten) i na podstawie tej daty obliczamy ilość dni na różnicę. Gdybyśmy tą różnicę nie obliczyli, a wpisalibyśmy stałą wartość to mogłoby się okazać, że któregoś dnia wszystkie nasze testy przestałyby przechodzić.
[Test]
public void Check_if_GetDaysFromNow_Work_With_Comparer()
{
var dateTimeCodeWritten = new DateTime(2012, 06, 25);
DateTime date_20_06_2012 = new DateTime(2012, 06, 20);

var calc = new NowDateTimeCalculator(date_20_06_2012);

var dateTimeCodeWrittenDiffTimeSpan =
 DateTime.Now - dateTimeCodeWritten;

int dateTimeDiffDays = 
(int)dateTimeCodeWrittenDiffTimeSpan.TotalDays;



var dateTimeComparer = 
new DateTimeComparer(new TimeSpan(dateTimeDiffDays, 0, 0,0));

Assert.IsTrue(dateTimeComparer
.Compare(calc.dateTime.AddDays(5), DateTime.Now) == 0);


var intComp = new IntComparer(dateTimeDiffDays);

Assert.IsTrue(intComp
.Compare(calc.GetDaysFromNow(), 5) == 0);  
   
}
Nie jest to najlepiej napisany test, ale można przetestować DateTime.Now. Zaletą tej techniki jest to, że nie musimy znać (zmieniać) implementacji kodu.

Te rodzaje rozwiązań można zastosować dla wielu innych statycznych metod w tym danych środowiskowych takich jak Environment.MachineName czy Thread.Sleep().

wtorek, 10 kwietnia 2012

Co nowego w Visual Studio 2011

I stało się. Musiałem przeinstalować mój stary system. Skoro wszystko usunąłem i na nowo musiałem zainstalować VS to pomyślałem sobie, że przetestuje Windows 8 oraz Visual Studio 2011. Pierwszy problem na jaki natrafiłem to instalacja systemu przez Boot Campa. Mam macbooka i obecna wersja Boot Campa nie pozwala na instalacje systemu nowszego niż Windows 7. Po tej operacji mogłem zaktualizować system do Windows 8. Na samym początku troszkę się przeraziłem bo nie wiedziałem, gdzie podział się stary dobry pulpit, a nawet nie wiedziałem jak się wyłącza komputer :)




Dla systemu Windows 8 jest dużo problemów z integracją z macbookiem. Są problemy m.in. z ustawieniem jasności (co jakiś czas ekran sam się gasi), działaniem sprzętu na usb czy po prostu klawiatura przestaje działać. Nie spotkałem się z tymi problemami w trakcie korzystania z Windows 7.


Po krótkim zapoznaniu się z systemem, zająłem się VS. Od razu widać, że instalacja całkowicie inaczej wygląda.



Ikony w VS są łagodniejsze dla oka. Kiedy w Solution Explorer najedziemy kursorem na zdjęcie to dostaniemy podgląd. Przy edycji tekstu z kolorem w pliku .css możemy zobaczyć ten kolor. Do VS dodano Unit Test Explorer (zamiast Test View i Test Results), w którym można odpalić testy oraz zobaczyć pokrycie kodu. W Solution Explorer można przeglądać metody danej klasy. W prawym górnym rogu jest Quick Lunch (Ctrl + Q), w którym możemy w szybki sposób wykonać polecenie VS. Jeżeli debugujemy stronę to już nie musimy za każdym razem ustawiać domyślną przeglądarkę, ale mamy możliwość wybory przeglądarki do debugowania. Możemy ekstraktować nie tylko metody, ale również kod w aspx do kontrolki. Poniżej zamieszczam zdjęcia, aby przybliżyć nowy wygląd VS.

















Więcej o nowych rzeczach w VS można znaleźć tutaj oraz tutaj. Mam nowe VS to mam nadzieję, że znajdę troszkę czas na naukę metro style:)

poniedziałek, 5 marca 2012

Metoda wypełniająca dla testów

Problem jaki napotykam w trakcie testowania aplikacji jest ciągłe wypełnianie tych samych pól, tymi samymi danymi. Aby zautomatyzować dany proces wystarczy dodać metodę wypełniającą.
Do metody wypełniającej dane (np. w formularzu) dodaje się atrybut Conditional("DEBUG"). Jeżeli flaga DEBUG nie jest ustawiona to metoda jest omijana w produkcyjnej binarce. Do tej metody dodatkowo dodaje warunek czy debugger jest podłączony. Debugger możne być podłączony w trybie Debug oraz Release. Dobrę praktyką jest dodanie informacji do okna Output z jakiej metody zostało dokonane wypełnienie, pisanie nazw tych metod z prefiksem "DEBUG_" oraz ustawienie dostępności na private.
Poniższy kod przedstawia ten mechanizm:
[Conditional("DEBUG")]
private void DEBUG_YourFillingMethod() //hack: Debug
{ 
    if (Debugger.IsAttached)
    { 
        Debugger.Log(0, this.ToString(), System.Reflection.MethodBase.GetCurrentMethod().Name);
        //todo: your filling code
    } 
} 


Dla aplikacji ASP.NET można zastosować HttpContext.Current.IsDebuggingEnabled.

poniedziałek, 27 lutego 2012

Jak przyspieszyć kodowanie cz.2



Już wcześniej pisałem o jednym ze sposobów przyspieszenia pisana kodu, a tym razem powiem więcej o Zen-Coding. Jest to silnik umożliwiający automatyczne generowanie kodu używanego w językach bazujących na SGML. Zapisując nazwy i specjalne symbole można w prosty sposób wygenerować wiele linijek kodu. Możliwości Zen-Codingu przedstawia poniższy filmik:



Jest dodatek do Visual Studio, a opis można znaleźć pod tym adresem.
Resharpera też można wzbogacić poprzez plugin. Lista innych programów jest przedstawiona na stronie Wikipedii

poniedziałek, 26 września 2011

Metro Style - Nowy interfejs dla Windows 8

Konferencja BUILD już się skończyła i to co Microsoft przygotował dla developerów całkowicie mnie zszokowało. Nowy interfejs dla Windows 8.



Microsoft zdał sobie sprawę, że system operacyjny dla różnych urządzeń powinien być jeden. Diametralne zmiany, które ujednolicają obsługę zwyczajnego notebooka, tableta czy telefonu komórkowego, mogą na samym początku wzbudzać strach. Pamiętam, że tak samo było z Ribbonem. Większość osób, krytykowało nowy interface, ale po któtkim czasie korzystania z niego uważali, że wstążka jest genialna :)





Więcej o Metro Style oraz o samym Windows 8 można znaleźć w poniższym filmiku.




Po obejrzeniu takiego filmiku, jedyne co mi się nasuwa to pytanie:
Jak nadążyć za technologią??