2010-01-26 6 views
9

J'ai plusieurs projets qui ont besoin de partager des fichiers de ressources (.resx) Des suggestions ont été faites pour déplacer des fichiers de ressources vers un assembly séparé et faire référence à des projets Web. Y a-t-il un exemple de comment faire cela? Est-ce que je crée un nouveau projet de bibliothèque de classes et déplace le dossier App_GlobalResource à l'intérieur de celui-ci? Je ne pense pas que cela fonctionnera car les classes de code générées pour les fichiers de ressources sont marquées comme 'internes', ce qui signifie qu'elles ne peuvent pas être accédées en dehors de cet assembly.Partage de fichiers de ressources asp.net entre applications Web

Répondre

17

Dans la fenêtre des propriétés de Visual Studio, vous devez pouvoir définir le modificateur d'accès du fichier de ressources sur public. Cependant, vous ne pourrez pas accéder aux ressources dans les fichiers aspx en utilisant la syntaxe normale <%$ Resources:... %>, car elle ne prend pas en charge les ressources dans les assemblys référencés. J'ai eu le même problème et l'ai résolu en implémentant un ExpressionBuilder personnalisé, mais je n'ai pas accès à mon code source pour le moment. Si c'est encore nécessaire, je peux regarder le code ce soir.


EDIT: OK, voici comment je l'ai résolu ce problème:

Étape 1: Déplacer les resx dans la bibliothèque de classes. Ils n'ont pas besoin d'être dans un dossier spécifique. Dans le concepteur visuel du fichier resx, définissez le « accès Modificateur » (en haut de coin à droite) à « Public »

Vous devriez maintenant être en mesure de

  • référence les ressources C#/code VB (dans la bibliothèque, ainsi que dans le projet Web), par exemple, Dim myMessage As String = [Namespace.]Resources.NameOfResx.NameOfResource

  • référence à la ressource code en ligne dans les pages ASPX, par exemple, <h1><%= [Namespace.]Resources.NameOfResx.MyTitle %></h1>.

Qu'est-ce que ne sera pas travail à ce stade est d'utiliser l'expression de ressources, par exemple, <asp:Button runat="server" Text="<%$ Resources:NameOfResx,MyButtonText %>" />. Malheureusement, vous ne pouvez pas simplement remplacer ceci par du code en ligne, puisqu'il se trouve dans la propriété d'un contrôle côté serveur.

Étape 2: Créons un ExpressionBuilder personnalisé dans notre bibliothèque, qui interprète des expressions de code arbitraires. Heureusement, nous pouvons laisser les classes puissantes du .net Framework font tout le travail pour nous:

Imports System.Web.Compilation 
Imports System.Resources 
Imports System.CodeDom 

<ExpressionPrefix("Code")> _ 
Public Class CodeExpressionBuilder 
    Inherits ExpressionBuilder 

    Public Overrides Function GetCodeExpression(ByVal entry As System.Web.UI.BoundPropertyEntry, ByVal parsedData As Object, ByVal context As System.Web.Compilation.ExpressionBuilderContext) As System.CodeDom.CodeExpression 
     Return New CodeSnippetExpression(entry.Expression) 
    End Function 
End Class 

Ensuite, nous avons besoin d'enregistrer ce ExpressionBuilder dans le web.config:

<system.web> 
    ... 
    <compilation ...> 
    <expressionBuilders> 
     <add expressionPrefix="Code" type="NamespaceOfYourLibrary.CodeExpressionBuilder" /> 
    </expressionBuilders> 
    </compilation> 
</system.web> 

Maintenant, vous devriez être en mesure de faire ce qui suit:

<asp:Button runat="server" Text="<%$ Code:[Namespace.]Resources.NameOfResx.MyButtonText %>" /> 

crédit: J'ai eu l'idée de la CodeExpressionBuilder du Infinites Loop blog. Si vous êtes plus en C# qu'en VB, vous pouvez regarder les exemples de code ici.

+0

Il est bon de savoir que je ne serai pas d'accéder aux ressources capables dans les fichiers ASPX (son certainement utilisé comme ça). Ce serait bien de voir ce ExpressionBuilder personnalisé si vous pouvez le partager. –

+0

@ dev.e.loper: J'ai mis à jour ma réponse. – Heinzi

+1

@Heinzi: Cette solution ne rend pas l'assembly séparé pour résoudre les ressources par culture. Vous obtiendrez toutes les chaînes pour la culture par défaut même si theres défini un resx supplémentaire pour la culture actuelle. – Sergio

7

Nous avions une application déjà développée où nous devions avoir 2 copies de tous les fichiers de ressources, un pour les services et un pour le projet asp.net, aussi le projet s'appuyait sur la syntaxe <%$ Resources:NameOfResx,MyButtonText %>. .

Après un certain temps j'ai trouvé le ExpressionBuilder et est venu avec la solution suivante:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Data; 
using System.Diagnostics; 
using System.Web.Compilation; 
using System.Resources; 
using System.CodeDom; 
using System.Reflection; 

