Archive for tag: .NET 4.5

Placer dine Entity Framework Migrationsfiler i en alternativ mappe

Jeg har i min anvendelse af Entity Framework haft behov for at placere mine migrationsfiler et andet sted end de, som standard, bliver placeret.

Dette kan gøres i constructoren af DbMigrationsConfiguration (Configurations.cs). Denne fil bliver genereret, når man slår migrations til (Enable-Migrations i PMC).

Egenskaben hedder MigrationsDirectory og skal sættes til en relativ sti ift. den DLL, som migrations ligger i. Følgende er et eksempel på dette:

internal sealed class Configuration 
    : DbMigrationsConfiguration<ShopContext>
{
    public Configuration()
    {
        MigrationsDirectory = "Ef\\Migrations";
        MigrationsNamespace = "Shop.Repositories.Ef.Migrations";
    }
}

I ovenstående har jeg dels, med MigrationsDirectory, sat mappen migrationfiler skrives til, dels, med MigrationsNamespace, det namespace, som nye migrations anvender. Det kan være relevant, at namespace følger placeringen af de fysiske filer og det har man, som her demonstreret, mulighed for også at styre.

Gem afledte egenskaber i databasen med Entity Framework

Jeg sidder og arbejder med CodeFirst i Entity Framework 6 (EF6) og det er sådan set også helt fint. Jeg dog hidtil haft en lille udfordring med, at beregnede egenskaber ikke rigtig kom med ned i databasen og jeg dermed skulle have indlæst objektet i hukommelsen for at kunne arbejde med disse egenskaber.

Det er i mere komplekse tilfælde nok også det rigtige at gøre, men for nogle typer data, kan det være rigtig praktisk, at få den beregnede værdi gemt i databasen, så der kan foretages forespørgsler på disse egenskaber direkte i databasen, uden man skal have dem ind i hukommelsen.

Hidtil har et objekt haft nogenlunde flg. struktur:

public class Kamp
{
    public List<Periode> Perioder { get; set; }

    public DateTime Startdato { get; set; }
    public DateTime Slutdato
    {
        get
        {
            return 
                Startdato.AddTicks(
                    Perioder.Sum(p => p.Loebetid.Ticks));
        }
    }
}

public class Periode
{
    public TimeSpan Loebetid { get; set; }
}

Her har jeg en Kamp som kan have flere perioder. Hver periode har en løbetid på et givet tidsinterval. Slutdatoen bliver derfor summen af løbetider for alle perioder. Hvis jeg ændrer løbetiden på en periode (via objektet af typen Kamp), vil slutdatoen ændre sig automatisk, hvilket jo er rigtig rart!

Nu vil jeg imidlertid gerne finde alle de kampe som er igang, dvs. den aktuelle dato ligger mellem startdatoen og slutdatoen. For at kunne opfylde dette krav er jeg med den aktuelle struktur, nød til at indlæse alle kampe, hvor den aktuelle dato er større end eller lig med startdatoen og derefter gennemløbe alle kampe (i hukommelsen) for at se, om slutdatoen er overskredet. Det er måske ikke det store problem, hvis man har 100 kampe, men hvis man har en mio eller flere, har jeg på fornemmelsen, at det bliver en ret tung omgang. Desuden vil det med tiden blive et større og større problem, da flere og flere kampes startdato vil ligge i fortiden og dermed blive indlæst i denne forspørgsel. Der må altså tænkes lidt alternativt...

En løsningsmulighed

Gem slutdatoen uden at gøre den til simpel egenskab med get og set. Dette gøres ved at lade slutdatoen bibeholde sin getter og så tilføje en tom setter. På den måde vil EF generere egenskaben i databasen og gemme den værdi som getteren genererer, når objektet gemmes.

Når objektet indlæses igen, vil værdien bare blive skrottet, da slutdatoen jo beregnes på grundlag af de indlæste perioder (husk at "Include" Perioder i din query!).

Kamp kommer til at se således ud i stedet:

public class Kamp
{
    public List<Periode> Perioder { get; set; }

    public DateTime Startdato { get; set; }
    public DateTime Slutdato
    {
        get
        {
            return 
                Startdato.AddTicks(
                    Perioder.Sum(p => p.Loebetid.Ticks));
        }
        set {}
    }
}

Kører du nu Add-Migration fra PMC (Package Manager Console) i VS, vil der blive genereret et felt til slutdato i Kamp-tabellen. Dette felt kan nu anvendes til at foretage forespørgsler på, for den gemte værdi er den beregnede slutdato på det tidspunkt, hvor Kampen sidst blev gemt. Det kunne f.eks. se således ud i LINQ to EF:

using(var ctx = new DataContext())
{
    var igangvaerendeKampe =
        ctx.Kampe
           .Include(k => k.Perioder)
           .Where(k => k.Startdato <= DateTime.Now
                       && k.Slutdato >= DateTime.Now)
           .ToList();

     return igangvaerendeKampe;
}

Med ovenstående har du filtreret kampene i databasen og nok gjort noget godt for dit systems performance og skalerbarhed i fremtiden...

Konklusion

Alt efter ens krav og behov til funktionalitet, kan det godt betale sig, at gå lidt på kompromis med de strikse normaliseringskrave der ellers bør herske ifm. databasedesign. I dette tilfælde vil jeg gætte på, at der på sigt er vundet en del performance og dermed et problem afværget - inden det opstod.

Manglende intellisence i Visual Studio 2013

Jeg sad idag og boksede med et problem i Visual Studio, hvor jeg ikke kunne få intellisence til at fungere. Dette til trods for, at jeg godt kunne complie projektet og køre det i IIS Express.

Der var røde bølgestreger under et namespace i using-sektionen og en type kunne ikke findes. Da projektet kunne compile, tænkte jeg det måtte have noget med udviklingsmiljøet at gøre.

Jeg havde lavet en cleanup og forsøgt at køre "rebuild" på hele min solution, men lige lidt hjalp det. 

Jeg søgte på problemet og fandt dette link: https://stackoverflow.com/questions/21471887/visual-studio-2013-intellisense-stops-working-for-asp-net-mvc5-controllers/23983883#23983883

Løsningen var, i mit tilfælde, helt enkelt, at lukke VS 2013 og slette filen <projektnavnet>.v12.suo og starte VS 2013 op igen.

DotNetOpenAuth og MVC 5

Jeg har et projekt som kører .NET 4.5 og MVC 5, hvor der også er noget OAuth involveret. I den forbindelse forsøger jeg at lave noget Reflection, men får en fejl som omhandler sikkerhed. Det viser sig at DotNetOpenAuth ikke er compatibel med MVC 5, fordi der er sket en ændring i måden sikkerheden håndteres i MVC 5 ifht. MVC 4. Desværre vedligeholdes DotNetOpenAuth ikke aktivt i øjeblikket, men der er dog lavet en eller anden løsning til problemet... jeg har bare ikke været i stand til at få den til at fungere.

Workaround

I stedet for at gennemløbe alle typer i alle assemblies, nøjes jeg med at gennemløbe dem i den assembly jeg har brug for (hvilket så gør, at jeg undgår projektet med DotNetOpenAuth-dll'erne) og problemet er dermed gemt af vejen.

Der findes som sagt en opdatering til DotNetOpenAuth som er rettet mod MVC 5 og den kan hentes via NuGet:

Install-Package DotNetOpenAuth.Mvc5