sábado, 29 de junio de 2013

[Entity Framework][Code First] Crear entidad simple

 

Introducción


Este artículo sea el inicio de varios donde iremos analizando paso a paso las diferentes alternativas que Entity Framework nos brinda en su modalidad Code First.

Partiremos de ejemplos simples hasta analizar implementaciones mas complejas, evaluando como se generan las consultas que impactaran en la base de datos.

Haremos uso de la estructura planteada por la db NorthWind, agregándole algunas modificaciones para poder estudiar algunos casos no contemplados en el modelo original, como ser el caso de la herencia.

Comenzaremos armando la estructura del proyecto, el cual no contara con ninguna interfaz grafica ya que haremos uso de proyectos de Test para aplicar validar la lógica que permite recuperar los datos.

También armaremos el código haciendo uso del concepto de Repository dejando encapsulado la funcionalidad de Entity Framework a un solo proyecto.

 

Incluir librería Proyecto


La estructura esta formado por 3 proyecto:

  • Test, el cual permitirá probar la funcionalidad y evaluar el correcto desarrollado
  • DataAccess, donde se definen las clases repository, así como también el contexto que requiere Code First para mapear las entidades
  • Entities, define las clases que representan las entidades de negocio

image

en la capa de DataAccess es donde necesitaremos referenciar la librería de Entity Framework, para ello haremos uso de NuGet

SNAGHTML467f868

 

SNAGHTML65f603

Una vez instalada la librería podremos verla como referencia en el proyecto

SNAGHTML4690fdf

 

Definir conexión a la Base de Datos


Para poder usar EF debemos definir la cadena de conexión en un archivo App.config o Web.config según sea el tipo de proyecto que se este usando, en este caso al ejecutar desde un Test será un App.config con el siguiente contenido:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <connectionStrings>
  
    <add name="NorthwindDb"
          connectionString="Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI;"
          providerName="System.Data.SqlClient"/>
  </connectionStrings>
  
</configuration>

 

El name de la key de la conexión se utilizara en la definición de la clase del contexto.

SNAGHTML341d896c

En este caso vamos a utilizar el servicio de Sql Server y no un attach dinámico porque dejaremos que el modelo definido en EF genere la base de datos, de esta forma se podrá analizar la estructura de tablas resultante.

Si la base de datos existe previamente (salvo que se lo indique lo contrario) usara esa db, sino la creara basándose en la estructura definida en el modelo.

 

Definición del contexto


El proyecto encargado de definir la persistencia será: NorthWind.DataAccess

Empezaremos creando la clase de contexto: NorthWindDataContext

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
    }

    public DbSet<Category> Categories { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        

        base.OnModelCreating(modelBuilder);
    }

}

La clase hereda de DbContext, y define en el constructor la key del archivo de configuración que usara para tomar la conexión a la base de datos.

Por cada entidad que necesitemos mapear con una tabla se creara una propiedad cuyo tipo será DbSet<>

Dejamos preparado el contexto con la definición del OnModelCreating donde podremos personalizar el mapeo de la entidad, esto lo veremos mas adelante.

 

Definición del Repositorio Genérico


La clase del repositorio permitirá definir las acciones sobre la persistencia que serán comunes para todas las entidades.

Comenzaremos definiendo la interfaz del repositorio:

interface IRepository<T> where T:class
{
    List<T> GetAll();
    List<T> GetAll(List<Expression<Func<T, object>>> includes);

    T Single(Expression<Func<T, bool>> predicate);
    T Single(Expression<Func<T, bool>> predicate, List<Expression<Func<T, object>>> includes);

    List<T> Filter(Expression<Func<T, bool>> predicate);
    List<T> Filter(Expression<Func<T, bool>> predicate, List<Expression<Func<T, object>>> includes);

    void Create(T entity);
    void Update(T entity);

    void Delete(T entity);
    void Delete(Expression<Func<T, bool>> predicate);
}

La implementación base

public abstract class BaseRepository<T> : IRepository<T> where T:class
{

    public List<T> GetAll()
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            return (List<T>)context.Set<T>().ToList();
        }
    }

    public List<T> GetAll(List<Expression<Func<T, object>>> includes)
    {
        List<string> includelist = new List<string>();

        foreach (var item in includes)
        {
            MemberExpression body = item.Body as MemberExpression;
            if (body == null)
                throw new ArgumentException("The body must be a member expression");

            includelist.Add(body.Member.Name);
        }

        using (NorthWindContext context = new NorthWindContext())
        {
            DbQuery<T> query = context.Set<T>();

            includelist.ForEach(x => query = query.Include(x));

            return (List<T>)query.ToList();
        }

    }


    public T Single(Expression<Func<T, bool>> predicate)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            return context.Set<T>().FirstOrDefault(predicate);
        }
    }

    public T Single(Expression<Func<T, bool>> predicate, List<Expression<Func<T, object>>> includes)
    {
        List<string> includelist = new List<string>();

        foreach (var item in includes)
        {
            MemberExpression body = item.Body as MemberExpression;
            if (body == null)
                throw new ArgumentException("The body must be a member expression");

            includelist.Add(body.Member.Name);
        }

        using (NorthWindContext context = new NorthWindContext())
        {
            DbQuery<T> query = context.Set<T>();

            includelist.ForEach(x => query = query.Include(x));

            return query.FirstOrDefault(predicate);
        }
    }


    public List<T> Filter(Expression<Func<T, bool>> predicate)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            return (List<T>)context.Set<T>().Where(predicate).ToList();
        }
    }

    public List<T> Filter(Expression<Func<T, bool>> predicate, List<Expression<Func<T, object>>> includes)
    {
        List<string> includelist = new List<string>();

        foreach (var item in includes)
        {
            MemberExpression body = item.Body as MemberExpression;
            if (body == null)
                throw new ArgumentException("The body must be a member expression");

            includelist.Add(body.Member.Name);
        }

        using (NorthWindContext context = new NorthWindContext())
        {
            DbQuery<T> query = context.Set<T>();

            includelist.ForEach(x => query = query.Include(x));

            return (List<T>)query.Where(predicate).ToList();
        }
    }
    

    public void Create(T entity)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            context.Set<T>().Add(entity);
            context.SaveChanges();
        }
    }

    public void Update(T entity)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            context.Entry(entity).State = EntityState.Modified;
            context.SaveChanges();
        }
    }

    public void Delete(T entity)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            context.Entry(entity).State = EntityState.Deleted;
            context.SaveChanges();
        }
    }

    public void Delete(Expression<Func<T, bool>> predicate)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            var entities = context.Set<T>().Where(predicate).ToList();
            entities.ForEach(x => context.Entry(x).State = EntityState.Deleted);
            context.SaveChanges();
        }
    }

}

 

