14

Estoy tratando de implementar la lógica de localización de datos para Entity Framework. De modo que si, por ejemplo, una consulta selecciona la propiedad Title, detrás de las escenas debe hacer referencia a la columna Title_enGB o Title_deCH dependiendo de la cultura del usuario actual.manipulación de consultas EntityFramework, envoltura del proveedor db, árboles de expresión db

Para lograr esto, me gustaría volver a escribir los CommandTrees de DbExpression desde Entity Framework. Pensé que estos trees son una nueva forma común de .NET para crear una base de datos cruzada insertar/actualizar/seleccionar consultas ... Pero ahora todos los constructores/fábricas relevantes en los espacios de nombres System.Data.Metadata y System.Data.Common.CommandTrees en System.Data.Entity.dll son internos. (En msdn documentado como público, como: DbExpressionBuilder).

¿Alguien tiene una idea para lograr esta manipulación de consultas con o sin reescritura del árbol de consultas?

mi código deseado: (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; 
} 



actualización

Tener dos columnas de títulos en una tabla no es bueno, pero es más fácil de implementar en un primer paso . Más tarde me uniré a otra tabla con los campos localizados, por lo que la tabla principal solo contendrá datos invariables.

Multilanguage

+1

Hay dos cuestiones dependientes de la cultura que parece ignorar con su localización. (1) El precio se expresa en moneda estadounidense. (2) Las cantidades se expresan en el sistema métrico. – smartcaveman

Respuesta

5

En .net tiene archivos resx para el manejo de la localización. Ver: What are the benefits of resource(.resx) files?

Hay un par de problemas con su enfoque:

  • Adición de un idioma adicional requiere un cambio de base de datos
  • Hay más tráfico de datos desde la base de datos que se requiere

Sé que esta no es una respuesta directa a su pregunta, pero creo que debería ver los archivos de resx.

Si debe almacenar en la base de datos se podría rediseñar la base de datos:

  • Tabla 1: Identificación, texto
  • Tabla 2: Identificación, Table1_id, LANGUAGE_CODE, texto

Este forma en que un nuevo idioma no requiere un cambio de base de datos, y el código EF se vuelve mucho más simple.

+0

Su sugerencia es fácil. Pero me gustaría escribir una extensión de marco de entidad, que pueda manejar esto automáticamente para que los desarrolladores no tengan que hacer manualmente estas combinaciones de lenguaje en cada consulta, ya que la mayoría de mis entidades tienen cadenas multilenguaje y propiedades binarias. En Genome, nuestro .Net 3.5 ORM (imagen superior) también se administró automáticamente. – benwasd

+1

Puede construir una vista y colocar las uniones en la vista –

+0

Pero esto no funcionaría bien con Code First, ¿o no? –

5

Estoy de acuerdo con la respuesta de Shiraz de que esto no debería ser lo que desea si aún puede cambiar el diseño, pero supongo que se trata de una aplicación existente que está convirtiendo a Entity Framework.

Si es así, es importante si las columnas Title_enGB/etc están asignadas en el archivo/POCO de EDMX. Si lo son, supongo que esto es posible.Lo que podría hacer aquí es usar un visitante de Expression que visite MemberExpressions, compruebe si accede a una propiedad llamada "Título" (podría crear una lista blanca de propiedades que necesitaban tratarse así) y luego devolver un nuevo MemberExpression que incluye accesos Title_enGB si el usuario que inició sesión tiene ese idioma configurado.

Un ejemplo rápido:

public class MemberVisitor : ExpressionVisitor 
{ 
    protected override Expression VisitMember(MemberExpression node) 
    { 
    if(node.Member.Name == "Title") 
    { 
     return Expression.Property(node.Expression, "Title_" + User.LanguageCode) 
    } 

    return base.VisitMember(node); 
    } 
} 

Y a continuación, antes de ejecutar la consulta:

var visitor = new MemberVisitor(); 
visitor.Visit(query); 

Una vez más, esto es sólo una buena idea si usted no tiene ningún control sobre la base de datos de cualquier Más.

Esta solución puede ser o no práctica para usted, dependiendo de su situación exacta, pero definitivamente es posible volver a escribir las consultas utilizando Expressions.

Es una solución de mucho más nivel que la modificación de cómo Entity Framework genera las consultas SQL reales. Eso está oculto para ti, probablemente con buenas razones. En su lugar, solo debe modificar el árbol de expresiones que describe la consulta y dejar que Entity Framework se preocupe por convertirlo a SQL.

+0

dónde poner esas dos líneas? var visitor = new MemberVisitor(); visitor.Visit (consulta); –

1

su lugar voy a proponer un diseño más ...

Products 
    ProductID 
    ProductName 
    Price 
    Description 
    ParentID (Nullable, FK on ProductID) 
    LangCode 

Ahora bien, en este caso usted tiene,

1, Milk, $1 , EnglishDesc , NULL, en-us 
2. M*^*, ^*&, OtherLangDesc, 1 , @$#$$ 

Su ficha 2 es en realidad otro lenguaje de descripción de la totalidad del producto en diferente idioma identificado por LanguageCode.

De esta forma, solo puede administrar una tabla y escribir una solución basada en genéricos o consultas basadas en reflexiones será mucho más fácil.

// Get Active Products 
q = context.Products.Where(x=> x.ParentID == null); 

// Get Product's Language Code Description 
IQueryable<Product> GetProductDesc(int productID, string langCode){ 
    return context.Products.Where(x=>x.ParentID == productID && 
       x.LangCode == langCode); 
} 

Puede crear una interfaz de la siguiente manera,

interface IMultiLangObject{ 
    int? ParentID {get;set;} 
    string LangCode {get;set;} 
} 

Y se puede escribir una solución genérica basada en esto.

Cuestiones relacionadas