Archive for tag: .NET

Entity Framework Code First Migrations på en Umbraco installation

Arbejder du med kode til en Umbraco installation, som anvender Entity Framework 6 og Code First Migrations, kan det være du kommer i samme situation som mig, hvor Migrations ikke bliver kørt ifm. Web Deploy.

Jeg kunne ikke umiddelbart finde frem til årsagen, men jeg fandt en vej udenom problemet. Det er ikke en perfekt løsning, da den involverer noget manuelt arbejde, men nogle vil påstå, at det er en bedre metode i et produktionssetup, hvor du gerne vil have 100% kontrol over, hvad der sker med din database og hvornår det sker.

Til formålet anvender jeg Package Manager Console, hvor jeg skyder en update-database af mod min produktionsdatabase server og får et script genereret, som jeg så kan fyre af i MSSMS (mod min produktionsdatabase server).

Inden jeg kunne gøre dette, løb jeg dog ind i et par problemer. Det første var at DbProviderFactory skal være til stede i min web.config, for ellers ville update-database kommandoen ikke køre. Dette betyder helt konkret, at flg. skal tilføjes i system.web-sektionen af web.config:

  <system.data>
    <DbProviderFactories>
      <remove invariant="System.Data.SqlClient" />
      <add name="SqlClient Data Provider"
           invariant="System.Data.SqlClient"
           description=".Net Framework Data Provider for SqlServer"
           type="System.Data.SqlClient.SqlClientFactory, System.Data, 
                 Version=2.0.0.0, Culture=neutral, 
                 PublicKeyToken=b77a5c561934e089" />
    </DbProviderFactories>
  </system.data>

Når dette er på plads kan update-kommandoen udføres med flg. format:

update-database -script -projectname <projektet med entity datacontext>
     -connectionstring <connectionstring til produktionsdatabasen>

NB: Det er naturligvis en forudsætning, at du har adgang til din produktionsdatabase server fra din udviklermaskine (hvilket nok i større setups ikke er sandsynligt), da update-database skal læse migrationshistorikken fra databasen, for at vide, hvilke migrationer der skal udføres.

Efter denne kommando er udført, får du, i Visual Studio, et vindue med SQL-koden, som modsvarer migrationen. Denne kode kan så udføres i MSSMS mod produktionsdatabasen og migrationen er dermed gennemført.

Hvis du føler dig rigtig modig, kan du undlade -script parameteren og få udført opdateringen direkte fra PMC'en. Det sparer dig for at skulle udføre scriptet i MSSMS efter det er genereret i PMC, men det fjerner også muligheden for, at kontrollere, hvad migrationsscriptet rent faktisk har tænkt sig, at gøre ved din produktionsdatabase...

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.

Datastyrede routes i ASP.NET MVC

I disse tider med større og større fokus på SEO-venlighed, kan det være rart, hvis man kan basere sine url'er på data fra eksempelvis sin database.

Man kunne have en produktdatabase, hvor produktet har en unik (url-venlig) streng, som kan anvendes til at finde produktet. I et setup uden nogen specielle regler, kan dette være en udfordring at få gjort stømlinet. Med en routedefinition som denne:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { 
        controller = "Home", 
        action = "Index", 
        id = UrlParameter.Optional 
    }
);

hvor produkt-controlleren hedder ProduktController, action-metoden til at vise et produkt hedder Vis og produktets urlnavn ligger i id-parameteren, kunne url'en til et produkt se således ud:

/produkt/vis/stor-tallerken-med-sort-kant

Det kan være fint nok ift. SEO, men det ville jo være rart, hvis man kunne slippe for de lidt overflødige controller- og action-tekster, så url'en kom til at se således ud i stedet:

/stor-tallerken-med-sort-kant

Dette kan opnåes med en RouteConstraint og en wildcard-urlparameter. Route-mapping kunne derfor se således ud:

routes.MapRoute(
    name: "Default",
    url: "{id}",
    defaults: new { controller = "Produkt", action = "Vis" },
    constraints: new { id = new ProduktRouteConstraint() }
);

Nu kaldes ProduktRouteConstraint's Match-metode hver gang routing kaldes. Denne Match-metode skal så håndtere et opslag i produktdatabasen for at se, om den angivne id refererer til et eksisterende produkt. Match-metoden returnerer en bool, der indikerer om den aktuelle route skal anvendes eller ej, dvs. om produktet findes i dette tilfælde. En implementering kunne se således ud:

public class ProduktRouteConstraint : IRouteConstraint
{
    public bool Match(
            HttpContextBase httpContext,
            Route route,
            string parameterName,
            RouteValueDictionary values,
            RouteDirection routeDirection
        )
    {
        var svc = new ProduktService();
        var id = values["id"].ToString();
        return svc.FindesProduktMedUrlnavn(id);
    }
}