Como se puede observar en este caso el repositorio define un contexto fijo para no complicar la implementación, pero se podría hacer uso de algún framework de IoC para inyectar el contexto ha utilizar.

 

Creación de una entidad simple


Para empezar vamos a hacer uso de una entidad muy simple, definiéndola en el proyecto: NorthWind.Entities

Se trata de una entidad que define categorías

image

public class Category
{
    public int CategoryID { get; set; }

    public string CategoryName { get; set; }
    public string Description { get; set; }

}

Definición del Repositorio especifico para al entidad


Es necesario implementar un repositorio para la entidad haciendo uso del base.

 

public interface ICategoryRepository
{
    Category GetById(int categoryID);
}

public class CategoryRepository : BaseRepository<Category>, ICategoryRepository
{

    public Category GetById(int categoryID)
    {
        using (NorthWindContext context = new NorthWindContext())
        {
            return context.Set<Category>().FirstOrDefault(x => x.CategoryID == categoryID);
        }

    }

}

En esta entidad se puede especializar funcionalidad que se necesites puntualmente, en el ejemplo se implemento un método que devuelve la entidad según su id, el método es definido no solo en la clase concreta del repositorio, sino que se especifica una interfaz ICategoryRepository para permitir cambiar la implementación en caso de ser necesario (o si se utiliza algún IoC).

Por supuesto siempre existen alternativas, el método GetById() podría haberse omitido ya que el mismo dato podría haberse recuperado mediante la el método Single() que define el repositorio base, utilizando:

CategoryRepository repository = new CategoryRepository();

Category category = repository.Simple(x=>x.CategoryID == categoryId);

Ese método GetById() es solo un ejemplo que pretende demostrar como se puede extender la funcionalidad definida en el repositorio base, en este no se definieron los métodos como virtual para poder sobrescribirlos, pero podría hacerse sin inconveniente.

 

Test de categorías


Para validar el funcionamiento de lo codificado hasta el momento crearemos un test que nos ayude.

En el proyecto EF.Test definimos la clase CategoryTest

[TestMethod]
public void Get_All_Category()
{
    CategoryRepository repoCategory = new CategoryRepository();

    Category categoryNew = new Category()
    {
        CategoryName = "CatName1",
        Description = "Desc1"
    };
    repoCategory.Create(categoryNew);

    var categoryList = repoCategory.GetAll();

    Assert.AreEqual(categoryList.Count, 1);
}

Al ejecutar el Test si la base de dato no existe EF la creara por nosotros, en este caso la tabla de categorías define los campos:

image

EF define convenciones que pueden utilizarse para no tener que configurar nada de la entidad, por ejemplo si definimos una propiedad que lleve el nombre de la entidad mas ID, esta automáticamente será tomada como clave.

 

Cambiar estructura tabla


Cualquier diferencia con lo especificado en la conversión requiere definición, es aquí donde entra en juego el OnModelCreating.

En la siguiente código se agrega al contexto la especificación a las propiedades de la entidad

 

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
    }

    public DbSet<Category> Categories { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CategoryMap());

        base.OnModelCreating(modelBuilder);
    }

}


