.NET tanker & tips

.Net, jQuery og andre nørdede emner

Url.Action i javascript fil

September 29
by steffen 29. September 2014 10:58

Min klare holdning er at man skal forsøge at opdele sin kode i filer, som klart og tydeligt afgrænser funktionaliteten. Det betyder f.eks. at jeg helst ikke vil have javascript kode liggende direkte i mine views, da jeg synes det skaber unødigt rod. Men hvis man ligger sin javascript i dedikerede javascript-filer, som inkluderes på de relevante sider, kan man ikke nyde godt at MVC's forskellige helper funktioner. Det kunne f.eks. være Url.Action, som hjælper med at returnere den rette URL til en controller og action metode. MVC parser ikke javascript filer og derfor vil det ikke fungere at kalde funktionen her.

Et klassisk eksempel kunne være et ajax kald, som skal bruge en given URL. Men ved at tilføje en data-attribut på et HTML element i viewet, kan man på en let måde gøre den rette URL til rådighed for javascripten. 

Lad os sige at vi har en knap, som laver et ajax kald, når man klikker på den:

<button id="testButton">Click me!</button>

Og i den tilhørende javascript fil, vil vi gerne hooke knappen op til at lave ajax kaldet:

$(function() {
    $('#testButton').click(function(){
        $.getJSON("/MyController/MyActionMethod",
            function (data) {
                });
            }
        );
    });
});

Jeg vil rigtig gerne bruge Url.Action til at få genereret min URl til getJSON, for at lade MVC administrere hvad den korrekte URL på serveren bliver. Det kan heldigvis løses nemt og elegant, ved at tilføje URL'en som en data-attribut i view'et, som herefter kan hentes i javascripten. Det ser således ud:

<button id="testButton" data-request-url="@Url.Action("MyActionMethod", "MyController")">Click me!</button>

Og i javascript filen:

$(function() {
    $('#testButton').click(function(){
        $.getJSON($(this).attr("data-request-url"),
            function (data) {
//Do something }); } ); }); });

Tags: , ,

ASP.NET MVC | Javascript | jQuery

Fejl ifm. opdatering af service reference

June 11
by steffen 11. June 2014 14:34

I forbindelse med et projekt, skulle der bruges en web service fra en 3. partsleverandør. Det er jo heldigvis nemt at tilføje og bruge web services i Visual Studio. Et højre-klik på projektet i Project exploreren, vælg "Add Service Reference" og så er man stort set kørende. Denne gang virkede det også fint, lige indtil der var kommet en udvidelse til web servicen og der derfor skulle laves en opdatering af referencen, for at Visual Studio kunne opdatere proxy klasserne. 

Pludselig kastede Visual Studio en fejl:

Custom tool error: Failed to generate code for the service reference 'ServiceReference1'.  Please check other error and warning messages for details

Ikke en specielt hjælpsom besked. Som fejlbeskeden foreslog blev de andre warnings/errors undersøgt, men uden at det gjorde problemet særligt meget nemmere:

Warning1Custom tool warning: Cannot import wsdl:portType

Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter

Error: Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.

Efter at have googlet lidt rundt og have mistet en hårtot eller to, lykkedes det dog at finde en løsning. Det viser sig at problemet bundede i at Visual Studio forsøgte at genbruge nogle assemblies, som ikke længere var gyldige. Problemet blev nemt løst ved at højre-klikke på referencen i Project Exploreren, vælge "Configure Service Reference" og fjerne fluebenet i "Reuse types in reference assemblies". Hvis referencen tilføjes på ny, findes denne valgmulighed under "Advanced" i dialog boksen, hvor URL'en på servicen indtastes.

 

Tags: ,

General

Windows authentication på localhost

May 07
by steffen 7. May 2014 12:13

Jeg oplevede forleden et problem med en af mine intranet applikationer, som kører med ASP.NET's Windows authentication, hvor applikationen bruger dét Windows login som man er logget på computeren med. 

Når jeg startede applikationen fra Visual Studio, fik jeg en fejlbesked:

Access is denied


Når jeg gik på applikationen i produktionsmiljøet, fungerede det fint og der var således ikke nogen problemer med den måde jeg var logget ind på min computer på. Jeg travede nettet igennem for at finde en løsning og det lykkedes heldigvis at finde frem til en let løsning

Man skal bare vælge projektet i Solution exploreren i Visual Studio og trykke F4, for at åbne Properties. Her kan man vælge at enable eller disable Windows Authentication. Den skal naturligvis være sat til Enabled.

Opsætning af Windows Authentication

Desværre er det ikke lykkedes mig at hitte ud af, hvor denne indstilling pludselig var blevet ændret, omend jeg i en anden forbindelse har været inde og rode med en globale applicationhost.config fil, hvor man tilsyneladende kan sætte denne værdi globalt. Jeg syntes dog ikke at jeg har været inde og pille ved nogen værdier, som har med ovenstående problem at gøre - omvendt har jeg taget fejl før :-)

Tags: , , , ,

General

Tving IE compability mode off

April 25
by steffen 25. April 2014 12:01

Jeg løb for nyligt ind i et problem, hvor Internet Explorer 9's compability mode som standard var slået til, hvis man gik på intranet sider. Det gav mildest talt nogle forfærdelige konsekvenser for Bootstrap layoutet som jeg bruger til siden.

Heldigvis er det relativt nemt at angive i HTML-dokumentets HEAD sektion, hvilken rendering metode der skal benyttes. Ved at tilføje nedenstående meta tag, kan man angive til IE, hvilken rendering der skal benyttes.

<meta http-equiv="X-UA-Compatible" content="IE=9" />

Ovenstående angiver at IE skal rendere siden som IE 9. Man kan også angive at IE altid skal bruge den nyeste rendering:

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

"edge" angiver at der altid skal benyttes den nyeste rendering, men man skal bruge denne indstilling varsomt, da det potentielt kan have konsekvenser når der kommer en ny version af IE, hvor renderingen af indholdet på siden ændrer sig ift. ældre versioner. 

Ovenstående er fundet på Stack Overflow.

Tags: , ,

General

Editor templates i ASP.NET MVC

April 07
by steffen 7. April 2014 13:44

Som en naturlig efterfølger til min sidste post om display templates, kommer der her en lille kort blog post om editor templates. 

Display templates bliver brugt til at vise visse typer af data i modellen. Det vil sige at de bruges når man i viewet skriver

@Html.DisplayFor(model => model.SomeProperty)

Hvor SomeProperty har sat UIHint propertyen, der matcher med display templaten. På samme vis fungerer editor templates, men i stedet for at fungere til visning af en værdi, er det til redigere af en værdi. Det vil sige at det bruges når man i viewet skriver:

@Html.EditorFor(model => model.SomeProperty)

Nøglen er igen at man templaten bliver brugt, hvis property'en har sat UIHint attributten.

Display templates skal placeres i en mappe kaldet 'DisplayTemplates', som enten skal ligge i én af view-mapperne som tilhører en bestemt controller eller i Shared mappen under 'Views'. Editor templates fungerer på samme måde. Den eneste forskel er at mappen i stedet - ikke overraskende - skal hedde 'EditorTemplates'.

Et eksempel på at bruges editor templates er en property som holder en DateTime. Hvis man gerne vil bruge en form for javascript datepicker, kan det laves i en editor templates, således at der for alle DateTime properties som har sat UIHint, vil der automatisk blive brugt editor templaten, som sørger for at initialisere datepickeren.

Tags: , ,

ASP.NET MVC

DisplayTemplates i ASP.NET MVC

April 01
by steffen 1. April 2014 16:53

Generelt er ASP.NET MVC et rigtig stærkt framework til hurtigt at opbygge CRUD funktionalitet op imod en domænemodel. Når man tilføjer en controller kan Visual Studio lave action metoder til alle de gængse CRUD operationer. Men alt efter hvor kompleks modellen er, har Visual Studio brug for lidt hjælp til at få det hele med. 

Man kan ofte komme ud for at man har et specielt krav til hvordan en bestemt property i modellen skal vises. Det kan hurtigt udvikle sig til en masse vedligeholdelse i de forskellige views, hvor den samme kode bliver gentaget. Som udviklere bør vi altid forsøge at lave DRY kode. Heldigvis har MVC en måde der nemt kan hjælpe med at centralisere den kode der bruges til at vise en bestemt property/data type. 

Metoden kaldes display templates og er ganske nemt at bruge, hvilket nemt kan illustreres med et lille eksempel. Vi har nedenstående Person klasse:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    [UIHint("Int")]
    public int Age { get; set; }
}

Den kvikke læser vil straks have bemærket UIHint attributten på Age property'en - det skal jeg nok vende tilbage til. Hvis vi antager at vi gerne vil have at der står " år gammel" efter alderen (f.eks. 32 år gammel), når vi udskriver Age-property'en til et view, kan det nemt klares. Først oprettes der en ny mappe under "Views -> Shared", som hedder "DisplayTemplates". Man kan også godt placere den under en af mapperne under "Views" som hører til en controller, men så vil Display Templaten kun bruges i denne controllers context.

I den nye DisplayTemplates oprettes der et nyt partial view - lad os kalde det "Age", for tydeligt at vise at det bruges ifm. visningen af Age-property'en. I det nye partial view, skal vi øverst angive hvilken type modellen har, ligesom man også skal i et almindelige strongly typed view. Age er en integer og derfor skriver vi "@model int" i øverste linje. Herefter kan vi skrive alt den markup og Razor, som vi gerne vil have der vises hver gang Age-property'en skal vises.

Dette eksempel er meget simpelt:

@model int
@Model år gammel

Og nu kommer magien - ved at sætte [UIHint("Int")] attributten på en property af typen, vil MVC altid bruge vores lille DisplayTemplate, til at vise værdien. Det vil sige at hver gang vi i et view, som bruger et Person object som model, kalder nedenstående:

@Html.DisplayFor(model => model.Age)

Vil vi få følgende resultat:

X år gammel

Tags: ,

ASP.NET MVC

Rettigheder i ASP.NET MVC

March 21
by steffen 21. March 2014 23:13

Det er dejligt nemt at sætte rettigheder på et MVC projekt. Det er faktisk muligt at styre det på applikations niveau, som vi kender det fra webforms, hvor man laver en overordnet definition af hvem der må komme ind og hvem der ikke må i web.config. Men det er også muligt at gøre det på controller niveau, hvor man opsætter krav til hvilke brugere, der overordnet har adgang til controllerens action metoder. Og sidst men ikke mindst kan man opsætte rettighedskravene på action metode niveau. 

Det er endvidere så smart skruet sammen, at hvis der er angivet på controller niveau (se nedenfor), at kun brugere med rollen X må tilgå kontrolleren, kan man opsætte strammere regler for en action metoderne og kun give bruger Y lov til at tilgå dén. Således vil bruger Y kunne kalde action metoden, men kun så længe at Y også overholder rettighederne, der er angivet på controller niveau. På denne måde kan man opsætte et hierarki af sikkerhed, hvor man kan stramme reglerne på metode niveau, såfremt det er nødvendigt.

Der er to metoder man kan authorize brugere på: Users og Roles

Users er angivelse af de brugernavne som har adgang. Her er det vigtigt at notere at hvis man benytter windows authentication mode (det vil sige at der tjekkes op imod et Active Directory - typisk en intranet løsning), skal man også angive domænet som brugeren er logget på. 

Roles er angivelsen af de brugerroller, som har adgang. Det er den mest elegante måde at styre adgangen på, da det ikke kræver en recompilering, hvis der er ændringer til hvem der har adgang. Hvis man benytter Users, skal der en recompilering og redeploy til, når der er ændringer til rettigheder. Hvis man tjekker på Roles, skal man blot tilføje eller fjerne brugere til de respektive roller og herefter vil det automatisk slå igennem, næste gang de tilgår applikationen.

Eksempel på rettigheder i web.config

<system.web>
    <compilation debug="false" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Windows" />
    <authorization>
<allow roles="BrugereMedDenneRolleHarAdgang" /> <deny users="*" /> </authorization> </system.web>

Her angiver vi at vi bruger windows authorization - det vil sige at der tjekkes op imod et Active Directory, med den bruger som er logget ind på computeren. Herefter angives det at alle brugere, der har rollen 'BrugereMedDenneRolleHarAdgang' har adgang til applikationen og alle andre har ikke adgang.

Eksempel på rettigheder på controller niveau

[Authorize]
public class TestController : Controller
{
    [Authorize(Roles = "GruppeSomHarAdgang")]
public ActionResult Index() { return View(); }

public ActionResult SomeOtherMethod()
{
return view();
}
}

Her har alle brugere som er logget ind adgang, men kun de brugere der har gruppen 'GruppeSomHarAdgang' har adgang til at eksekvere Index action metoden. Bemærk at hvis man benytter Authorize attributten uden at angive Roles eller Users, tolkes det som at brugeren blot skal være logget ind. Det vil sige alle ikke-anonyme brugere får adgang.

En lille gotcha

Hvis man ved en fejl forsøger at validere brugere op imod en gruppe, der ikke findes får man en lidt kryptisk fejlbesked:

The trust relationship between the primary domain and the trusted domain failed

Det kan virke noget forvirrende og hvis det sker, skal man lige tjekke at man har stavet rolle-navnet rigtigt i Authorize attributten og selvfølgelig at rollen rent faktisk eksisterer.

 

Tags: , ,

ASP.NET MVC

Dropdown i partial view

March 12
by steffen 12. March 2014 12:31

I dette indlæg vil jeg kort ridse op, hvordan man kan lave et partial view med en dropdown liste, som man kan bruge på tværs af siderne i applikationen. Fordelen ved denne tilgang er at der kun er ét sted man skal rette, hvis der er rettelser til hvordan dropdown listen skal vises.

Lad os lave et eksempel hvor der skal laves en dropdown liste med butikker. Til det har vi en Shop klasse, som ser således ud:

public class Shop
{
   public int Id { get; set; }
   public string Name { get; set; }
}

Det partielle view skal have en ViewModel, som skal bestå af en liste med de objekter, som skal vises i dropdown'en, det vil sige en liste af Shop objekter i vores tilfælde, samt en property til at holde den værdi man vælger i dropdown'en, således at vi kan "fange" værdien når der postes på siden, som benytter det partielle view.

public class ShopDropdownViewModel
    {
        public ShopDropdownViewModel()
        {
            this.Shops = new List<Shop>();
            this.SelectedShop = new Shop();
        }

        public IEnumerable<Shop> Shops { get; set; }

        public Shop SelectedShop { get; set; }
    }

Nu har vi en model som vores partielle view kan bruge. Derfor opretter vi nu et partial view, ved højre klikke på 'Shared' under Views og vælge 'Add -> View'. Det er god stil at starte filnavne på partial views med underscore. Et passende navn kunne f.eks. være '_ShopDropdown'. I Template vælger vi 'Empty (without model)' og vælg 'Create as a partial view'. Visual Studio laver nu en ny tom fil. Øverst i denne fil skal vi angive hvilken type view'et forventer at få som model. Derfor tilføjer vi nedenstående som øverste linje i filen:

@model YourNamespace.Models.ShopDropdownViewModel

Nu skal vi tilføje koden, som generer dropdownlisten. Det klares ret nemt vha. en html-helper:

@Html.DropDownListFor(m => m.SelectedShop, new SelectList(Model.Shops, "Id", "Name"))

Hvis vi kigger på syntaxen, betyder den første del 'm => m.SelectedShop' at den værdi man vælger i dropdown'en skal indsættes i SelectedShop property'en på vores ViewModel klasse. Herefter danner vi et nyt SelectList objekt, hvor vi giver listen med Shops, samt to tekststrenge som angiver navnet på property'erne i listens objekter, som peger på hhv. værdien der skal bruges som value i de enkelte option-elementer og teksten som skal vises i option-elmenterne.

Nu er det partielle view klar til brug. Vi kan nemt bruge det i andre views ved at generere et ShopDropdownViewModel objekt, hvor vi populerer listen med Shops, med de butikker vi gerne vil vise i dropdown'en, i controlleren. Dét objekt vil typisk være en del af det pågældende views ViewModel. Når det er på plads er det nemt at kalde det partielle view i det pågældende view.

@Html.Partial("_ShopDropdown",Model.ShopDropdownViewModel)

Det første parameter er navnet på det partielle view - det vil sige filnavnet uden .cshtml - og det andet parameter er ViewModel objektet som det partielle view skal bruge til at danne dropdownlisten.

Tags: , ,

ASP.NET MVC

Transformation af web.config

November 01
by steffen 1. November 2013 13:57

Når man skal deploye en web applikation er der ofte en masse ændringer i web.config'en, fordi man benytter nogle bestemte indstillinger når man udvikler lokalt ift. hvad der skal bruges på produktionsserveren. Den mest oplagte er selvfølgelig at fjerne debug=true fra compilation elementet, samt at ændre connectionstring elementet, hvis man har siddet og udviklet op mod en test-database. 

Det sørger Visual Studio heldigvis for at klare på en nem og bekvem måde. I Solution exploreren kan man folde web.config ud, ligesom man kan med aspx-filer for at se deres codebehind. Når man gør det er der en web.release.config og en web.debug.config. Disse to filer bruges til at lave ændringer i web.config alt efter om man har valgt debug eller release, når man compiler projektet. Hvis du har flere compilation profiler, kan du få tilføjet én til hver af profilerne, ved at højreklikke på web.config og vælge "Add config transform".

Transformationsfilerne ligner meget en web.config, bare med mindre indhold. Man angiver de forskellige steder i web.config'en man gerne vil lave ændringer, ved at opbygge xml-strukturen lige som den ser ud i web.config. Det vil sige at hvis man f.eks. gerne vil ændre noget i en af elementerne under <connectionStrings>, skal der være de samme elementer som i web.config'en. 

<?xml version="1.0"?>
  <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings>
      <add name="database_connection"
        connectionString="Data Source=MySource;Initial Catalog=DbName;Persist Security Info=True;User ID=MyDbUser;Password=MyPassword"
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
    </connectionStrings>
    <system.web>
      <compilation xdt:Transform="RemoveAttributes(debug)" />
    </system.web>
  </configuration>

Der er mange forskellige muligheder for at manipulere indholdet i web.config'en, men den mest normale er, som vist ovenfor, at man gerne vil benytte en anden connection string, når man deployer til produktionsmiljøet. Først skal XML-elementet findes (i dette tlifælde et givent add-element under connectionstring elementet). Man angiver dette vha. 'xdt:Locator' hvor man giver værdien 'Match(name)'. Det betyder at der matches på attributen 'name' på det pågældende element. Herefter angives det hvad man ønsker at gøre ved elementet og angives 'xdt:Transform', hvor man angiver værdien 'SetAttribute'. 

Man kan teste de forskellige transformationer ved at i Visual Studio højre klikke på den transformation man vil se (f.eks. filen web.release.config for at se release transformationen) og vælge 'Preview Transform'.

Tags:

ASP.NET | ASP.NET MVC