Match-metoden fyrer op under en produktservice-klasse, der, via metoden FindesProduktMedUrlnavn, kan svare på, om produktet med det angivne navn (fra url'en) findes eller ej.

Hvis url'en ikke er et match, fortsætter routing-systemet videre til næste regel (som det altid har gjort). Derfor skal denne regel helst ikke være den sidste regel i rækken af routing-regler. 

En ting man skal være opmærksom på med denne metode er, at routing-systemet aktiveres hver gang der foretages et request til sitet. Derfor skal routingconstraints helst være så optimerede som muligt, da sitet ellers vil virke langsomt. Det vil med andre ord være en god idé, at overveje caching af grundlaget for disse specielle routingregler, i dette eksempel produktets urlnavne.

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.

CSV parser til .net projekter

Det sker at man falder over en opgave, som kan løses ved at importere data fra en CSV-fil (kommaseparerede data) og i den forbindelse kunne man jo strikke sin egen parser sammen.

CSV-formatet er dog lidt mere tricky end man lige umiddelbart skulle forestille sig, så derfor bør man nok kigge på markedet, om der ikke findes en anden som allerede har strikket noget sammen (og som er testet grundigt igennem af mange andre).

Det var netop hvad jeg gjorde. Jeg fandt frem til denne på CodeProject: http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader

Den ser ud til at fungere fint og kan installeres i et .NET-projekt vha. NuGet, så det bliver vist ikke ret meget lettere at komme igang. Oveni det er den ret simpel at anvende.

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

 

Upload af store filer i asp.net

Det er ofte et krav, at der skal kunne uploade filer til en webapplikatione. Dette er heldigvis ret let, når der er tale om en ASP.NET webapplikation. Det giver dog til tider lidt problemer, når der skal kunne uploades større filer (over 4 Mb), men ikke større end at det også kan løses. Jeg vil her kort beskrive en løsning.

Ændr indstillingerne

Det er faktisk ikke mere kompliceret end, at ændre indstillingerne i web.config. Helt konkret er det elementet httpRuntime (som ligger under system.web-elementet), der skal ændres således:

<httpRuntime 
    maxRequestLength="8096" 
    requestTimeout="120" 
    requestLengthDiskThreshold="8096" />

Hvis du kører på en IIS 7+ skal du muligvis også tilføje  security-elementet (som ligger under sektion system.webServer-elementet) således:

<security>
  <requestFiltering>
    <requestLimits maxAllowedContentLength="8096000" />
  </requestFiltering>
</security>

Nu skulle du være i stand til at uploade 8 Mb i stedet for 4 Mb. Husk at requestTimeout skal være længere end den længste forventede tid der går fra en klient starter upload til det er færdigt, ellers fejler uploadoperationen med timeout i stedet.

Advarsel

Vær også opmærksom på, at højre grænser for mængden af data der kan sendes til en side i din webapplikation, kan have alvorlige konsekvenser for din sårbarhed overfor DOS-angreb, da det bla. vil optage en tråd i puljen indtil data er modtaget og behandlet. Mange af denne slags kald vil derfor risikere vil udsulte puljen af tråde til at behandle requests, hvilket medfører, at din applikation er nede. Derfor skal det overvejes nøje, hvis denne grænse sættes op til store værdier. Jeg har ikke fået undersøgt, om dette kan afbødes med async/await, men det var måske en mulighed. Dog vil du måske stadig ende med en ekstraregning for trafik efter et DOS-angreb af denne slags...

Se også

http://forums.asp.net/t/1810854.aspx er en tråd med reflektioner omkring dette emne og inspirationskilden til dette indlæg.

Ikke standard 404 handler i ASP.NET MVC 3

Jeg vil, i tilfælde med indhold som ikke eksisterer, gerne returnere en side med statuskoden 404 (af hensyn til SEO), men samtidig gerne have, at siden ikke er en standard 404-side. Dette sker som udgangspunkt ikke i ASP.NET MVC3, da MVC 3 kobler en standard handler på fejlsider. Dette er også helt fint, men i disse særlige tilfælde, hvor jeg gerne selv vil styre 404-siden, er det træls.

Efter lidt søgen rundt på nettet (primært StackOverflow), fandt jeg, i en kommentar til et indlæg, en henvisning til denne egenskab på Response-objektet:

Response.TrySkipIisCustomErrors = true;

Den sørger åbenbart for, at den standard handler som er opsat, ikke aktiveres, når jeg sætte Response.StatusCode = 404.

Dermed kan jeg med følgende kode i min Controller's Action, få vist en ikke standard 404-side (som, i mit tilfælde, ligger i et view med navnet "NotFound"):

public ActionResult View(int id)
{
   Response.Status = "404 - Not Found";
   Response.StatusCode = 404;
   return View("NotFound");
}

Jeg kunne måske endda pakke denne kode ind, så jeg kan kalde den fra forskellige controllers, som så kunne have deres egne specialiserede views til NotFound-håndtering...

Bundling og minification i ASP.NET MVC 3

Har man arbejdet med ASP.NET MVC 4, ved man der findes en bundling og minification mulighed indbygget i frameworket. Dette er umiddelbart nyt ifht. MVC 4 og fandtes ikke som udgangspunkt i MVC 3.

Man kan dog benytte sig af disse features i et MVC 3-projekt, hvis man benytter .NET 4, man skal bare lige selv sætte de grundlæggende ting op i sit projekt (registrering af bundles, inkludering af System.Web.Optimization og kald af bundles i sit layout).

Du kan finde en nærmere forklaring til dette i artiklen om "Using MVC 4 bundling and minification in an MVC 3 project".

Se også

Denne artikel på MSDN er endnu mere uddybende omkring emnet.

"Javascript minification in MVC 3" kommer også ind på, hvordan man kan overstyre bundling- og minificationprocessen, så man kan smide sine egne regler ind i mix'et.

Hvis du arbejder med webforms kan du måske få lidt ud af kigge på, hvordan man kan benytte scriptmanager til at kombinere scriptfiler.