EntityFramework 查询操作、数据库提供程序包装、数据库表达式树
我正在尝试为实体框架实现数据本地化逻辑。因此,如果查询选择 Title
属性,则它应该在幕后引用列 Title_enGB
或 Title_deCH
,具体取决于当前用户区域性。
为了实现这一目标,我想重写实体框架中的 DbExpression CommandTrees。我认为这些树是构建跨数据库的一种新的通用.NET方法插入/更新/选择查询..但是现在命名空间 System.Data.Metadata
和 System.Data.Common.CommandTrees
中的所有相关构造函数/工厂System.Data.Entity.dll
是内部的! (在 msdn 中公开记录,例如: DbExpressionBuilder
)。
有谁知道在有或没有查询树重写的情况下实现这种查询操作?
我想要的代码:(公共类DbProviderServicesWrapper:DbProviderServices
)
/// <summary>
/// Creates a command definition object for the specified provider manifest and command tree.
/// </summary>
/// <param name="providerManifest">Provider manifest previously retrieved from the store provider.</param>
/// <param name="commandTree">Command tree for the statement.</param>
/// <returns>
/// An exectable command definition object.
/// </returns>
protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree)
{
var originalCommandTree = commandTree as DbQueryCommandTree;
if (originalCommandTree != null)
{
var expression = new MyCustomQueryRewriter(originalTree.MetadataWorkspace).Visit(originalCommandTree.Query);
commandTree = DbQueryCommandTree.FromValidExpression(originalCommandTree.MetadataWorkspace, originalCommandTree.DataSpace, expression);
}
// TODO: UpdateCommand/InsertCommand
var inner = this.Inner.CreateCommandDefinition(providerManifest, commandTree);
var def = new DbCommandDefinitionWrapper(inner, (c, cd) => new DbCommandWrapper(c));
return def;
}
更新
在一个表上有两个标题列并不酷,但第一步更容易实现。稍后我将使用本地化字段连接另一个表,因此主表将仅包含不变数据。
I'm trying to implement data localization logic for Entity Framework. So that if for example a query selects Title
property, behind the scenes it should reference the column Title_enGB
or Title_deCH
depending on the current user culture .
To achieve this, I'd like to rewrite the DbExpression CommandTrees from Entity Framework. I thought these trees are a new common .NET way for building cross database insert/update/select queries.. But now all relevant constructors/factories in the namespaces System.Data.Metadata
and System.Data.Common.CommandTrees
in System.Data.Entity.dll
are internal!! (In msdn documentated as public, like: DbExpressionBuilder
).
Does anyone have an idea to achieve this query manipulation with or without query tree rewrite?
my desired code: (public class DbProviderServicesWrapper : DbProviderServices
)
/// <summary>
/// Creates a command definition object for the specified provider manifest and command tree.
/// </summary>
/// <param name="providerManifest">Provider manifest previously retrieved from the store provider.</param>
/// <param name="commandTree">Command tree for the statement.</param>
/// <returns>
/// An exectable command definition object.
/// </returns>
protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree)
{
var originalCommandTree = commandTree as DbQueryCommandTree;
if (originalCommandTree != null)
{
var expression = new MyCustomQueryRewriter(originalTree.MetadataWorkspace).Visit(originalCommandTree.Query);
commandTree = DbQueryCommandTree.FromValidExpression(originalCommandTree.MetadataWorkspace, originalCommandTree.DataSpace, expression);
}
// TODO: UpdateCommand/InsertCommand
var inner = this.Inner.CreateCommandDefinition(providerManifest, commandTree);
var def = new DbCommandDefinitionWrapper(inner, (c, cd) => new DbCommandWrapper(c));
return def;
}
Update
Having two title columns on one table isn't cool but its easier to implement in a first step. Later I'll join an other table with the localized fields, so the main table will only contain invariant data.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在 .net 中,您有用于处理本地化的 resx 文件。请参阅:resource(.resx) 有哪些好处文件?
您的方法存在一些问题:
我知道这不是直接回答你的问题,但我认为你应该看看 resx 文件。
如果必须将其存储在数据库中,可以重新设计数据库:
这样,新语言不需要更改数据库,并且 EF 代码变得更加简单。
In .net you have resx files for handling localization. See: What are the benefits of resource(.resx) files?
There are a couple of problems with your approach:
I know that this is not a direct answer to your question but I think you should look at resx files.
If you must store it in the database you could redesign the database:
This way a new language does not require a database change, and the EF code becomes much simpler.
我同意 Shiraz 的回答,如果您仍然能够更改设计,那么这不应该是您想要的,但我假设这是您要转换为实体框架的现有应用程序。
如果是这样,Title_enGB/etc 列是否映射到 EDMX 文件/POCO 中就很重要。如果是的话,我想这是可能的。您在这里可以做的是使用访问 MemberExpressions 的表达式访问者,检查它们是否访问名为“Title”的属性(您可以创建需要像这样处理的属性白名单),然后返回一个新的 MemberExpression 来代替访问Title_enGB 如果登录用户设置了该语言。
一个简单的例子:
然后在执行查询之前:
同样,只有当您不再对数据库有任何控制权时,这才是一个好主意。
该解决方案对您来说可能实用,也可能不实用,具体取决于您的具体情况,但使用表达式重写查询绝对是可能的。
这是比修改实体框架生成实际 SQL 查询的方式更高级别的解决方案。这确实对你隐藏,可能有充分的理由。相反,您只需修改描述查询的表达式树,并让实体框架负责将其转换为 SQL。
I agree with the answer of Shiraz that this shouldn't be what you want if you are still capable of changing the design, but I'll be assuming that this is an existing application that you are converting to Entity Framework.
If so, it matters if the Title_enGB/etc columns are mapped in the EDMX file / POCOs. If they are, I suppose this is possible. What you could do here, is use an Expression visitor that visits MemberExpressions, checks if they access a property named "Title" (you could create a whitelist of properties that needed to be treated like this) and then return a new MemberExpression that insteads accesses Title_enGB if the logged in user has that language set.
A quick example:
And then before you execute the query:
Again, this is only a good idea if you don't have any control over the database any more.
This solution may or may not be practical to you, depending on your exact situation, but rewriting queries using Expressions is definitely possible.
It's a much higher level solution than modifying how Entity Framework generates the actual SQL queries. That's indeed hidden from you, probably with good reason. Instead, you just modify the expression tree that describes the query and let Entity Framework worry about converting it to SQL.
相反,我将提出另一种设计...
现在在这种情况下,您
的记录 2 实际上是整个产品的另一种语言描述,采用由 LanguageCode 标识的不同语言。
这样您就可以只管理一个表,并且编写一些基于泛型或基于反射的查询解决方案会容易得多。
您可以创建一个接口,如下所示,
并且您可以基于此编写通用解决方案。
Instead I will propose one more design...
Now in this case you have,
Your record 2 is actually another language description of entire product in different language identified by LanguageCode.
This way you can only manage one Table and writing some Generics based or Reflection based Querying solution will be lot easier.
You can create an interface as follow,
And you can write a generic solution based on this.