.NET tanker & tips

.Net, jQuery og andre nørdede emner

SDD day 2 & 3 - Impressions and takeaways

May 23
by steffen 23. May 2019 20:23

As a part of my attendance at the SDD Conference in London, I have decided to write small blog posts summing up some of the impressions from the talks I attend. The format will be rougher since the posts have been written during the day and finished in the evening at the hotel.

This post is a summary of my impressions and takeaways from the second and third day of the SDD conference.

Version ASP.NET Core APIs

Philip Japiske (@skimedic) gave a walkthrough of the new versioning capabilities in .NET Core. Every public-facing API's are facing the same issues with regards to versioning. An API - like any other code - will almost always be a living thing, where new requirements will show up and change the endpoints and functionality.

In order not to introduce breaking changes to the existing API, that is being consumed by customers, a proper versioning strategy must be put into place. This was previously a manual process that the team agreed on, but with the new Microsoft.AspNetCore.MVC.Versioning.ApiExplorer assembly, this can be streamlined significantly by expressing which endpoints are supported in which versions of the API. By decorating a controller with the [ApiVersion("2.0")] data annotation, you can express which versions of the API the controller is currently supporting. It is even possible to have multiple decorations if the controller is supported in multiple version of the API.

Build RabbitMQ Microservices in C#

Queuing technology is here to stay. Richard Blewett gave a short introduction to one of the biggest actors in this technology stack: RabbitMQ. To support big spikes in load it is a common approach to implement a queue that can handle the messages. This can be handy if the request does not need an instant response (e.g. sending some data for processing). Each request can be put into the queue and the consumers can handle one at a time without being a bottleneck. RabbitMQ is written in Erlang and is optimized for performance - rumour has it that Google has a queue implementation that handles 1.000.000 messages per second!

One of the neat tricks that RabbitMQ has is the ability to add a 'topic'. The consumers can filter on this topic so that they only pick up the messages that they actually want to. It is a very simple approach, but the simplicity is what makes it so powerful. RabbitMQ is definitely on my list of things I want to learn more about.

Vue.js

One of the things that I am focusing on learning at the moment, is the different frontend javascript frameworks. Vue.js is Google's take on this and it plugs in very nicely with the ASP.NET MVC background that I have. Vue is extremely lightweight coming in at only 33k and also has its own logic with regards to manipulating the DOM, which means that you can also cut away jQuery saving even more bandwidth. It can work side by side with jQuery, but only if you actually have a specific need for jQuery (maybe legacy code). 

Vue supports all the cool stuff like two-way data binding and can easily be plugged into an existing Razor based MVC view. This means that you can use you traditional view models from MVC, to hand down data to Vue, which will use the data on the client-side. 

The first impression was very good and I will be digging into the pros and cons of Vue vs React and Angular soon.

UI Design crash course for software developers

I am not a designer, but as Joe Natoli (@joenatoli) pointed out it is very often that the developers (backend and frontend both) end up doing design work. In this talk Joe went through a few guidelines that will help anybody - even a developer - design UI that isn't horrible (might not be beautiful UI, but at least not horrible). 

Each element in the page must be in harmony with the other elements. This essentially means that it is important to align the elements vertically and horizontally as much as possible. If you draw a line from each vertical and horizontal edge on an element, this line should align with as many other lines in the screen. Also, make sure that the spacing between each element is the same.

If certain elements belong together, make sure to enhance this relationship by grouping the elements close together. This provides structure to the page and will make it easy for the user to understand the relationship between elements. Grouping can also be done with contrasting colours, but distance is superior for this, so choose to place elements close together overusing colour if possible.

One should be very aware of the concept of contrast. Black and white is the best contrast you can get and provides the user with very easy reading experience. Be aware of using very vibrant colours. Red on yellow might have great contrast, but can be very straining for the eye to read.

One should always try to keep the 'noise' as low as possible. Often there is simply too much content on a page, making it seem very messy and noisy. 'Less is more' is the goto motto, when deciding what to have on the page.

Tags: , , , , ,

.NET Core | RabbitMQ | SDD | Vue.js

Let StructureMap be your Alfred

July 11
by steffen 11. July 2018 14:03

Alfred Pennyworth - http://www.mothershiptoys.com/hot-toys-mms236-batman-armory-with-bruce-wayne-and-alfred-pennyworth.html

In my previous post I addressed the concept of dependency injection (DI) and what the advantages are by using this technique. In this post we will look at little closer at some concrete examples on how to use StructureMap to help resolve dependencies (injecting the correct objects to each class that has an dependency on an interface). 

Like all good super heroes a coder need a tech saavy helper, that make sure the hero has all the tools needed for the job. James Bond has Q, Batman has Alfred and .NET has StructureMap. 

What is StructureMap?