public class CategoryMap : EntityTypeConfiguration<Category>
{
    public CategoryMap()
    {
        ToTable("Categories");

        HasKey(c => c.CategoryID);
        Property(c => c.CategoryID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(c => c.CategoryName).IsRequired().HasMaxLength(15);
        Property(c => c.Description).HasColumnType("ntext");

    }
}

Al ejecutar nuevamente el Test la definición de la tabla habrá cambiado:

image

En este caso especificar la clave como identity es redundante, ya que por convención EF puede deducirlo, aunque es bueno conocer las opciones de mapping para los casos en donde no se ajuste a la convención.

 

Análisis de las consulta (Sql Profiler)


Para poder validar las consultas que Entity Framework genera contra la base de datos será necesario utilizar el Sql Profiler

Una vez ejecutada la aplicación el primer paso crea un nuevo Trace

image

En el cuadro de configuración podremos definir un nombre del trace, además de los filtros y columnas de información que necesitemos para realizar el seguimiento, en este el combo “Use the template” se selecciono un témplate que solo nos muestre los datos relativos a las consultas sql

SNAGHTML70825f7c

También se define el nombre de la base de datos que necesitamos analizar, esto lo conseguimos mediante la definición de un filtro.

SNAGHTML7074e5f9

El ultimo paso solo será inicia el trace con el botón “Run”

 

Obtener todas las entidades


Empezaremos definiendo un test que no permita validar el recuperar todas las entidades registradas.

[TestMethod]
public void Get_All_Category()
{
    CategoryRepository repoCategory = new CategoryRepository();

    Category categoryNew = new Category()
    {
        CategoryName = "CatName1",
        Description = "Desc1"
    };
    repoCategory.Create(categoryNew);

    var categoryList = repoCategory.GetAll();

    Assert.AreEqual(categoryList.Count, 1);
}

si analizamos en el Sql Profiler veremos que EF genera la query

image

En el ejemplo se instancia al repositorio directamente y se utilizan los métodos para crear o recuperar la entidad.

 

Obtener una entidad por el ID


Se hará uso del método Single() definido en el repository indicando el lambda que filtrara la entidad por el ID

 

[TestMethod]
public void Get_SingleById_Category()
{
    CategoryRepository repoCategory = new CategoryRepository();

    Category categoryNew = new Category()
    {
        CategoryName = "CatName1",
        Description = "Desc1"
    };
    repoCategory.Create(categoryNew);

    var categorySel = repoCategory.Single(x => x.CategoryID == categoryNew.CategoryID);

    Assert.IsNotNull(categorySel);
    Assert.AreEqual(categorySel.CategoryID, categoryNew.CategoryID);
    Assert.AreEqual(categorySel.CategoryName, categoryNew.CategoryName);
    Assert.AreEqual(categorySel.Description, categoryNew.Description);

}

La query que genera EF en el profiler será

image

Se puede apreciar claramente la definición del filtro en el WHERE al cual se le asigna el parámetro del Id

 

Eliminar una entidad


Para eliminare la entidad simplemente hace falta definir el id de la misma, no es obligatorio recuperar la entidad para poder eliminarla

En este caso como estamos en un mismo test method donde se crea y elimina en una misma secuencial para poder validar el código no tenga tanto sentido, pero es necesario entender que con solo crear una nueva entidad en donde solo se asigne el id alcanza para poder llevar a cabo la finalidad.

 

[TestMethod]
public void Delete_ById_Category()
{
    CategoryRepository repoCategory = new CategoryRepository();

    //
    //creamos una nueva categoria
    //
    Category categoryNew = new Category()
    {
        CategoryName = "CatName2",
        Description = "Desc2"
    };
    repoCategory.Create(categoryNew);

    //
    //la eliminamos
    //
    repoCategory.Delete(new Category() { CategoryID = categoryNew.CategoryID });

    //
    // se recupera para validar que no exista
    //
    var categorySel = repoCategory.GetById(categoryNew.CategoryID);

    Assert.IsNull(categorySel);

}

En la definición del test se crea una entidad nueva, pero solo se utiliza el id para crear una entidad nueva que se adjunta al contexto para poder asignar el estado

context.Entry(entity).State = EntityState.Deleted;

 

analizando con el profiler podremos ver como EF genera la instrucción sql para eliminar el registro

image

 

Código


El código fue confeccionado con Visual Studio 2012, utilizando Entity Framework 5

La base de datos es creada de forma automática por el mismo Entity Framework, solo es necesario definir el connectionstring al servicio de sql server

[C#]
 

80 comentarios:

  1. Buenas tardes Leandro; ando buscando un articulo tuyo que publicaste sobre como dar mantenimiento a una datagridview c# con entity Framework. Saludos

    ResponderEliminar
  2. hola Unknown

    no he creado ningun articulo que una el datagridview y EF

    por ahora en los articulos solo trato EF Code Fisrt desde Test


    saludos

    ResponderEliminar
  3. Maestro sigo tus publicaciones desde hace un par de años y no he tenido oportunidad de agradecerte por compartir tus conocimientos. Me has iluminado en muchas ocaciones y eso se agradece.
    GRACIAS TOTALES

    ResponderEliminar
  4. Muy buenas Leandro, a ver si me das una mano. He creado 4 tablas con CodeFirst, EF 5.0.0, VS2010, en las que no he seguido las convenciones para llaves primarias, y todo bien. En la siguiente tengo problema, que un campo es llave primaria y a la vez es llave foránea (primaria en otra tabla). El mensaje dice: The navigation property 'CodDeptoPais' is not a declared property on type 'Lotificacion'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property. Tuve que poner freno aquí, porque encuentro como resolverlo. En las primeras 4 tablas no tuve problema. ¿existe algún ejemplo que pueda seguir, sin usar convenciones para nombres de llaves?, porque los campos llave están predefinido de cierta longitud, tipo string, y combinados no deben repetirse.
    Saludos, y de antemano gracias.

    ResponderEliminar
  5. hola CESAR

    es una relacion uno a muchos la que quiere definir, no ?

    como defines en la clase Map el HasRequired o HasOptional

    porque alli debes indicar cuales son las propiedades que permiten la navegacion

    [Entity Framework][Code First] Asociación uno a muchos (1/3)

    saludos

    ResponderEliminar
  6. Muchas gracias, muy KISS la explicacion ;)

    ResponderEliminar
  7. Hola Leandro, buenas noches, primero que nada para agradecerte la ayuda que nos brindas con estos temas que, para los que vamos empezando con mvc son de mucha ayuda.

    Tengo una duda, y no se sí me la pudieras aclarar, estoy desarrollando una aplicación con mvc4 y la manera de hacer la relación con la bd es bd first, todo funciona a la perfección y ya casi término la primera etapa del proyecto, el problema es que necesito estar creando más tablas con forme avanza el proyecto, entonces al actualizar el modelo EF en mi sistema, me actualiza mis modelos en los cuales ya tengo mis validaciones, textos para los campos y necesito evitar que esto esté sucediendo, por que lo que hago ahorita es hacer un backup de los mismos y luego los restauro, he estado buscando que pudiera hacer para solucionar esto, y no encuentro un alternativa, sabes de algo que me pudiera ser de utilidad?

    De antemano muchas gracias!

    ResponderEliminar
  8. hola Sergio

