6

No es posible establecer una aplicación .NET completa en otra cultura que no sea la cultura de perfil de usuario en .net. La forma adecuada de controlar cultureinfo parece ser el uso de métodos dedicados en objetos como DateTime.Enhebrado, CultureInfo .net, TPL, PLINQ

Sin embargo, cuando se trata de cantidades masivas de código heredado (no todos los códigos bajo su control) esto no es posible. Por lo tanto, uno puede, por ejemplo, crear una subclase/envoltorio para Thread och Threadpool y establecer el culturalinfo requerido antes de que se ejecute el delegado, o se puede requerir que el delegado contenga un conjunto de la cultura. (Difícil de validar y propenso a errores) ...

En cuanto a TPL, más específicamente PLINQ, sin embargo, me resulta difícil, si no imposible, cambiar la configuración cultural de forma centralizada.

¿Alguna sugerencia que se ocupe de overriding thread/application-cultureinfo en el código heredado?

Gracias!

Respuesta

6

Cuando se inicia un subproceso, su cultura se determina inicialmente mediante GetUserDefaultLCID desde la API de Windows. No encontré manera (supongo que no hay forma) de anular este comportamiento. Lo único que puede hacer es establecer la cultura del hilo después.

Escribí una extensión. Para ello:

public static class ParallelQueryCultureExtensions 
{ 
    public static ParallelQuery<TSource> SetCulture<TSource>(this ParallelQuery<TSource> source, CultureInfo cultureInfo) 
    { 
     SetCulture(cultureInfo); 
     return source 
      .Select(
       item => 
        { 
         SetCulture(cultureInfo); 
         return item; 
        }); 
    } 

    private static void SetCulture(CultureInfo cultureInfo) { 
     if (Thread.CurrentThread.CurrentCulture != cultureInfo) { 
      Thread.CurrentThread.CurrentCulture = cultureInfo; 
     } 
    } 
} 

tanto, si utiliza sólo después de fraccionar la fuente original mediante .AsParallel() obtendrá lo que desea.

CultureInfo kaCulture = CultureInfo.GetCultureInfo("ka-Ge"); 

    int[] array = new int[100]; 
    Random random = new Random(); 
    int index =0; 
    Array.ForEach(array, i => { array[index++] = index;}); 

    array 
     .AsParallel() 
     .SetCulture(kaCulture) 
     .ForAll(
      i => 
       { 
        Thread.Sleep(random.Next(5)); 
        Console.WriteLine("Thread-{0} \t Culture-'{1}' \t Element-{2}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.CurrentCulture, i); 
       }); 

    Console.WriteLine("Press any key to quit"); 
    Console.ReadKey(); 
+1

Esto funcionó perfecto, al menos hasta que me pueda mover a .NET 4.5 :) – Porco

1

Sorprendentemente esta extensión no retrasó mis consultas PLINQ - que podía medir.

En una consulta compleja con muchas llamadas AsParallel(), es posible que deba llamar a SetCulture() después de cada AsParallel(). No estoy seguro si hay un lugar para agregar .SetCulture() (o un punto para AsParallel para el caso), así que acabo de agregar .SetCulture() después de cada llamada AsParallel(), y funcionó muy bien.

Además, también puede considerar configurar CurrentUICulture. p. El uso de PLINQ para buscar una colección Business Object para encontrar Business Objects con reglas rotas (CSLA framework, Broken Rules collection) provocará que los hilos PLINQ (Thread Pool threads) busquen recursos de cadena localizados (nuestros requisitos) para establecer la cadena de error (RuleArgs. Descripción).

Solo necesitaba extender la extensión ParallelQueryCultureExtensions. Esto funcionó bien para mí (tengo que usar VB.NET, por lo tanto, ...):

Public Module PLINQExtensions 

    <Extension()> _ 
    Public Function SetCulture(Of TSource)(ByVal source As ParallelQuery(Of TSource), ByVal culture As CultureInfo, ByVal uiCulture As CultureInfo) As ParallelQuery(Of TSource) 
     SetCulture(culture, uiCulture) 
     Return source.Select(Function(item) 
           SetCulture(culture, uiCulture) 
           Return item 
          End Function 
          ) 
    End Function 

    <Extension()> _ 
    Private Sub SetCulture(ByVal culture As CultureInfo, ByVal uiCulture As CultureInfo) 
     If (Not Thread.CurrentThread.CurrentCulture.Equals(culture)) Then 
      Thread.CurrentThread.CurrentCulture = culture 
     End If 

     If (Not Thread.CurrentThread.CurrentUICulture.Equals(uiCulture)) Then 
      Thread.CurrentThread.CurrentUICulture = uiCulture 
     End If 
    End Sub 

End Module 
2

que comienzan con .NET 4.5 usted será capaz de definir la cultura de todo un dominio de aplicación (véase el párrafo titulado "Core Nueva Características y mejoras ").

0

Cuando creo una Tarea usando el TPL, paso el Culture del UI-Thread actual al Subproceso en segundo plano usando un State Object.

private void WorkProcessingAsync(IWorkItem workItem) 
     { 
      IsBusy = true; 
      /* ============================= 
      * Create a TPL Task and pass the current UiCulture in an state Object to resolve the correct .resx file for translation/globalisation/Multila`enter code here`nguate features in Background Thread 
      * ==============================*/ 
      Task<IWorkItem> task = Task.Factory.StartNew((stateObj) => 
      { 
       // here we are already in the task background thread 
       // save cast the given stateObj 
       var tuple = stateObj as Tuple<IWorkItem, CultureInfo>; 


      Debug.Assert(tuple != null, "tuple != null"); 

      Thread.CurrentThread.CurrentUICulture = tuple.Item2; // Here we set the UI-Thread Culture to the Background Thread 

      var longRunningOperationAnswer = LongRunningOperation.DoLongWork(tuple.Item1); 
      return longRunningOperationAnswer; 

     }, new Tuple<IWorkItem, CultureInfo>(workItem, Thread.CurrentThread.CurrentUICulture)); // here we pass the UI-Thread Culture to the State Object 



     /* ======================================================================= 
     * Handle OnlyOnRanToCompletion Task and process longRunningOperationAnswer back in UiThread 
     * =======================================================================*/ 
     task.ContinueWith((t) => 
     { 
      IsBusy = false; 
      // handle longRunningOperationAnswer here in t.Result 
      Log.Debug("Operation completet with {0}", t.Result); 

     }, CancellationToken.None 
     , TaskContinuationOptions.OnlyOnRanToCompletion 
     , TaskScheduler.FromCurrentSynchronizationContext()); 

     /* ======================================================================= 
    * Handle OnlyOnFaulted Task back in UiThread 
    * =======================================================================*/ 
     task.ContinueWith((t) => 
     { 
      IsBusy = false; 
      AggregateException aggEx = t.Exception; 

      if (aggEx != null) 
      { 
       aggEx.Flatten(); 
       Log.ErrorFormat("The Task exited with Exception(s) \n{0}", aggEx); 
       foreach (Exception ex in aggEx.InnerExceptions) 
       { 
        if (ex is SpecialExaption) 
        { 
         //Handle Ex here 
         return; 
        } 
        if (ex is CustomExeption) 
        { 
         //Handle Ex here 
         return; 
        } 
       } 
      } 
     }, CancellationToken.None 
     , TaskContinuationOptions.OnlyOnFaulted 
     , TaskScheduler.FromCurrentSynchronizationContext()); 
    }