2010-03-15 20 views
5

estoy mapear un conjunto de tablas que comparten un conjunto común de campos:Cómo implementar tabla-por-tipo concreto utilizando la estrategia marco de la entidad

alt text

Así como se puede ver que estoy utilizando una estrategia de tabla por hormigón para mapear la herencia.

Pero ...

tengo no podía relacionarlos con un tipo abstracto que contiene estas propiedades comunes.

¿Es posible hacerlo utilizando EF?


PRIMA: El único no documentado Entity Data Model Mapping Scenario es Table-per-concrete-type inheritancehttp://msdn.microsoft.com/en-us/library/cc716779.aspx: P

+0

http://weblogs.asp.net/manavi/archive/2011/01/03/herheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete -type-tpc-and-choosing-strategy-guidelines.aspx –

Respuesta

5

Finalmente I creado una interfaz 'Iggy' que contenía los descriptores de acceso a las propiedades comunes:

public Interface Iggy 
{ 
    string modifiedBy { get; set; } 
    DateTime modifiedDate { get; set; } 
} 

y utilizan clases parciales implementarlo en las clases de dominio

public partial class Al:Iggy{} 
public partial class Ben:Iggy{} 
public partial class Carl:Iggy{} 

C# es realmente muy útil, y aunque me gustaría hacerlo utilizando una característica entidad-marco, parciales funciona como un encanto:)

1

Por qué no fijar el diseño de la mesa ?!

+0

¡Hola Reinier! gracias por tu sugerencia :). pero en realidad resultaría muy costoso (hay ~ 150 tablas en el proyecto) así que lo consideraría en la próxima iteración. en este momento, preferimos implementarlo de esta manera, evitando un refactor enorme. también hay muchas concesiones entre estrategias, esta es buena en el rendimiento (evite muchas combinaciones contra una mesa central). gracias de nuevo :). – SDReyes

+0

BTW Supongo que estás hablando de una estrategia de herencia Tabla por tipo. (offtopic):) – SDReyes

+1

Claro, usted ahorra en combinaciones. Pero no sé cómo ayudarte, lo siento. – reinierpost

0

He logrado este escenario exacto hoy. No parece que el diseñador para hacerlo correctamente, pero aquí es cómo lo hice modificando el EDMX: -

  1. En la clase base, poner todas sus propiedades compartidas por ejemplo la fecha de modificación y modificado por. Puede hacer esto en la sección CSDL. No pongas ninguna condición en el CSDL.
  2. En el contenido de la asignación de C-S, ingrese las asignaciones de sus campos. Obviamente, necesita asignar en ambas entidades secundarias las propiedades compartidas a las columnas de DB físicas.
  3. Ponga una condición en cada tabla donde la columna PK, p. Ej. Id establece IsNull = falso.

Si vuelve a abrir el diseñador, usted debe ver que los campos compartidos están en la clase base, y los únicos campos que aparecen en los tipos derivados (en la columna Asignaciones de zona) serán los únicos columnas.

Al menos, esto funcionó para mí :-)

0

En realidad, el uso de clases parciales para implementar la interfaz realmente no resolver su problema si usted tiene sólo unas pocas mesas (entidades) que desea cuando map.But necesita hacer esto con más entidades o con aún más interfaces, puede usar la plantilla T4 utilizada por EF para generar las clases y luego implementar la interfaz directamente en las clases autogeneradas de POCO, sin necesidad de trabajo manual.

He hecho esto yo mismo, hemos creado una interfaz ISimpleAuditable, muy similar a la suya, y la T4 comprueba si la tabla tiene los campos correctos, y si lo hace, agregará el código de implementación de la interfaz a la clase.

hemos creado un archivo Include.tt que tiene un código como éste:

GetTypeInterfaces public static string (entidad EntityType) { interfaces de cadena = String.Empty;

if(IsNome(entity)) 
    { 
     if(IsIdentifiableNumeric(entity)) 
      interfaces = "IEntity<int>"; 

     if(IsIdentifiableText(entity)) 
      interfaces = "IEntity<string>"; 

     if (interfaces == String.Empty) 
      interfaces = "INome"; 

     if(IsSimpleAuditable(entity)) 
      if (interfaces==String.Empty) 
       interfaces = "ISimpleAuditable<string>"; 
      else 
       interfaces += ", ISimpleAuditable<string>"; 
    } 
    else 
    { 
     if(IsIdentifiableNumeric(entity)) 
      interfaces = "IIdentifiable<int>"; 

     if(IsIdentifiableText(entity)) 
      interfaces = "IIdentifiable<string>"; 

     if(IsSimpleAuditable(entity)) 
      if (interfaces==String.Empty) 
       interfaces = "ISimpleAuditable<string>"; 
      else 
       interfaces += ", ISimpleAuditable<string>"; 
    } 
    if (interfaces != string.Empty) 
     if (entity.BaseType !=null) 
      interfaces = string.Format(", {0}", interfaces); 
     else 
      interfaces = string.Format(": {0}", interfaces); 

    return interfaces; 
} 