    se que el codigo que genera EF define las clases como partial por lo que podrias crear un .cs separado definir alli una clase con el mismo nombre a la generada por EF y extender la funcionalidad

    si el tema son las validaciones de DataAnnotation podrias implementar

    http://fluentvalidation.codeplex.com/

    aqui no uses atributos, sino que defines las validaciones en clases separada con fluent validation, asi no tendras problemas si el modelo se actualiza las validaciones estaran separadas

    Integration with ASP.NET MVC

    saludos

    ResponderEliminar
  9. Buenos días! Quisiera que por favor explicaras un poco como funcionan los métodos que tienen por parámetro la lista 'List>> includes' y cual sería su utilidad, es decir como se generaría esa lista de expressions y luego que función cumpliría en alguno de los métodos que la reciben como parámetro. Desde ya gracias!

    ResponderEliminar
  10. hola JuanFa

    el include se define para optimizar la accion de recuperar entidades relacionadas, recuerda que al usar un repositorio no habilitamos lazy load, por lo que se debe especificar de antemano que entidades asociadas se van a utilizar

    por ejemplo si recupero una categoria y se requiere los productos asociados se debe indicar en el include, sino la lista de producto nunca se cargara

    explicar como funciona un expresion tree puede ser algo largo

    Expression Trees (C# and Visual Basic)

    Expression Tree Basics

    la idea es poder definir un lambda que pueda ser parseado he interpretado, en este caso para obtener la propiedad que se requiere incluir en la query de EF

    saludos

    ResponderEliminar
  11. Hola Leandro,
    Muchas gracias por el post, esta genial!
    Tengo una duda:
    En la distribucion de los proyectos, has puesto Entities, DataAccess para el contexto y patron repositorio, etc..
    en el caso de un proyecto de MVC el controlador donde estarian en otro proyecto? y las interfaces con sus implementaciones de las entidades en el de data access?

    muchas gracias de antemano

    ResponderEliminar
  12. Estuve leyendo un poco lo que linkeaste y aún intento entender como funcionan las expresiones lambda je.. En tu ej:

    new List>>() { x => x.Category, x => x.Supplier });

    Si quisiera mostrar los datos de esas entidades asociadas a Producto (Categoria y Proveedor) como lo harías? Digo utilizando las funciones que ya tienes en el repositorio.. la consulta es la esa misma y hay que trabajar en otro lado para recuperar los datos de las entidades asociadas? O hay que modificar esa consulta? Desde ya muchas gracias!

    ResponderEliminar
  13. hola IMANOL IZA MARTIN

    el controlador es parte del patron MVC por lo que va en la capa de presentacion, tiene que estar en el mismo proyecto web

    las entidades van separadas a la implementacion del repositorio, si puede definir la interfaz del repositorio en el mismo proyecto donde defines als interfaces, pero la implementacion del repositorio va en otro proyecto

    saludos

    ResponderEliminar
  14. hola JuanFa

    asi como lo defines esta correcto

    new List<Expression<Func<Product, object>>>() { x => x.Category, x => x.Supplier }

    recuerda applicar un encoding cuando pones codigo asi se puede visualizar
    http://www.opinionatedgeek.com/DotNet/Tools/HTMLEncode/Encode.aspx

    saludos

    ResponderEliminar
  15. Perfecto! Entonces si te he entendido bien seria algo así?

    EF.Test
    - CategoryTest.cs
    EjemploMVCWebAPI
    - ICategoryRepository.cs
    - CategoryRepository.cs
    - CategoryController.cs
    NorthWind.DataAccess
    - IRepository.cs
    - BaseRepository.cs
    - NorthWindDataContext.cs
    NorthWind.Entities
    - Category.cs

    Gracias de antemano! un saludo.

    ResponderEliminar
  16. hola IMANOL

    lo que veo es que el repositorio concretos, en este caso de categoria, no deberia estar junto a los controladores de web-api

    quizas deberian estar en un proyecto separado

    saludos

    ResponderEliminar
  17. Hola Leandro.

    Estoy probando tu codigo e incluso he ejecutado tu ejemplo y en ambos tengo el mismo error al ejecutar el Test :
    et_All_Category has failed:
    El método de inicialización EF.Test.CategoryTest.inicializar produjo una excepción. System.Data.ProviderIncompatibleException: System.Data.ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct. ---> System.Data.ProviderIncompatibleException: El proveedor no devolvió una cadena de ProviderManifestToken. ---> System.Data.SqlClient.SqlException: Error de inicio de sesión del usuario 'MACOSA\lormeno'..
    d:\Macosac\EFExample\EFExample\NorthWind.DataAccess\DbHelper.cs(15, 0) : NorthWind.DataAccess.DbHelper.CreateDb()
    d:\Macosac\EFExample\EFExample\EF.Test\CategoryTest.cs(15, 0) : EF.Test.CategoryTest.inicializar()

    Get_SingleById_Category has failed:
    El método de inicialización EF.Test.CategoryTest.inicializar produjo una excepción. System.Data.ProviderIncompatibleException: System.Data.ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct. ---> System.Data.ProviderIncompatibleException: El proveedor no devolvió una cadena de ProviderManifestToken. ---> System.Data.SqlClient.SqlException: Error de inicio de sesión del usuario 'MACOSA\lormeno'..
    d:\Macosac\EFExample\EFExample\NorthWind.DataAccess\DbHelper.cs(15, 0) : NorthWind.DataAccess.DbHelper.CreateDb()
    d:\Macosac\EFExample\EFExample\EF.Test\CategoryTest.cs(15, 0) : EF.Test.CategoryTest.inicializar()