StructureMap is a DI framework, that rely heavly on conventions to resolve the dependecies a class has. It is preferable to choose the convention based approach, in order to save as much time on the tedious configuration. The main convention in StructureMap is that it rely on the name of the interfaces and classes to "connect the dots". The common naming convention is to prefix all your interfaces with "I" and StructureMap leverages this convention.

If a class is depending on an interface IBar, StructureMap will look for a concrete class called Bar. This means everytime you have a one to one realationship between an interface and a class (IBar to Bar), this is handled automatically, without any extra configuration. There will of course always be some configuration - especially if the dependecies does not fall into the default convention - we'll take a look at this next.

Configuring StructureMap in ASP.NET MVC

I'll use an ASP.NET MVC project as an example. First we have to install StructureMap in the solution. This is easiest done from the package manager console:

Install-Package StructureMap.MVC5

At the time of writing I'm running MVC version 5 and hence is using the package targeted this version. After all the dependecies has been installed a new folder called "DependencyResolution" has been created in the root of the web application project. In this folder open the DefaultRegistry.cs file:

public class DefaultRegistry : Registry {
        public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                    scan.With(new ControllerConvention());
                });
        }
    }

The body of the call to Scan is where the container is configured. The most important part here is to configure which assemblies StructureMap should search when resolving a dependency. The call to scan.TheCallingAssembly() means that StructureMap will be able to resolve all classes in the current assembly (all classes in the web project in this case).

Usually the solution will consist of more than one project. This could be a class library with all the domain classes. In this case StructureMap must be configured to include these assemblies when resolving a dependency. Let's illustrate this with an example.

Including assemblies

In the solution on the right, there are two projects: a class library called Domain, which contains an interface and a concrete implementation of the interface, and a MVC project. Assuming that the MVC project has a reference to the Domain project and uses an instance of the IFoo interface, we must set up StructureMap to find the correct assembly and resolve the correct concrete implementation of IFoo. 

The controller could look something like this:

public class HomeController : Controller
    {
        private IFoo _foo;

        public HomeController(IFoo foo)
        {
            _foo = foo;
        }

        public ActionResult Index()
        {
            return View();
        }
    }

At this moment StructureMap does not know about the Domain assembly and will throw an exception (a very confusing "Class has not parameterless constructur" error, which does not indicate that it is a StructureMap issue). In order to get StructureMap to inject an instance of Foo, when the controller is created and is requires an instance of IFoo, StructureMap must be configured to look in the Domain assembly. This is done fairly simple by telling StructureMap to add the assembly containing the required classes and interfaces. There is a few ways to do this, but the two most simple ways are adding it by the assembly name or pointing to a type (ie. class, interface) in the assembly. Just remember that if you change the assembly name/move the type to another assembly, StructureMap will not be able to find the correct assembly needed for the dependency injection.

The code for configuring StructureMap to look in the Domain assembly in the small test project looks like this (code using the method pointing to a type is commented out):

public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
					scan.With(new ControllerConvention());
                    //scan.AssemblyContainingType<Foo>();
                    scan.Assembly("Domain");
                });
        }

When conventions doesn't cut it

Obviously the example above is very basic, but it really get you off the ground in a hurry and let's you focus on the code instead of the configuration. When you run into scenarios that does not fit into the common StructureMap conventions a little configuration is needed. But fortunately the good guys and girls at StructureMap made sure that this is very easy to set up using the fluent API.

Let's assume that we have a concrete implementation of IFoo that is called Bar. Obviously this does not match the StructureMap conventions (IFoo -> Foo) and in order for StructureMap to handle this, we must configure this. The configuration is quite simple and readable and must be added after the call to Scan. The syntax is For<TheInterfaceName>().Use<TheTypeYouWantInjected>();. It looks like this:

public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
					scan.With(new ControllerConvention());
                    //scan.AssemblyContainingType<Foo>();
                    scan.Assembly("Domain");
                });
                For<IFoo>().Use<Bar>();
        }

Now each time a class requires a concrete type of IFoo StructureMap will inject an instance of Bar. You can even add logic in the configuration, if the application have different dependencies in different situations. This could look something like this:

public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
					scan.With(new ControllerConvention());
                    //scan.AssemblyContainingType<Foo>();
                    scan.Assembly("Domain");
                });
            if (someBoolValue)
            {
                For<IFoo>().Use<Bar>();
            }
            else
            {
                For<IFoo>().Use<Foo>();
            }
        }

I have used this approach with a webshop where there were different business rules (ie. VAT, tax, delivery) depending on which country the customer was from. 

Tags: , ,

ASP.NET | ASP.NET MVC | Dependency Injection

Let StructureMap be your Alfred

July 11
by steffen 11. July 2018 14:01

Alfred Pennyworth - http://www.mothershiptoys.com/hot-toys-mms236-batman-armory-with-bruce-wayne-and-alfred-pennyworth.html

In my previous post I addressed the concept of dependency injection (DI) and what the advantages are by using this technique. In this post we will look at little closer at some concrete examples on how to use StructureMap to help resolve dependencies (injecting the correct objects to each class that has an dependency on an interface). 

