2011-02-17 15 views
5

Al tratar de implementar el patrón de repositorio me he encontrado con un problema menor que me preocupa, en realidad oculta problemas mayores.Fundición, genéricos y subtipos

Tengo una DatabaseEntity <T> que estoy usando para manejar todas las operaciones básicas de CRUD y de la cual descenderán todas las otras clases que necesitan almacenarse en una base de datos. Está funcionando bien para las clases que heredan directamente de él, sin embargo, al usarlo con clases que tienen un padre intermedio, me he encontrado con un problema.

Supongamos que tengo otras tres clases, los padres, Childa y Parto de y y la heredad será similar a:

DatabaseEntity
                  |
        Padres
        |             |
Childa Parto de

también suponer que DatabaseEntity <T> tiene un método con la siguiente firma:

public static T FindBy(int id) 

El problema que estoy teniendo es cuando intento algo como:

ChildA Foo = ChildA.FindBy(SomeID); 

I obtener un error de compilación que me dice que no hay una conversión implícita de un Parent a un ChildA. Esto se debe a que Parent es la clase que se transfiere para el parámetro tipo a DatabaseEntity para ChildA y ChildB. Solución fácil, creo, solo agregue un parámetro de tipo a Parent pasando por el tipo apropiado. Solo espere un segundo, luego tendré que definir explícitamente el subtipo cada vez que use Parent que arruine cualquier polimorfismo. No, pensándolo bien, quizás esa no sea una gran solución.

Creo que acabo podría caer el parámetro de tipo en el propio DatabaseEntity clase y tienen cada método requiere un parámetro de tipo, pero entonces tendría que hacer algo como:

ChildA Foo = ChildA.FindBy<ChildA>(SomeID); 

Mientras que compila, se parece menos limpio y ciertamente requiere más tipeo. Visual Studio pregunta si me falta un molde y mientras su verdadero tan sólo pudiera emitir mi primer ejemplo su sólo una cuestión de tiempo antes de que accidentalmente escribir a máquina:

ChildB Foo = (ChildB) ChildA.FindBy(SomeID) 

No estoy especialmente satisfecho con cualquiera de los soluciones que he pensado hasta ahora y espero que alguien aquí pueda señalar una elegante que me he perdido.

+0

¿Con qué versión de .NET está limitado? – Adrian

+0

necesita un ORM que admita operaciones LINQ, luego puede usar 'OfType ' para hacer la conversión entre parent a childa. – RPM1984

Respuesta

5

Creo que hacer de Parent una clase genérica es el camino a seguir. No explicó exactamente cuál es el propósito del tipo T en su ejemplo, pero supongo que desea que sea el tipo real de la entidad, por lo que su Parent heredaría Entity<Parent>.

Todavía se puede escribir código polimórfico en este escenario - sólo tiene que utilizar los genéricos:

static void Foo<T>(Parent<T> p) where T : Parent<T> 
{ 
    Parent<T> entity = p.Find(); 
} 

Este método puede ser invocado tanto con ChildA y ChildB. El único aspecto difícil es que en realidad no puede crear una instancia de Parent<Parent<...>> (debido a que los puntos tendrían que ser reemplazados con más Parent<...> tipos anidados), pero se puede escribir algo como esto:

class ParentFix : Parent<ParentFix> { } 

.. entonces se puede pase instancias de ParentFix al método Foo también.

+0

Genial, no sabía que pudieras escribir una definición de método así, aún acostumbrándote a los genéricos. Tiene razón al suponer que me gustaría que T sea el tipo real de la entidad. Por suerte, no necesito instanciar Parent en este caso. Sin embargo, tener una clase de arreglador vacía parece algo feo (sin ofender), ¿es esta una técnica común o he creado inadvertidamente un escenario extraño? – Trajanus

2

Sé que esta no es la respuesta que está buscando, pero recomendaría usar un sólido ORM comercial, como Entity Framework 4. Es compatible con la herencia de la caja, y es básico, fundamental el uso es un repositorio.

Creo que tienes mucho dolor si tratas de rodar esto tú mismo.

+0

En realidad estoy usando NHibernate, estoy tratando de configurar mi clase DatabaseEntity para que no tenga que agregar repetidamente métodos básicos de Buscar/Eliminar/Guardar que llamen a NHibernate para cada una de mis clases. NHibernate ya está haciendo todo el trabajo pesado :) – Trajanus

0

Lo siento si yo no obtiene la imagen más grande aquí, pero se puede hacer DatabaseEntity<T> una clase genérica separado (una estática puede ser), sin derivar la Parent de ella.

Esta disposición permitiría usar todas las operaciones CRUD definidas con DatabaseEntity<T> con cualquiera de las clases que pueda tener.

P.S .: Por favor no me llame si he olvidado algo obvio, preferiría solicitar alguna aclaración para poder aprender mejor.