    Get_ById_Category has failed:
    El método de inicialización EF.Test.CategoryTest.inicializar produjo una excepción. System.Data.ProviderIncompatibleException: System.Data.ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct. ---> System.Data.ProviderIncompatibleException: El proveedor no devolvió una cadena de ProviderManifestToken. ---> System.Data.SqlClient.SqlException: Error de inicio de sesión del usuario 'MACOSA\lormeno'..
    d:\Macosac\EFExample\EFExample\NorthWind.DataAccess\DbHelper.cs(15, 0) : NorthWind.DataAccess.DbHelper.CreateDb()
    d:\Macosac\EFExample\EFExample\EF.Test\CategoryTest.cs(15, 0) : EF.Test.CategoryTest.inicializar()

    Delete_ById_Category has failed:
    El método de inicialización EF.Test.CategoryTest.inicializar produjo una excepción. System.Data.ProviderIncompatibleException: System.Data.ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct. ---> System.Data.ProviderIncompatibleException: El proveedor no devolvió una cadena de ProviderManifestToken. ---> System.Data.SqlClient.SqlException: Error de inicio de sesión del usuario 'MACOSA\lormeno'..
    d:\Macosac\EFExample\EFExample\NorthWind.DataAccess\DbHelper.cs(15, 0) : NorthWind.DataAccess.DbHelper.CreateDb()
    d:\Macosac\EFExample\EFExample\EF.Test\CategoryTest.cs(15, 0) : EF.Test.CategoryTest.inicializar()

    Saludos

    ResponderEliminar
  18. hola Luis

    pero el error esta muy claro, y dice:

    Error de inicio de sesión del usuario 'MACOSA\lormeno'

    valida el connection string porque no estas pudiendo conectarte al sql server

    saludos

    ResponderEliminar
  19. Muchas gracias Leandro, no me percate de ello, funciono a la perfección luego de la corrección de mi error.

    Saludos

    Luis

    ResponderEliminar
  20. Muchas gracias Leandro, no me percate de ello, funciono a la perfección luego de la corrección de mi error.

    Saludos

    Luis

    ResponderEliminar
  21. Leandro buenos dias sigo tus publicaciones, estoy empezando con Netito y viendo este articulo ¡descargue el ejemplo pero la verdad no pude ejecutarlo dpronto no se como puesto que me dice que una librería no puede ser iniciada, si puedes ayudarme te agradezco quiero probar y ver tu ejemplo para entenderlo mejor

    ResponderEliminar
  22. hola ksoto

    obtienes algun error cuando ejecutas ?
    imagino lo que corres son los test

    validaste la conexion a la base de datos? el servicio de sql server este iniciado y las credenciales de acceso sean correctas

    saludos

    ResponderEliminar
  23. si lo que pasa es que configuro el app.config y lo ejecuto como cualquier aplicacion creada es decir con f5 o con el iniciar, pero no se si asi no se inicia un test me gustaria que me explicaras por favor como hago para ver corriendo este test

    ResponderEliminar
  24. hola ksoto

    para ejecutar el test podrias ayudarte con ventana del "Text Explorer"

    How to: Run Automated Tests from Microsoft Visual Studio

    desde alli ejecutas y pudes ver tambien si lo hicieron correctamente

    saludos

    ResponderEliminar
  25. Leandro ya lo pude ejecutar, estoy creando un proyecto para aprender pero me surgió una pequeña duda, como puedo crear solo la base de datos sin registros, lo intente llamando el método CreateDB de la clase DBHelper pero no ejecuta nada.

    ResponderEliminar
  26. Me gustaria crear la base de datos pero sin tener que insertarle un registro como lo podria hacer

    ResponderEliminar
  27. hola ksoto

    que raro, porque si creas un test que no hace nada solo incoca al inicializar este deberia generar la base de datos

    podrias sino usar migratios

    Automatic Code First Migrations

    o sea por linea de comando desde la consola de nuget podrias ejecutar la creacion o actualizacion de la db

    saludos

    ResponderEliminar
  28. Leandro buenas noches estoy creando una aplicacion y me genera el siguiente mensaje "Unable to determine the principal end of an association between the types 'Entidades.ControlTriage' and 'Entidades.Centros'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations."

    ResponderEliminar
  29. hola ksoto

    que tipo de relacion se trata, es uno a mucho o muchos a muchos ?

    en el mapping de alguna de las entidades defines la relacion de asociacion?
    porque parece ser que algo no puede determinar por medio de la convencion y requiere que lo definas

    saludos

    ResponderEliminar
  30. Ocupo tu ayuda urgente Leandro la razón es que tengo un sitio con conexión entity framework code first el problema es que al hacer un cambio ya se agregar una propiedad en la clase si hago el cambio manual en la base de datos osea agregar ese campo en la tabla con las mismas especificaciones no me funciona y ps obviamente no quiero que borre la base de datos y me la cree de nuevo si no que actualice la entidad con el cambio nomas esto sucede con el hecho de ponerle requerido o cualquier cambio estoy muy perdido AYUDAA mi correo es marttin.ng@gmail.com

    ResponderEliminar
  31. Hola buenas tardes, una pregunta no tendras algun ejemplo ,donde apliques code first pero a una base de datos existente, y que tu clases se mapeen con la base.

    saludos

    ResponderEliminar
  32. hola Martin

    si vambias algo en la db no se actualiza tu modelo en el codigo de forma automatica, eres tu quien debe aplicar el cambio

    no existe esto de aplicar el cambio que realices en la db en la entidad en el codigo

    la actualizacion automatica puede configurarse en un solo sentido desde el codigo hacia los datos, pero no es automatica en el otro sentido debes aplicar tu los cambios en el mapping

    saludos

    ResponderEliminar
  33. hola Tonkyfiero

    la unica diferencia es que usarias el

    public class NorthWindContext : DbContext
    {

    public NorthWindContext()
    : base("NorthwindDb")
    {
    Database.SetInitializer<NorthWindContext>(null);


    }