El código T4 es como la siguiente:

<#@ template language="C#" debug="true" hostspecific="true"#> 
<#@ import namespace="System.Diagnostics" #> 
<#@ include file="EF.Utility.CS.ttinclude"#> 
<#@ include file="Winsys.Sandstone.Data.ttinclude"#><#@ 
output extension=".cs"#><# 

const string inputFile = @"Winsys.Sandstone.Data.edmx"; 
var textTransform = DynamicTextTransformation.Create(this); 
var code = new CodeGenerationTools(this); 
    var ef = new MetadataTools(this); 
var typeMapper = new TypeMapper(code, ef, textTransform.Errors); 
var fileManager = EntityFrameworkTemplateFileManager.Create(this); 
var itemCollection = new EdmMetadataLoader(textTransform.Host, TextTransform.Errors).CreateEdmItemCollection(inputFile); 
var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); 

if  (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) 
{ 
return string.Empty; 
} 

WriteHeader(codeStringGenerator, fileManager); 

foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) 
{ 
fileManager.StartNewFile(entity.Name + ".cs"); 
BeginNamespace(code); 
#> 
<#=codeStringGenerator.UsingDirectives(inHeader: false)#> 
<#=codeStringGenerator.EntityClassOpening(entity)#> 
{ 
<# 
    var propertiesWithDefaultValues =  typeMapper.GetPropertiesWithDefaultValues(entity); 
    var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); 
    var complexProperties = typeMapper.GetComplexProperties(entity); 

    if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any()) 
    { 
#> 
    public <#=code.Escape(entity)#>() 
    { 
<# 
     foreach (var edmProperty in propertiesWithDefaultValues) 
     { 
#> 
     this.<#=code.Escape(edmProperty)#> =   <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; 
<# 
     } 

     foreach (var navigationProperty in collectionNavigationProperties) 
     { 
#> 
     this.<#=code.Escape(navigationProperty)#> = new  List<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>(); 
<# 
     } 

     foreach (var complexProperty in complexProperties) 
     { 
#> 
     this.<#=code.Escape(complexProperty)#> = new   <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); 
<# 
     } 
#> 
    } 

<# 
    } 

var simpleProperties = typeMapper.GetSimpleProperties(entity); 
if (simpleProperties.Any()) 
{ 
    foreach (var edmProperty in simpleProperties) 
    { 
#> 
<#=codeStringGenerator.Property(edmProperty)#> 
<# 
    } 
} 

if (complexProperties.Any()) 
{ 
#> 

<# 
    foreach(var complexProperty in complexProperties) 
    { 
#> 
<#=codeStringGenerator.Property(complexProperty)#> 
<# 
    } 
} 

var navigationProperties = typeMapper.GetNavigationProperties(entity); 
if (navigationProperties.Any()) 
{ 
#> 
<#=WinsysGenerator.GetSimpleAuditable(entity)#> 
<# 
    foreach (var navigationProperty in navigationProperties) 
    { 
#> 
<#=codeStringGenerator.NavigationProperty(navigationProperty)#> 
<# 
    } 
} 
#> 

Tenga en cuenta que esta no es la T4 total, pero se puede conseguir la idea

0

I como respuesta por @SDReyes anteriores. Pero el uso de interfaces y clases parciales no siempre es conveniente. Cuando utiliza la interfaz, debe obligatoriamente repetir el mismo conjunto de accesadores de interfaz predeterminados en cada clase parcial heredada. En el caso de una herencia única, usar una clase abstracta es mucho más conveniente. Por defecto, la clase abstracta dará mapeo exactamente deseada:

tabla por clase concreta (TPC): Este enfoque sugiere una tabla para una clase concreta, pero no para la clase abstracta. Entonces, si hereda la clase abstracta en múltiples clases concretas, entonces las propiedades de la clase abstracta serán parte de cada tabla de la clase concreta .

http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx

entonces podemos reescribir el código de @SDReyes de la siguiente manera:

public abstract class Iggy 
{ 
    string modifiedBy { get; set; } 
    DateTime modifiedDate { get; set; } 
} 

public class Al:Iggy{} 
public class Ben:Iggy{} 
public class Carl:Iggy{} 

Podemos dejar de Al, Ben y Carl definiciones vacía. Todos los campos heredados de Iggy se tomarán automáticamente de la definición de Iggy en una tabla individual por clase.

Cuestiones relacionadas