Like all good super heroes a coder need a tech saavy helper, that make sure the hero has all the tools needed for the job. James Bond has Q, Batman has Alfred and .NET has StructureMap. 

What is StructureMap?

StructureMap is a DI framework, that rely heavly on conventions to resolve the dependecies a class has. It is preferable to choose the convention based approach, in order to save as much time on the tedious configuration. The main convention in StructureMap is that it rely on the name of the interfaces and classes to "connect the dots". The common naming convention is to prefix all your interfaces with "I" and StructureMap leverages this convention.

If a class is depending on an interface IBar, StructureMap will look for a concrete class called Bar. This means everytime you have a one to one realationship between an interface and a class (IBar to Bar), this is handled automatically, without any extra configuration. There will of course always be some configuration - especially if the dependecies does not fall into the default convention - we'll take a look at this next.

Configuring StructureMap in ASP.NET MVC

I'll use an ASP.NET MVC project as an example. First we have to install StructureMap in the solution. This is easiest done from the package manager console:

Install-Package StructureMap.MVC5

At the time of writing I'm running MVC version 5 and hence is using the package targeted this version. After all the dependecies has been installed a new folder called "DependencyResolution" has been created in the root of the web application project. In this folder open the DefaultRegistry.cs file:

public class DefaultRegistry : Registry {
        public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                    scan.With(new ControllerConvention());
                });
        }
    }

The body of the call to Scan is where the container is configured. The most important part here is to configure which assemblies StructureMap should search when resolving a dependency. The call to scan.TheCallingAssembly() means that StructureMap will be able to resolve all classes in the current assembly (all classes in the web project in this case).

Usually the solution will consist of more than one project. This could be a class library with all the domain classes. In this case StructureMap must be configured to include these assemblies when resolving a dependency. Let's illustrate this with an example.

Including assemblies

In the solution on the right, there are two projects: a class library called Domain, which contains an interface and a concrete implementation of the interface, and a MVC project. Assuming that the MVC project has a reference to the Domain project and uses an instance of the IFoo interface, we must set up StructureMap to find the correct assembly and resolve the correct concrete implementation of IFoo. 

The controller could look something like this:

public class HomeController : Controller
    {
        private IFoo _foo;

        public HomeController(IFoo foo)
        {
            _foo = foo;
        }

        public ActionResult Index()
        {
            return View();
        }
    }

At this moment StructureMap does not know about the Domain assembly and will throw an exception (a very confusing "Class has not parameterless constructur" error, which does not indicate that it is a StructureMap issue). In order to get StructureMap to inject an instance of Foo, when the controller is created and is requires an instance of IFoo, StructureMap must be configured to look in the Domain assembly. This is done fairly simple by telling StructureMap to add the assembly containing the required classes and interfaces. There is a few ways to do this, but the two most simple ways are adding it by the assembly name or pointing to a type (ie. class, interface) in the assembly. Just remember that if you change the assembly name/move the type to another assembly, StructureMap will not be able to find the correct assembly needed for the dependency injection.

The code for configuring StructureMap to look in the Domain assembly in the small test project looks like this (code using the method pointing to a type is commented out):

public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
					scan.With(new ControllerConvention());
                    //scan.AssemblyContainingType<Foo>();
                    scan.Assembly("Domain");
                });
        }

When conventions doesn't cut it

Obviously the example above is very basic, but it really get you off the ground in a hurry and let's you focus on the code instead of the configuration. When you run into scenarios that does not fit into the common StructureMap conventions a little configuration is needed. But fortunately the good guys and girls at StructureMap made sure that this is very easy to set up using the fluent API.

Let's assume that we have a concrete implementation of IFoo that is called Bar. Obviously this does not match the StructureMap conventions (IFoo -> Foo) and in order for StructureMap to handle this, we must configure this. The configuration is quite simple and readable and must be added after the call to Scan. The syntax is For<TheInterfaceName>().Use<TheTypeYouWantInjected>();. It looks like this:

public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
					scan.With(new ControllerConvention());
                    //scan.AssemblyContainingType<Foo>();
                    scan.Assembly("Domain");
                });
                For<IFoo>().Use<Bar>();
        }

Now each time a class requires a concrete type of IFoo StructureMap will inject an instance of Bar. You can even add logic in the configuration, if the application have different dependencies in different situations. This could look something like this:

public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
					scan.With(new ControllerConvention());
                    //scan.AssemblyContainingType<Foo>();
                    scan.Assembly("Domain");
                });
            if (someBoolValue)
            {
                For<IFoo>().Use<Bar>();
            }
            else
            {
                For<IFoo>().Use<Foo>();
            }
        }

I have used this approach with a webshop where there were different business rules (ie. VAT, tax, delivery) depending on which country the customer was from. 

Tags: , ,

ASP.NET | ASP.NET MVC | Dependency Injection

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

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