    ..

    }

    el SetInitializer para que no actualice o cree la db, asi usa la existente

    saludos

    ResponderEliminar
  34. Buenas tardes leandro; primeramente agradecerte por la ayuda que nos brindas.
    Acabo de leer detenidamente esta publicación, pero me surgió las siguientes dudas.
    En que consiste y para que se define el repositorio genérico?
    En que capa se debe crear el repositorio genérico y el repositorio especifico?
    Es correcto que las clases que se definen para personalizar el mapeo de las entidades, se definan en el mismo archivo donde se define el contexto. O seria mejor definir en otro archivo, Si es así por cada entidad se tiene crear un archivo, y en que capa debería ir estes archivos?
    De antemano le agradesco la ayuda......

    ResponderEliminar
  35. hola cquispe

    el repositorio generico tiene como objetivo implementar funcionalidad base que sera comun a las diferentes implementaciones especificas
    hay funcionalidad que es la misma sin importar de que entidad se trate, entonces para no repetir el codigo se crea la implementacion base para todas

    un repositorio siempre tiene que estar en la capa de datos

    la ubicacion de la definicion del mapping no se ve afectada por el lugar donde la defines, podrias hacerlo en el mismo archivo o separados ambos caminos son validos
    quizas lo que determina uno u otro es la cantidad y complejidad de las entidades que vayas a definir, si solo son 4 o 5 entidades se podria definir todo junto, ahora si son 50 bueno seria bueno separararlo

    podrias definir un archivo quizas por modulo, agrupando entidades que representen la funcionalidad de compra, la de ventas, etc, asi no se crean tantos archivos

    esto siempre debe estar en la capa de acceso a datos

    saludos

    ResponderEliminar
  36. Leandro muchas gracias por la ayuda...

    ResponderEliminar
  37. Hola leandro, En un proyecto nuevo que estoy realizando estoy implementando tu patrón de repositorio genérico. En este ejemplo que muestras no lo creaste la capa de negocios, más bien directamente accedes a la capa DAL desde la aplicación cliente.Mi pregunta es:
    En el proyecto que estoy realizando debería incluir la capa de negocios desde donde puedo acceder al repositorio específico que se encuentra en la capa de Acceso a Datos. Ó no es necesario implementar la capa de negocios?

    ResponderEliminar
  38. hola cquispe

    si estas creando una aplicacion en capas entonces si deberias definir la capa de negocio y acceder desde esta al repositorio

    saludos

    ResponderEliminar
  39. Leandro muchas gracias por tu respuesta me sirvió de mucho.

    ResponderEliminar
  40. Hola Leandro, desde mi capa de negocios como debo de especificar los parámetros (condiciones) para los métodos del repositorio:
    1.- GetAll() que tiene un parametro.

    2.- Single() que tiene dos parámetros.

    Espero contar con su ayuda gracias...

    ResponderEliminar
  41. hola cquispe

    pero puedes no definir el parametro del GetAll() ya que este tiene un sobrecarga, no son obligatorios
    el parametro es unexpression del Func<> que define la expression que realiza el include

    si revisas los articulos que cree sobre EF vera sque invoco esta funcionalidad, pero no solo valides este simle, ve a los que utilizo son relaciones uno a muchos
    en estos utilizo el include

    saludos

    ResponderEliminar
  42. Hola Leandro, en que parte del código generas el proyecto para que te genere la DB?

    ResponderEliminar
  43. hola Pedro

    alli no planteo ese tema, pero podrias definir la inicializacion

    Database Initialization Strategies in Code-First

    Understanding Database Initializers in Entity Framework Code First

    como veras puedes definir como se inicializa la db
    sino recuerdo mal por defcto aplica el CreateDatabaseIfNotExists

    saludos

    ResponderEliminar
  44. Hola Leandro, yo no uso .NET en mi trabajo ni EF. Por lo cual se me vienen a la mente la siguientes dudas:

