11

我有多个项目需要共享资源文件 (.resx) 已建议将资源文件移动到单独的程序集并让 Web 项目引用它。有如何做到这一点的例子吗?

我是否创建一个新的类库项目并在其中移动 App_GlobalResource 文件夹?我认为这不会起作用,因为为资源文件生成的代码类被标记为“内部”,这意味着它们无法在此程序集之外访问。

4

2 回答 2

21

在 Visual Studio 的属性窗口中,您应该可以将资源文件的访问修饰符设置为 public。但是,您将无法使用正常<%$ Resources:... %>语法访问 aspx 文件中的资源,因为它不支持引用程序集中的资源。我遇到了同样的问题,并通过实现自定义 ExpressionBuilder 解决了它,但我目前无法访问我的源代码。如果还需要的话,我今晚可以查一下代码。


编辑:好的,这就是我解决这个问题的方法:

第 1 步:将 resx 文件移动到类库中。它们不需要位于特定文件夹中。在 resx 文件的可视化设计器中,将“Access Modifier”(右上角)设置为“Public”

您现在应该能够

  • 引用C#/VB 代码中的资源(在库中以及在 Web 项目中),例如,Dim myMessage As String = [Namespace.]Resources.NameOfResx.NameOfResource

  • 在 aspx 页面中将资源作为内联代码引用,例如<h1><%= [Namespace.]Resources.NameOfResx.MyTitle %></h1>.

此时不起作用的是使用 Resources 表达式,例如<asp:Button runat="server" Text="<%$ Resources:NameOfResx,MyButtonText %>" />. 不幸的是,您不能简单地将其替换为内联代码,因为它位于服务器端控件的属性中。

第 2 步:让我们在我们的库中创建一个自定义的 ExpressionBuilder,它可以解释任意代码表达式。幸运的是,我们可以让 .net 框架的强大类为我们完成所有工作:

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

然后我们需要在 web.config 中注册这个 ExpressionBuilder:

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

现在您应该能够执行以下操作:

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

信用:我从Infinites Loop 博客中得到了 CodeExpressionBuilder 的想法。如果您更喜欢 C# 而不是 VB,则可以查看那里的代码示例。

于 2010-01-26T16:13:24.057 回答
9

我们已经开发了一个应用程序,其中我们必须拥有所有资源文件的 2 个副本,一份用于服务,一份用于 asp.net 项目,而且该项目依赖于<%$ Resources:NameOfResx,MyButtonText %>语法,因此无法更改语法。

一段时间后,我找到了 ExpressionBuilder 并提出了以下解决方案:

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);
    }
}

替换OneTypeInResourceAssembly为包含资源的程序集中的类型。

之后,您可以将以下内容添加到 web.config 中,它应该可以正常工作..

<system.web>
  <compilation>
    <expressionBuilders>
      <remove expressionPrefix="Resources" />
      <add expressionPrefix="Resources" type="Assembly.ResourceExpressionBuilder" />
    </expressionBuilders>
  </compilation>
</system.web>
于 2014-10-13T10:56:49.303 回答