Introducción
Analizaremos un modelo de herencia el cual permite definir una tabla para cada tipo concreto. En este caso la relación entre las tablas define el tipo de instancia de la entidad.
El modelo utilizado en este artículo es idéntico al anterior:
[Entity Framework][Code First] Herencia - Tabla por jerarquía - Table per Hierarchy (TPH)
Definición del modelo
Modelaremos la entidad empleado y sus derivados que representan al personal externo y el contratado por la compañía.
Partiremos de un modelo de clases como el siguiente:
y obtendremos un modelo de tablas
en donde cada la clase base estará en una tabla mientras que los derivados, con sus atributos particulares, en otras distintas.
En este caso la relación entre las tablas define el tipo, a diferencia del modelo TPH (Tabla por jerarquía) en donde un campo era quien lo indicaba.
Definición del Mapping
Se va a necesitar definir en que tabla se persiste cada tipo especifico, es por ello que se requiere una clase de mapping para cada derivado que herede de la base.
public class NorthWindContext : DbContext { public NorthWindContext() : base("NorthwindDb") { this.Configuration.LazyLoadingEnabled = false; this.Configuration.ProxyCreationEnabled = false; } public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new EmployeeMap()); modelBuilder.Configurations.Add(new EmployeeInternalMap()); modelBuilder.Configurations.Add(new EmployeeExternalMap()); base.OnModelCreating(modelBuilder); } } public class EmployeeMap : EntityTypeConfiguration<Employee> { public EmployeeMap() { HasKey(x => x.EmployeeID); Property(x => x.LastName).HasMaxLength(20).IsRequired(); Property(x => x.FirstName).HasMaxLength(10).IsRequired(); Property(x => x.Address).HasMaxLength(60); Property(x => x.City).HasMaxLength(15); Property(x => x.Region).HasMaxLength(15); Property(x => x.PostalCode).HasMaxLength(10); Property(x => x.Country).HasMaxLength(15); } } public class EmployeeInternalMap : EntityTypeConfiguration<EmployeeInternal> { public EmployeeInternalMap() { ToTable("EmployeeInternal"); } } public class EmployeeExternalMap : EntityTypeConfiguration<EmployeeExternal> { public EmployeeExternalMap() { ToTable("EmployeeExternal"); Property(x => x.ConsultantName).IsRequired() .HasColumnType("varchar") .HasMaxLength(100); } }
El principal detalle esta en la definición de los nombre de la tablas
Definición Repository
Este no sufrió cambios con respecto al articulo anterior donde tratamos TPH.
Nota: se agrego un método adicional a la interfaz del repositorio el cual implementa una query linq que utiliza el “is”, pero no varia la forma de definir esta funcionalidad con respecto al articulo anterior. Se analizara este punto más adelante cuando tratemos el Test que utiliza esta implementacion.
Test – Inicializar datos
La inicialización de datos es idéntica al artículo anterior donde explique TPH.
Pero el resultado de los “insert” que define EF son bastante diferentes, ya que por cada entidad creada requiere realiza dos operaciones, uno en la tabla base y otro en cada especialización, esto se aprecia claramente en el trace tomado desde el sql profiler.
Test – Obtener todos los empleados
Recuperar todos los empleados requiere implementar la unión de dos tablas, la de empleados internos y externos, al ejecutar el test se puede observar claramente si analizamos el select definido por EF
La unión de las dos tablas que representan las entidades hijas se unen mediante un INNER JOIN con la tabla padre de empleados
Aquí nuevamente aparecen los “0X0X” y “0X1X” los cuales son usados por EF para determinar que tipo de instancia debe crear
Test – Recuperar todos los empleados de tipo interno, usando un repositorio especifico
Utilizamos el repositorio definido para un tipo concreto, en este caso se recuperan los empleados internos a la empresa
Esta acción unirá mediante un INNER JOIN la tabla que representa estos empleados con la tabla base, del cruce se obtendrán solo los registros de un único tipo.
Test – Recuperar todos los empleados del tipo interno, usando funcionalidad del repositorio genérico
Utilizar el método OfType<> definido en un método en al repositorio general
dará el mismo resultado que utilizar el repositorio especifico del test anterior
Test – Obtener todos los empleados Externos
Se obtienes todos los empleados de contratación externa.
El join se realiza utilizando la tabla que representa los empleados externos a la compañía.
Test – Obtener todo los empleados externos, usando el “is”
Aplicaremos una variación en la técnica utilizada al definir el tipo que queremos filtrar, para ello nos ayudaremos con el “is” en el where de la query linq.
como veremos EF detecta el “is” como la acción de recuperar un tipo especifico, por eso el sql que define nuevamente hace uso del JOIN entre las tablas
Documentación de referencia
Inheritance with EF Code First: Part 2 – Table per Type (TPT)
Código
Se utilizo Visual Studio 2012, no se adjunta ninguna base de datos, esta será creada al ejecutar los test utilizando el modelo definido en el contexto de EF
[C#]
|
Hola muy buenas :
ResponderEliminarTengo una pequeña duda sobre una base de datos que estoy realizando.
Tengo varias tablas una de ellas se llama Modelo_zapatos, en esta tabla a través de un formulario damos de alta todos los modelos de los zapatos, el trabajo que lleva y el precio del trabajo.
A la hora de facturar los trabajos tengo que seleccionar,Notas, el modelo del zapato y quiero que solo me aparezca el trabajo que lleva solo ese modelo y cuando yo seleccione el trabajo en la casilla precio aparezca automaticamente el precio del trabajo que se tiene que multiplicar por el total de pares para que me aparezca el total de la nota.
Me puede echar una mano ?
Un saludo gracis
hola
ResponderEliminarrespondi en la pregunta del foro
http://social.msdn.microsoft.com/Forums/es-ES/b69cc83f-da99-4314-aa44-4f832e2dc88f/ayuda-sobre-access
saludos
Hola Leandro, una vez más tengo una consulta, esta no tiene nada que ver con la entrada, pero no encontré alguna relacionada con la consulta que tengo. Quisiera saber si existe alguna herramienta o alguna forma de hacer una barra buscadora dentro de un proyecto web desarrollado con C# en Visual Studio 2010 o 2012, el problema es que necesito que esta búsqueda no solo se realice sobre la pagina web, también tengo archivos guardados en una base de datos sql server en un campo de tipo varbinary, ¿se puede buscar dentro de estos archivos coincidencias en cuanto a palabras? Tengo entendido que el buscador que proporciona SharePoint realiza este tipo de busquedas
ResponderEliminarhola david
ResponderEliminarhasta donde se control para implementar una busqueda no existe, podrias simplemente definirlo con un textbox y un boton
se que existen servicios que proporciona el iis para implementar busquedas
Search Engine Optimization Toolkit
para buscar sobre la db deberias ver el servicio de
Full-Text Search (SQL Server)
saludos
Hola leandro, muchas gracias por las sugerencias, las probare y te comento como me va, adicionalmente quisiera preguntarte si conoces una forma de guardar en una base de datos el contenido de documentos como word o pdf, pero solo el contenido eliminando el formato, algo parecido a un parser, pero implementarlo desde C# para poder tener una tabla en sql con el contenido de los archivos.
ResponderEliminarSaludos
hola david
ResponderEliminarimagino apuntas a persistir los documentos usando EF
si es asi podrias definir un campo del tipo bute[] y este mapear contra el campo de la tabla, de esta forma si usas el
File.ReadAllByte()
podrias asignar el contenido del archivo en el campos
saludos
Hola leandro, no estoy utilizando EF, estoy desarrollando una aplicación web con visual studio 2010 y C#, utilizo una base de datos sql server 2008 en donde guardo archivos que los usuarios suben a la aplicación, estos archivos los guardo en la BD en un campo de tipo varbinary(max), pero ahora existe la necesidad de generar un buscador para encontrar archivos que contengan cierta información y esta búsqueda tengo que realizarla en el contenido de los archivos ya que no basta con buscar en nombres del archivo o propiedades, pero no se de que forma se pueda implementar este buscador
ResponderEliminarhola david
ResponderEliminarsi solo guardas el binario del archivo esta claro que no se podra aplicar busquedas
quizas debas analizar el uso de
Full-Text Search (SQL Server)
saludos
Hola leandro muchas gracias por la ayuda, full text search funciona perfecto
ResponderEliminarhola leandro! tengo una duda, en este tipo de herencia, si deseo hacer referencia a un empleado, por ejemplo en una liquidacion, la misma va dirigida a la clase padre o a uno de sus hijos (heredadas)? tambien supongo que un empleado deberia ser de un tipo u otro, no ambos...esto lo manejas con un atributo discriminador, o un disparador? saludos!!
ResponderEliminarhola matias
ResponderEliminarla referencia que realices va a depender del negocio, si este indica que esa liquidacion se realiza a un determinado tipo de empleado vincularias contra la clase hija
si la operacion permite que sea una entidad empleado sin discriminar que tipo lo harias contra la entidad padre
para que sea de un tipo u otro sin que se puedan crear instancias de la entidad padre lo defines mediante la declaracion como "abstract" de la entidad padre, en el ejemplo justamente lo realizo de esta forma
saludos
Hola de nuevo, es clarp lo que decis, pero mi pregunta iba referida a como evitar que se carguen las dos hijas, en los casos que el negocio no lo permita, como este ejemplo, donde una persona no podria ser los dos tipos de empleado al mismo tiempo.
ResponderEliminarMe gustaria saber sobre todo a nivel tabla como lo haria, por eso preguntaba si se ponía algun atributo discriminador, saludos!!
hola matias
ResponderEliminarsi entendi bien, lo que planteas no se puede dar nunca, porque solo puedes crear un unico tipo de instancia
si defines:
EmployeeInternal emp1 = new EmployeeInternal();
solo sera un emplado de contratacion interna, no puede ser tambien un empleado externo porque lo define la instancia, la simple declaracion ya lo esta limitando
saludos
Hola Leandro me puedes hacer el favor y me ayudas.es que tengo 2 tablas y necesito restar de una de ellas x cantidad o sea de la tabla producto.existencias restarle a la tabla venta.cantidad estoy trabajando en Access y vb.net y están en 3 formularios diferentes.muchas gracias
ResponderEliminarhola Aldo
ResponderEliminarpero lo estas implementando con entity framework ? o usas ado.net simple
si solo es una query del UPDATE sabes como ejecutarla, digo utilizas los objeto OleDbConnection, OleDbCommand, para poder ejecutar la query de update
ADO.NET – Parte 4 – Actualización Información Ms Access
saludos
Leandro te queria preguntar si tienes algun ejemplo con Silverlight 4 en asp
ResponderEliminarhola Noe
ResponderEliminarme temo que no tengo, la verdad no he trabajado con silverlight
si conozco algo del tema porque trabaje con xaml en wpf, pero fue todo desktop
saludos
ok de todos modos gracias por tu ayuda y responder nuestras dudas eres un excelente programador
ResponderEliminarBuenas tarde Leandro!!
ResponderEliminarOye estoy desarrollando en MVC3 con C#, javascript y razor.
Pero intento hacer algo que no me funciona. Ojala puedas apoyarme..
Tengo una función de agregar lineas. Para esto agrego código html con razor y javascript ya que es algo dinamico.
El conflicto que tengo es agregarle una variable javascript a la lista,
trato de concatenarla pero no se como concatenar en una linea razor con javascript.
'@Html.TextBoxFor(model => model.RMRP.lstForm[a].TAG, new { id = "TAG" , maxlength = "7" })'
Ojala puedas ayudarme...
hola mike`s
ResponderEliminarpero porque no lo concatenas en el model, o quizas en el action
o sea podrias hacer
public class ModelXX{
public string TagText{
get{
return string.Join(",", this.lstForm);
}
}
}
entonces usarias
@Html.TextBoxFor(model => model.RMRP.TagText, new { id = "TAG" , maxlength = "7" })'
imagine que lstForm es un array de estring no? sino se puede adaptar pero creo que la idea se entiende
saludos
Hola Leandro. Tengo una duda. He leido tu post y me fue de mucha utilidad pero me he topado con un problema.
ResponderEliminarCree una entidad base que se llama "Persona" de esta le heredan "EmpleadoInterno" y "EmpleadoExterno" muy parecido a tu codigo.
Cuando agrego Empleado Internos o Externos no tengo problema. Pero cuando agrego una persona que despues quiero convertir a Empleado Interno o Externo me da una error de llave primaria.
Me podrias explicar como corregir este error
hola William
ResponderEliminarpero si crear una entidad como Persona esta claro que no podras pasarla a empleado ya que la herencia no lo permite
podrias asignar instancia de empleado interno o externo a Persona pero no al contrario, es mas no tendria mucho sentido porque habria propiedades que no se pueden definir cuando vas a una instancia de entidad mas compleja
es mas me entra la duda como lograste hacer que la aplicaicon compile, porque yo hay esto
public class Persona
{
}
public class Empleado : Persona
{
}
y luego defino
Persona p1 = new Persona();
Empleado e1 = p1;
y no me compila, justamente porque no se puede realizar esa asignacion, ahora si uso
Empleado e1 = new Empleado();
Persona p1 = e1;
sa asignacion si me deja
saludos
Claro que entiendo lo que comentas. Por lo que entiendo en este modelo si creas una persona como entidad base nunca va a poder ser empleado interno o externo. Si quisiera un empleado interno o externo tendría que crear otra entidad que tenga la misma información que la entidad base mas los campos que la diferencian de la entidad base. Y estriamos hablando de otra persona.
ResponderEliminarEn la vida real tu puedes registrar todas las personas que quieras. Y estas personas después pueden pasar a formar parte de una empresa como empleados internos o externos. Muchas veces los empleados externos se convierten a empleados internos. Pero en todos los casos siempre es la misma persona.
Eso es lo que estoy tratando de implementar en el modelo. Que una persona registrada en la entidad persona sea la base para crear un empleado interno o externo. Es decir el mismo Id
Mi pregunta es si se soporta esto en el entity framework.
Muchas gracias de antemano.
hola William
ResponderEliminareso puedes hacerlo, para persistir un empleado base solo no definas como abstract la clase base, asi podras crear instancias
con esto podrias crear entidades de la clase base y persistirlas
ahora de ahi a que despues pase a ser interno o externo seguramente requiere de una transformacion, la verdad no he probado convertir de un tipo a otro
saludos
Hola Leandro.
ResponderEliminarComo lo comentas resolví este problema haciendo una transformación. Agregue un método en la clase DbContext que reciba una entidad base como entrada e inserte con un stored procedure en la tabla de la entidad heredada y al final regresa la instancia de la entidad heredada.
Este es el código.
public EmpleadoInterno AddEmpleadoInterno(Persona Persona)
{
SqlConnection sqlConn = new SqlConnection(Empleados.Properties.Settings.Default.EmpleadosContext);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "spEmpleadoInterno_Add";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@PersonaId",Persona.PersonaId));
cmd.Connection = sqlConn;
sqlConn.Open();
cmd.ExecuteNonQuery();
sqlConn.Close();
using (var db = new EmpleadosContext())
{
return db.EmpleadosInternos.Single(x => x.PersonaId == Persona.PersonaId);
}
}
Y así lo mando llamar.
EmpleadoInterno new_ei = db.AddEmpleadoInterno(p, db);
new_ei.Nomina = "1010";
db.SaveChanges();
Te parece bien la solución o que le mejorarías tu.
Gracias por tu apoyo.
hola William
ResponderEliminarla verdad no veo correcta la solucion, no es una buena idea tener que recurrir a un procedure cuando deberias lograrlo usando solo entity framework
si quieres convertir una entidad cambiandola, podrias eliminar la original y crear una nueva, quizas para este caso puntual el id no lo hagas identity sino que tu lo defines asi puedes reusarlo cuando creas la nueva entidad de persona a empleado interno
ademas el procedure ni siquiera lo invocas por medio de EF, sino que usas ado.net, eso es aun peor
saludos
Pues si puedo hacer lo que comentas de eliminar la instancia base y crear una instancia heredada con el mismo Id. Lo único que me da temor es afectar relaciones de llaves foráneas que pueda tener en algunos otros puntos de la base de datos. Pero enteramente si funciona.
ResponderEliminarCon respecto al stored procedure, lo hice de esa forma solo para ejemplificar que si funciona solo complementar la base de datos para instanciar la entidad heredada y también no me he adentrado en customizar el EF con stored procedures.
Muchas gracias por tu dirección y pronta respuesta.
hola William
ResponderEliminarsi es cierto las relaciones son un tema, por eso comente que quizas para este caso en concreto no usar identity para la key, asi podrias reutilizar para la nueva entidad el id que ya tenias
Si bien es una nueva entidad cuando lo attaches al contexto de EF lo deberias marcar para actualizacion
Es mas ahora que lo pienso, si armas una nueva entidad "empleado interno" y le completas las propiedades copiando los datos de la entidad persona, y ademas asignas el id que ya tenias, pero al attachar al contexto lo pones para modificar quizas funcione
saludos
Ok. Voy a intentar esta parte de agregar la entidad al dbcontext y marcarla como update no como new.
ResponderEliminarGracias