    1) Como haces con clientes cuyas politicas de seguridad no te permiten crear tablas en su base de datos? probablemente estos clientes no quieran ofrecerme un usuario para yo crear y modificar las tablas por codigo y que prefieran que se les entreguen scripts. Ademas que por otra parte el personal de base de datos no se esta enterando de lo que estas creando sino despues que ya creas las tablas

    2) No es algo delicado tener esta capa de creacion de base de datos en codigo? puede que ocurra un accidente y alguien haga modificaciones en una tabla.

    Saludos

    ResponderEliminar
  45. hola Juanfish

    1- Podrias trabjar la capa d enegocio remota usando WCF, entonces los clientes no necesitan de ningun usuario para poder acceder a las tablas con EF, sino que defines uno solo y unico en el sitio web donde expones los servicio
    El cliente consume los servicio y ni se entera por detras como se acceden a los datos.

    2- Es que si te pones a buscar todo resulta siendo delicado, pero no por eso no lo usas
    si se supone que trabajas prolijo y realizas pruebas de tu codigo no deberias tener ningun problema.
    Es mas millones de desarrolladores en todo el mundo usan EF crees que si fuera "delicado" lo usarian?
    yo lo he usado con proyectos grandes y va perfecto

    saludos

    ResponderEliminar
  46. Puedo utilizar el contexto en un modelo de patron Sigleton?

    ResponderEliminar
  47. hola Pedro

    no lo aconsejaria a menos que solo mantenga la intencia del contexto por el request o transaccion
    luego de terminada se destruya

    saludos

    ResponderEliminar
  48. Si estoy usando Visual Studio 2012 ya no instalo atravez de nuget EF, pero como agrego la referencia?

    Pq si la busco por nuget me va instalar la versión 6

    ResponderEliminar
  49. hola Pedro

    y porque no puedes usar la version 6 ?

    sino deberias buscar la dll en la carpeta "packages" y referenciar desde alli

    saludos

    ResponderEliminar
  50. Leo
    Es necesario crear la clase helper?
    me sale el siguiente error
    Error de Assert.AreEqual. Se esperaba <13>, pero es <1>.
    en Prueba.UnitTest.UnitTest1.Get_All_Category() en UnitTest1.cs: line 27

    ResponderEliminar
  51. Es necesario poner esto en el test

    [TestInitialize]
    public void inicializar()
    {
    DbHelper.CreateDb();
    }

    ResponderEliminar
  52. Ya veo que era eso, ahora puedo decir excelente post.
    Viva Argentina!!!

    ResponderEliminar
  53. Leandro Buenas Noches, sigo tu blog y estoy muy metido en el cuento de Entity Framework me ha gustado mucho, me gustaria que por favor me indicaras como puedo usar

    public List GetAll(List>> includes)
    {
    List includelist = new List();

    foreach (var item in includes)
    {
    MemberExpression body = item.Body as MemberExpression;
    if (body == null)
    throw new ArgumentException("The body must be a member expression");

    includelist.Add(body.Member.Name);
    }

    using (NorthWindContext context = new NorthWindContext())
    {
    DbQuery query = context.Set();

    includelist.ForEach(x => query = query.Include(x));

    return (List)query.ToList();
    }

    }

    para traer solo las columnas que necesito y llenar un datagridview, agradezco todo lo que me has enseñado.

    ResponderEliminar
  54. hola ksoto

    con el GetAll() no puedes definir que columnas recuperar, solo se indica que entidad quieres tomar

    si tienes una entidad compleja quizas puedas usar

    [Entity Framework][Code First] Dividir Tabla (Table Splitting)

    o sino recuperar toda la entidad y luego cuando este en memoria hacer un select de linq y tomar solo algunos campos

    var result = from item in entidad.GetAll()
    select new {
    prop1 = item.prop1,
    prop2 = item.prop2
    };


    de esta forma decides que campos recuperar

    ademas recuerda que en el grid puede indicar que columnas mostrar definiendolas en tiempo de diseño y asignando el DataPropertyName para mapearla con los datos

    saludos

    ResponderEliminar
  55. Hola Leandro como estas? muy bueno todo , ya baje el ejemplo voy a ponerme a trabajar y aprender.
    consulta como o con que aplicarías Inversion de control y/o inyección de dependencias? estuve trabajando con una versión mvc adoc donde inyectábamos en los controlers los Dao.. Gracias Totales

    ResponderEliminar
  56. hola Ariel

    las librerias mas difundidas que podrias usar son Unity, Ninject o StructureMap

    cualquiera de estas son muy buenas, por supuesto alguna puede que tenga alguna ventaja adicional, segun que necesites lograr

    saludos

    ResponderEliminar
  57. Leandro , consulta , estoy tratando de mapear dos clase ejemplo
    curso alumnos.
    hay que mapear en el map esta situacion? o se hace solo con declarar en la clase q tiene una coleccion de alumnos? no logro hacerlo. tenes un ejemplo? gracias totales

    ResponderEliminar
  58. hola Ariel

    si usas las convernsiones que define EF como estandar no hace falta definir el map, con solo poner las entidades en el las propeidad DbSet<> del context alcanza

    ahora si quieres personalizar el mapping definiendo algun atributo o relacion entonces si debes mapearlo

    las relaciones tambien tienen convenciones
    Convenciones de Code First

    si las sigues no debes indicar nada, aunque personalmente me gusta declararlas

    saludos

    ResponderEliminar
  59. a mi tambien me gusta declararlas ( NHibernate) pero no se como hacerlo en EF. y no veo ejemplos. si tenes uno ya arranco gracias

    ResponderEliminar
  60. el problema q cuando quiero acceder a la coleccion de elementos sale una excepcion pq arma un sql con campos q presupone q se llaman asi y no es verdad. necesito mapear la relacion. y no veo ejemplos

    ResponderEliminar
  61. hola Ariel

    para definir el mapping lo haces como los articulos que he escrito sobre el tema, revisaste mis otros articulos sobre code first?
    el mapping se hace desde codigo, no se si usaste Fluent de NHibernate, bueno es esa misma tecnica

    el mapping lo defines en el OnModelCreating de la clase que hereda de DbContext
    defines clases que hereden de EntityTypeConfiguration<> esto lo explico en el articulo

    saludos

    ResponderEliminar
  62. Si e mapeado con fluent , ya logre lo que queria aca muestran como hacerlo.
    http://stackoverflow.com/questions/19342908/entity-framework-many-to-many-mapping.

    ahora sale un error de contexto al anvegar sobre la propiedad mapeada . sale "La instancia de ObjectContext se ha eliminado" ...no deja de darme sorpresas..veremos dodne enceuntro como solucionar esto . MUchas gracias Leandro

    ResponderEliminar
  63. hola Ariel

    pero yo ye tengo articulos sobre ese tema
    [Entity Framework][Code First] Asociación mucho a muchos

    por eso te decia si revisaste los otros temas del blog

    saludos

    ResponderEliminar
  64. Si Leandro lei todos los articulos ..q estan excelentes . ya que no funciona la carga o navegacion sobre la lista de objetos contenidos dentro de la clase opte por no mapear esa clase y asignarle una lista de objetos a mano utilizando el get de la clase contenida. pero no anda quiere crear un campo q no existe. ejemplo Cuando consulto MenuItem

    dame todos los Items de menu Donde IdMenuPadre = id , me dice {"El nombre de columna 'ParentMenu_IdMenu' no es válido."} y si no existe no se pq la quiere crear.. asi estoy trabado. en estoy dias voy a tomar un curso presencial ojala puedan evacuar esta duda tan básica. alguien se topo con un problema asi??? Gracias Totales

    ResponderEliminar
  65. hola Ariel

    pero tu defines esa columna ParentMenu_IdMenu, o la esta creando EF de forma automatica?
    porque puede ser que el mapping de la relacion no este definido

    esta definiendo el HasMany() con el Map() para indicar que columnas usas para mapear la relacion ?

    no lo dejes solo con las propiedades porque EF va a usar un campos por defecto que quizas no tengas definidos

    saludos

    ResponderEliminar
  66. Leandro otra vez yo, consulta mapeo al realaciones en los maps. pero esas colecciones vienen vacias en el get. EF no te puebla las coleccciones IColleccion? aparte del mapeo las tengo que poblar a mano , od eberia poblarlas? si le cargas elementos salva la relacion?? Gracias totales

    ResponderEliminar
  67. ahora consulto los usuarios y hago varios for para ir trallendo los elementos de una relacion muchos a muchos de a un elemento pq no puebla.
    ojala se pueda hacer en EF asi no hay que hacerlo a mano

    public override List GetAll()
    {
    using (GigaContext context = new GigaContext())
    {
    List _l = (List)context.Set().ToList();
    IList _pu = new List();

    foreach (Usuario item in _l)
    {
    _pu = (List)context.Set().Where( x => x.idUsuario == item.Id) .ToList();
    foreach (PerfilPorUsuario _PerfilPorUsuario in _pu)
    {
    item.Perfiles.Add(_PerfilPorUsuario.Perfil);
    }
    }
    return _l;
    }
    }

    ResponderEliminar

  68. este map da error al consulta
    pq no lo pude descubrir aun

    CREATE TABLE [dbo].[tUsuarioPerfil](
    [idUsuarioPerfil] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [idUsuario] [int] NOT NULL,
    [idPerfil] [int] NOT NULL,
    [Version] [int] NOT NULL,

    HasMany(Perfil => Perfil.Usuarios)
    .WithMany(Usuario => Usuario.Perfiles)
    .Map(mc =>
    {
    mc.MapLeftKey("IdPerfil");
    mc.MapRightKey("idUsuario");
    mc.ToTable("tUsuarioPerfil");
    });

    en la otra clase al reves si funciona

    HasMany(Usuario => Usuario.Perfiles)
    .WithMany(Perfil => Perfil.Usuarios)
    .Map(mc =>
    {
    mc.MapLeftKey("IdUsuario");
    mc.MapRightKey("idPerfil");
    mc.ToTable("tUsuarioPerfil");
    });

    ResponderEliminar
  69. hola Ariel

    para cargar las relaciones tienes que usar el Include()

    Loading Related Entities

    saludos

    ResponderEliminar
  70. Hola Leandro, que tal.
    He estado viendo los artículos que has publicado acerca de Code First, de mas esta decir que son excelentes.
    Tengo una pregunta:
    En una solución de VS que contiene varios proyectos, y que define varios contextos diferentes (uno o mas por cada proyecto), requiero aislar la clase base, y a esta pasarle el contexto de acuerdo al proyecto que se este ejecutando. como puedo pasar dicho contexto a la clase base?.
    Tendrías un ejemplo o alguna referencia donde poder buscar mas información?
    Saludos
    Luis

    ResponderEliminar
  71. hola Luis

    todos los contextos crean su modelo contra la misma db, o cada contexto de EF crea su propia base de datos?

    en el repository podrias definir en el constructor que contexto utilizar
    algo como ser

    public class BaseRepository<T> : IRepository<T>{

    private DbContext _context;

    public BaseRepository(DbContext context){
    _context = context;
    }

    //resto codigo

    }

    de esta forma en la clase base puede cambiar el contexto segun que entidad lo utilice

    si usas IoC la instancia podrias inyectarla dinamicamente

    saludos

    ResponderEliminar
  72. Hola Leonardo, gracias por tu respuesta.
    Si efectivamente la idea es que cada contexto crea su modelo contra la BD. Voy a probar lo que indicas.
    Saludos y gracias

    ResponderEliminar
  73. Hola a todos alguien se le ocurre pq c este mapeo muchos a muchos esta buscando una columna idperfil en la tabla usuario siendo q es una tabla ternaria llmada tUsuarioPerfil??
    me sigue sin andar.
    hay alguna forma de meterle los perfiles a mana en la colleccion ya q no funciona??

    HasMany(Usuario => Usuario.Perfiles).WithMany(Perfil => Perfil.Usuarios)
    .Map(mc =>
    {
    mc.MapLeftKey("idUsuario");
    mc.MapRightKey("idPerfil");
    mc.ToTable("tUsuarioPerfil");
    });

    ResponderEliminar
  74. ojala encuentre algun curso de esto asi alguen lo hace andar. es muy loco no poder hacer un simple mapeo.
    alguien usa una bd sin mapear os ea con mapeo automatico? como? en lso videos no muesta como mapear muchos a muchos es idem al ejemplo empleado territorio

    ResponderEliminar
  75. hola Ariel

    recuerda que para la relacion importa desde que entidad estas configurando la relacion para definir el left y right con los campos
    si estas desde la entidad de usuario entonces el idUsuario deberia estar en MapRightKey()

    no revisaste los otros articulos

    [Entity Framework] Code First - Manejando Relaciones

    saludos

    ResponderEliminar
  76. Que tal Leandro, mucha gracias por tus aportes ayudan mucho, podrias hacer un post sobre estensiones IQueryable para realizar consultas de varias tablas ?

    ResponderEliminar
  77. podrias implementar UoW en este ejemplo?? como lo harias? Gracias

    ResponderEliminar
  78. Excelente Leo, perfecto, impecable..muchas gracias.

    ResponderEliminar