- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试在我的 Entity Framework 模型中添加对多语言分类字符串的支持。这是我的:
实体:
public partial class ServiceState : ITranslatableEntity<ServiceStateTranslation>
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ServiceStateTranslation> Translations { get; set; }
}
ITranslatableEntity 接口(interface):
public interface ITranslatableEntity<T>
{
ICollection<T> Translations { get; set; }
}
然后是包含翻译的实体:
public partial class ServiceStateTranslation
{
public int Id { get; set; }
[Index("IX_ClassificationTranslation", 1, IsUnique = true)]
public int MainEntityId { get; set; }
public ServiceState MainEntity { get; set; }
[Index("IX_ClassificationTranslation", 2, IsUnique = true)]
public string LanguageCode { get; set; }
public string Name { get; set; }
}
包含本地化字符串的属性的名称在主实体和翻译实体中始终相同(在本例中为 Name
)。
使用这样的模型我可以做这样的事情:
var result = query.Select(x => new
{
Name = x.Name,
StateName =
currentLanguageCode == DEFAULTLANGUAGECODE
? x.ServiceState.Name
: x.ServiceState.Translations.Where(i => i.LanguageCode == currentLanguageCode)
.Select(i => i.Name)
.FirstOrDefault() ?? x.ServiceState.Name
}).ToList();
问题是我不喜欢为包含任何可翻译实体的每个查询编写这种代码,所以我正在考虑使用 QueryInterceptor
和一个 ExpressionVisitor
这会产生一些魔力,让我可以用这样的东西替换查询:
var result = query.Select(x => new
{
Name = x.Name,
StateName = x.ServiceState.Name
}).ToLocalizedList(currentLanguageCode, DEFAULTLANGUAGECODE);
我想可以创建一个 ExpressionVisitor 来:
Select
里面的表达式实现 ITranslatableEntity<>
的导航属性 block 界面如果当前语言不是默认语言,改变表达式x.ServiceState.Name
到
x.ServiceState.Translations.Where(i => i.LanguageCode == currentLanguageCode)
.Select(i => i.Name)
.FirstOrDefault() ?? x.ServiceState.Name
但我对 expression visitors 和 trees 不是很熟悉,所以我有点迷路了。有人能让我走上正轨吗?
最佳答案
好的,看来我已经想出了一个可行的解决方案。
public class ClassificationTranslationVisitor : ExpressionVisitor
{
private string langCode = "en";
private string defaultLangCode = "en";
private string memberName = null;
private Expression originalNode = null;
public ClassificationTranslationVisitor(string langCode, string defaultLanguageCode)
{
this.langCode = langCode;
this.defaultLangCode = defaultLanguageCode;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (langCode == defaultLangCode)
{
return base.VisitParameter(node);
}
if (!node.Type.GetCustomAttributes(typeof(TranslatableAttribute), false).Any() && originalNode == null)
{
return base.VisitParameter(node);
}
if (IsGenericInterface(node.Type, typeof(ITranslatableEntity<>)))
{
return AddTranslation(node);
}
return base.VisitParameter(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node == null || node.Member == null || node.Member.DeclaringType == null)
{
return base.VisitMember(node);
}
if (langCode == defaultLangCode)
{
return base.VisitMember(node);
}
if (!node.Member.GetCustomAttributes(typeof(TranslatableAttribute), false).Any() && originalNode == null)
{
return base.VisitMember(node);
}
if (IsGenericInterface(node.Member.DeclaringType, typeof(ITranslatableEntity<>)))
{
memberName = node.Member.Name;
originalNode = node;
return Visit(node.Expression);
}
if (IsGenericInterface(node.Type, typeof(ITranslatableEntity<>)))
{
return AddTranslation(node);
}
return base.VisitMember(node);
}
private Expression AddTranslation(Expression node)
{
var expression = Expression.Property(node, "Translations");
var resultWhere = CreateWhereExpression(expression);
var resultSelect = CreateSelectExpression(resultWhere);
var resultIsNull = Expression.Equal(resultSelect, Expression.Constant(null));
var testResult = Expression.Condition(resultIsNull, originalNode, resultSelect);
memberName = null;
originalNode = null;
return testResult;
}
private Expression CreateWhereExpression(Expression ex)
{
var type = ex.Type.GetGenericArguments().First();
var test = CreateExpression(t => t.LanguageCode == langCode, type);
if (test == null)
return null;
return Expression.Call(typeof(Enumerable), "Where", new[] { type }, ex, test);
}
private Expression CreateSelectExpression(Expression ex)
{
var type = ex.Type.GetGenericArguments().First();
ParameterExpression itemParam = Expression.Parameter(type, "lang");
Expression selector = Expression.Property(itemParam, memberName);
var columnLambda = Expression.Lambda(selector, itemParam);
var result = Expression.Call(typeof(Enumerable), "Select", new[] { type, typeof(string) }, ex, columnLambda);
var stringResult = Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { typeof(string) }, result);
return stringResult;
}
/// <summary>
/// Adapt a QueryConditional to the member we're currently visiting.
/// </summary>
/// <param name="condition">The condition to adapt</param>
/// <param name="type">The type of the current member (=Navigation property)</param>
/// <returns>The adapted QueryConditional</returns>
private LambdaExpression CreateExpression(Expression<Func<ITranslation, bool>> condition, Type type)
{
var lambda = (LambdaExpression)condition;
var conditionType = condition.GetType().GetGenericArguments().First().GetGenericArguments().First();
// Only continue when the condition is applicable to the Type of the member
if (conditionType == null)
return null;
if (!conditionType.IsAssignableFrom(type))
return null;
var newParams = new[] { Expression.Parameter(type, "bo") };
var paramMap = lambda.Parameters.Select((original, i) => new { original, replacement = newParams[i] }).ToDictionary(p => p.original, p => p.replacement);
var fixedBody = ParameterRebinder.ReplaceParameters(paramMap, lambda.Body);
lambda = Expression.Lambda(fixedBody, newParams);
return lambda;
}
private bool IsGenericInterface(Type type, Type interfaceType)
{
return type.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == interfaceType);
}
}
public class ParameterRebinder : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> map;
public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression node)
{
ParameterExpression replacement;
if (map.TryGetValue(node, out replacement))
node = replacement;
return base.VisitParameter(node);
}
}
我还添加了 TranslatableAttribute
,这是任何要翻译的属性所必需的。
代码当然缺少一些检查,但它已经在我的环境中运行了。我也没有检查被替换的表达式是否在 Select
block 内,但看起来 TranslatableAttribute
没有必要。
我使用了 ParameterRebinder
和这个答案中的一些其他代码 ExpressionVisitor soft delete
关于c# - 使用 ExpressionVisitor 修改表达式以进行自动翻译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29410428/
我从 MSDN 上关于 How to: Modify Expression Trees 的文章中得知ExpressionVisitor 应该做什么。它应该修改表达式。 然而,他们的示例非常不切实际,所
我需要在执行之前使用 ExpressionVisitor 来分析表达式。根据我的需要,我需要评估 Divide 表达式的正确部分,但我不知道该怎么做。这是我拥有的示例代码: internal clas
如何使用 olingo4 将原始表达式传递给 Odata4 的 ExpressionVisitor 实现。这就是在 olingo2 JDBC Visitor Filter 中完成的方法 String
我正在尝试在我的 Entity Framework 模型中添加对多语言分类字符串的支持。这是我的: 实体: public partial class ServiceState : ITranslata
我正在关注 example series on MSDN用于创建 LINQ 提供程序并碰壁了。 我希望当我编写以下测试时 ExpressionVisitor下面源代码中的子类是 VisitMethod
我正在关注 this blog尝试为我目前正在处理的项目创建适用于 MSAccess 的 IQueryable 提供程序。 我得到了上面链接的页面,但是有几段代码创建了继承自 ExpressionVi
我正在使用 ExpressionVisitor 解析表达式树以查明它是否包含指定参数。找到参数后,继续遍历就没有意义了。 有什么方法可以停止一般访问者模式的遍历,更具体地说是.NET 中Express
我有一个在数据库中实现软删除的框架(称为 DeletedDate 的可空日期时间)。我正在使用存储库来处理主要实体请求,如下所示: /// /// Returns a Linq Queryable
我正在尝试使用定义明确的模型为定义明确的 Web API 构建 LINQ 提供程序。我正在关注这些演练: Part I Part II 我已经能够创建将表达式转换为所需 URL 的查询提供程序,并且效
我必须为一项任务设计一个解决方案,我想使用理论上类似于 C# 的 ExpressionVisitor 的东西。 出于好奇,我打开了 ExpressionVisitor 的 .NET 源代码来查看它。从
在有人喊出答案之前,请通读问题。 .NET 4.0 的 ExpressionVisitor 方法的目的是什么: public static ReadOnlyCollection Visit(ReadO
我在 Eclipse 中从实体创建表时遇到一些麻烦。这个项目最初是在 Windows 上的 Eclipse 上,我从 mysql 表创建了实体并将其导入到项目中。一切都运转良好。昨天我决定从 Wind
我正在尝试按照 Creating an IQueryable LINQ Provider 上的说明进行操作,但是当我实现从 ExpressionVisitor 继承的类时按照指示,我被告知 Expre
我有一个问题,像这样: var query = from sessions in dataSet where (names.Contains(sessions.
我有一个 ExpressionVisitor我将其添加到 EF Core 的 IQueryable .除 Include 方法外,一切正常。可能是因为他们强制执行您的 IQueryable.Provi
我正在为我们系统的一部分编写数据层,该部分记录有关每天运行的自动化作业的信息 - 作业名称、运行时长、结果等。 我正在使用 Entity Framework 与数据库对话,但我试图对更高级别的模块隐藏
我正在使用 Olingo 框架以 Java 语言实现符合 OData 标准第 4 版的 Web 服务。我需要自定义 $filter 的响应。我已经实现了一个访客 as documented in th
System.Linq.Expressions.ExpressionVisitor 有一个名为 VisitExtension 的方法,它似乎除了在表达式被访问。 protected internal
我是一名优秀的程序员,十分优秀!