2010-05-17 9 views
5

Wie kann man in einer ASP.NET MVC-Anwendung Zufallszahlen generieren, wenn ich genau eine Zahl pro Anfrage benötige? Um eine Zufälligkeit ausreichender Qualität zu erhalten, ist es nach MSDN erforderlich, mehrere Nummern zu generieren, die ein einzelnes einmal erstelltes System.Random-Objekt verwenden. Da für jede Anforderung in MVC eine neue Instanz einer Controller-Klasse erstellt wird, kann ich kein privates Feld verwenden, das im Konstruktor des Controllers für das Objekt "Random" initialisiert wurde. Also, in welchem ​​Teil der MVC-App sollte ich das Random-Objekt erstellen und speichern? Ich speichere es zur Zeit in einem statischen Feld der Controller-Klasse und träge initialisieren es in der Aktion-Methode, die es verwendet:Generierung von Zufallszahlen in MVC-Anwendungen

public class HomeController : Controller 
{ 
    ... 

    private static Random random; 

    ... 

    public ActionResult Download() 
    { 
     ... 

     if (random == null) 
      random = new Random(); 

     ... 

    } 
} 

Da das „random“ Feld durch mehrere Instanzen der Controller-Klasse zugegriffen werden kann, ist es Ist es möglich, dass sein Wert beschädigt wird, wenn zwei Instanzen gleichzeitig versuchen, ihn zu initialisieren? Und noch eine Frage: Ich weiß, dass die Lebensdauer der Statik die Lebensdauer der Anwendung ist, aber im Falle einer MVC-App, was ist das? Ist es von IIS Start bis zum Herunterfahren von IIS?

Antwort

10

Idealerweise möchten Sie eine Instanz der Klasse Random länger als die Lebensdauer einer einzelnen Seite verwalten. Tun Sie nicht tun Sie dies, indem Sie es in eine statische Variable setzen; Die Klasse Random ist nicht threadsicher und dies führt zu Problemen. Von the docs:

Für alle Instanzmitglieder ist nicht garantiert, dass sie threadsicher sind.

Mein Liebling Ansatz ist die RandomGen2 Wrapper-Klasse aus dem Microsoft ParallelFX Team (die wirklich weiß, was sie mit einem Gewinde tun), die eine Instanz pro Thread verwendet für (meist) Lock-frei und Thread-sichere Zufallszahl .

public static class RandomGen2 
{ 
    private static Random _global = new Random(); 
    [ThreadStatic] 
    private static Random _local; 

    public static int Next() 
    { 
     Random inst = _local; 
     if (inst == null) 
     { 
      int seed; 
      lock (_global) seed = _global.Next(); 
      _local = inst = new Random(seed); 
     } 
     return inst.Next(); 
    } 
} 

die Sie dann rufen Sie einfach wie folgt:

var rand = RandomGen2.Next(); 

Sie müssen möglicherweise zusätzliche Methoden hinzufügen, um die anderen Random Methoden wickeln Sie zugreifen möchten, und ich würde einen besseren Namen so vorschlagen als ThreadSafeRandom, aber es zeigt das Prinzip.

1

Sie könnten einen statischen Konstruktor in HomeController haben, um zu sparen, dass Sie ihn in jeder Methode faul initialisieren müssen. Dies stellt ziemlich sicher, dass die Random nur einmal initialisiert wird (das erste Mal, wenn es aufgerufen wird).

public class HomeController : Controller 
{ 
    ... 

    private static Random random; 

    static HomeController() 
    { 
     random = new Random(); 
    } 

    ... 

    public ActionResult Download() 
    { 
     ... 

     //use random - its already created. 


     ... 

    } 
} 
2

Es sei denn, Sie werfen ein paar schnelle Demo oder etwas zusammen, würde ich diese Verantwortung in einer Dienstleistung oder Infrastrukturebene setzen (dh nur eine andere Klasse) und lassen Sie es die Lebensdauer der Zufallszahlengenerator verwalten. Es ist nicht wirklich der Controller-Job, um dies trotzdem zu verwalten - und Sie werden sich nicht darum kümmern, wenn/wenn Sie einen anderen Controller haben, der eine Zufallszahl benötigt.