/// <summary> 
/// This class allows asp.net Resource lookups to different assembly 
/// </summary> 
[ExpressionPrefix("Resources")] 
public class ResourceExpressionBuilder : ExpressionBuilder 
{ 
    static readonly Dictionary<string, ResourceManager> mResourceManagers = new Dictionary<string, ResourceManager>(StringComparer.OrdinalIgnoreCase); 
    static ResourceExpressionBuilder() 
    { 
     Assembly resourceAssembly = Assembly.GetAssembly(typeof(OneTypeInResourceAssembly)); 
     const string suffix = ".resources"; 
     string assemblyName = resourceAssembly.GetName().Name; 
     foreach (string resource in resourceAssembly.GetManifestResourceNames()) { 
      if ((resource.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))) { 
       string resourceName = resource.Substring(0, resource.Length - suffix.Length); 
       string resourceFriendlyName = resourceName.Substring(assemblyName.Length + 1, resourceName.Length - (assemblyName.Length + 1)); 
       mResourceManagers.Add(resourceFriendlyName, new ResourceManager(resourceName, resourceAssembly)); 
      } 
     } 
    } 

    /// <summary> 
    /// When overridden in a derived class, returns a value indicating whether the current <see cref="T:System.Web.Compilation.ExpressionBuilder" /> object supports no-compile pages. 
    /// </summary> 
    /// <returns>true if the <see cref="T:System.Web.Compilation.ExpressionBuilder" /> supports expression evaluation; otherwise, false.</returns> 
    public override bool SupportsEvaluate { 
     get { return true; } 
    } 

    /// <summary> 
    /// When overridden in a derived class, returns an object that represents an evaluated expression. 
    /// </summary> 
    /// <param name="target">The object containing the expression.</param> 
    /// <param name="entry">The object that represents information about the property bound to by the expression.</param> 
    /// <param name="parsedData">The object containing parsed data as returned by <see cref="M:System.Web.Compilation.ExpressionBuilder.ParseExpression(System.String,System.Type,System.Web.Compilation.ExpressionBuilderContext)" />.</param> 
    /// <param name="context">Contextual information for the evaluation of the expression.</param> 
    /// <returns> 
    /// An object that represents the evaluated expression; otherwise, null if the inheritor does not implement <see cref="M:System.Web.Compilation.ExpressionBuilder.EvaluateExpression(System.Object,System.Web.UI.BoundPropertyEntry,System.Object,System.Web.Compilation.ExpressionBuilderContext)" />. 
    /// </returns> 
    public override object EvaluateExpression(object target, System.Web.UI.BoundPropertyEntry entry, object parsedData, System.Web.Compilation.ExpressionBuilderContext context) 
    { 
     if ((parsedData != null && object.ReferenceEquals(parsedData.GetType(), typeof(string)))) { 
      return GetRequestedValue(Convert.ToString(parsedData)); 
     } 
     return base.EvaluateExpression(target, entry, parsedData, context); 
    } 

    /// <summary> 
    /// When overridden in a derived class, returns code that is used during page execution to obtain the evaluated expression. 
    /// </summary> 
    /// <param name="entry">The object that represents information about the property bound to by the expression.</param> 
    /// <param name="parsedData">The object containing parsed data as returned by <see cref="M:System.Web.Compilation.ExpressionBuilder.ParseExpression(System.String,System.Type,System.Web.Compilation.ExpressionBuilderContext)" />.</param> 
    /// <param name="context">Contextual information for the evaluation of the expression.</param> 
    /// <returns> 
    /// A <see cref="T:System.CodeDom.CodeExpression" /> that is used for property assignment. 
    /// </returns> 
    public override System.CodeDom.CodeExpression GetCodeExpression(System.Web.UI.BoundPropertyEntry entry, object parsedData, System.Web.Compilation.ExpressionBuilderContext context) 
    { 
     CodeExpression[] inputParams = new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()) }; 
     return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(this.GetType()), "GetRequestedValue", inputParams); 
    } 


    /// <summary> 
    /// Gets the requested value. 
    /// </summary> 
    /// <param name="expression">The expression.</param> 
    /// <returns></returns> 
    public static object GetRequestedValue(string expression) 
    { 
     string[] parts = expression.Split(new char[] { ',' }, 2, StringSplitOptions.None); 
     if ((parts.Length != 2)) { 
      throw new ArgumentException("Expression must contain ,"); 
     } 
     string resourceFile = parts[0].Trim(); 
     string resourceName = parts[1].Trim(); 
     return mResourceManagers[resourceFile].GetString(resourceName); 
    } 
} 

Remplacer OneTypeInResourceAssembly avec un type dans l'ensemble contenant les ressources.

Après cela, vous pouvez simplement ajouter ce qui suit à web.config et il devrait fonctionner ..

<system.web> 
    <compilation> 
    <expressionBuilders> 
     <remove expressionPrefix="Resources" /> 
     <add expressionPrefix="Resources" type="Assembly.ResourceExpressionBuilder" /> 
    </expressionBuilders> 
    </compilation> 
</system.web> 
+0

Nice. Cela marche! – Fanda

Questions connexes