2011-08-25 15 views
9

con Entity Framework Code-First Tengo que tener todas las propiedades públicas para generar la base de datos, esto significa utilizar Entity Framework Code-First approach ¿Estoy obligado a tener un anemic domain mezclado con un modelo rico? Porque las propiedades que no necesitan ser públicas tienen que serlo.Uso del código de Entity Framework: primero, ¿necesito que el modelo de mi dominio sea anémico?

Por ejemplo:

Utilizando un modelo rico, esto con el marco de la entidad no funcionará:

public class Car 
{ 
    private int _actualPosition; 

    public void Accelerate() 
    { 
     this._actualPosition += 10; 
    } 
} 

Para trabajar tenemos que hacer _actualPosition pública:

public class Car 
{ 
    public int ActualPosition { get; set; } 

    public void Accelerate() 
    { 
     this.ActualPosition += 10; 
    } 
} 

Ahora Entity IMO es feo, porque tengo un método que agrega + 10 en la propiedad y lo tiene público al mismo tiempo, no quiero que operty sea público.

Otro ejemplo:

Imagínese que quiero una relación de muchos a muchos, pero con una sola manera como:

public class User 
{ 
    public long Id { get; set; } 

    public string Name { get; set; } 

    public IList<Role> Roles { get; set; } 
} 

public class Role 
{ 
    public long Id { get; set; } 

    public string Name { get; set; } 
} 

¿Cómo hago una relación de muchos a muchos con ese modelo? Que yo sepa, no hay manera de que voy a tener que hacer una relación de dos vías:

public class User 
{ 
    public long Id { get; set; } 

    public string Name { get; set; } 

    public IList<Role> Roles { get; set; } 
} 

public class Role 
{ 
    public long Id { get; set; } 

    public string Name { get; set; } 

    public IList<User> Users { get; set; } 
} 

Con este modelo voy a ser capaz de hacer muchos-a-muchos relación:

modelBuilder.Entity<User>() 
       .HasMany(x => x.Roles) 
       .WithMany(y => y.Users); 

I estoy en lo cierto? Si es así, Entity Framework no me agrada.

El "Otro ejemplo" trabajo con la respuesta de @Slauma:

modelBuilder.Entity<User>() 
      .HasMany(x => x.Roles) 
      .WithMany() 
      .Map(a => 
      { 
       a.MapLeftKey("UserId"); 
       a.MapRightKey("RoleId"); 
       a.ToTable("UserRoles"); 
      }); 
+0

Por cierto, no necesita propiedades públicas en EF EDMX, así que me gustaría se sorprenderá si lo hizo por el código EF primero. ¿Realmente no hay una anotación o algo que pueda usar para especificar las propiedades que desea? Sin embargo, creo que definitivamente necesitas accessors. ¿Funcionaría un acceso interno o protegido? (Originalmente pensé en privado, pero sospecho que debe protegerse al menos.) – Rup

+2

@Rup: No, no hay anotación. Las propiedades no tienen que ser públicas, pero deben estar visibles para el contexto y la asignación de EF. –

+0

@Ladislav ¿Está de acuerdo con EF en ese caso? –

Respuesta

1

respuesta corta: A menos que el marco soporta, que están atrapados tratando de interferir demasiado en esta clase si intenta con las reglas comerciales Y las propiedades públicas.

Respuesta larga: una vez que utiliza un sistema muy parecido a este, y básicamente me di cuenta de que la mejor manera para las pruebas y la encapsulación era para envolver esta clase con mi clase de dominio real.

public class Car 
{ 
    private CarPersistence _carPersistence; 

    public void Accelerate() 
    { 
     this._carPersistence.ActualPosition += 10; 
    } 

    public Car (CarPersistence car) { 
     _carPersistence = car; 
    } 
} 

public class CarPersistence 
{ 
    public int ActualPosition; 
} 

Cuando hice esto antes, yo era capaz de entonces mi nivel de clase baja persistencia implementar una interfaz que podría ser utilizado por mis pruebas para inyectar en la clase de negocios para las pruebas simples sin tener que hablar con la base de datos.

Un sistema como este, con sus clases de negocio en una "capa" y sus clases de persistencia en otra "capa", puede tener fácilmente relaciones uno a muchos, solo tiene que integrarse en lo que sea que construya su negocio clases fuera de las clases de persistencia:

public class User 
{ 
    private UserPersistence _user; 
    private IList<Persistence> _roles; 

    public User (UserPersistence user, IList<Persistence> roles) { 
     _user = user; 
     _roles = roles; 
    } 
} 

lo sentimos, la sintaxis puede no ser perfecto, eso es lo mejor que puedo recordar que la transliteración de VB en C#;)

1

Usted puede configurar su Car Entidad así:

public class Car 
{ 
    public int ActualPosition { get; private set; } 

    public void Accelerate() 
    { 
     this.ActualPosition += 10; 
    } 
} 

Esto fuerza la modificación de la propiedad ActualPosition para usar el método especializado Accelerate. Un efecto secundario del uso de este estilo es que su método Accelerate no se puede probar.

También puede configurarlo como esto:

public class Car 
{ 
    protected int ActualPosition { get; set; } 

    public void Accelerate() 
    { 
     this.ActualPosition += 10; 
    } 
} 

Sin embargo, este enfoque no es comprobable directamente sin necesidad de utilizar la reflexión.

0

Sí, el código que ha publicado es Anemic Model. Además, ni siquiera lo llamaría un código orientado a objetos adecuado. Simplemente no hay encapsulación y los objetos son solo contenedores de datos. Parece que su ORM de elección está limitando las opciones de implementación de su dominio. Por lo que vale, NHibernate le permite mapear campos directamente (incluso si estos campos son de solo lectura). No requiere que los objetos tengan propiedades. Por ejemplo:

public class Car { 

    private int _position; 

    public void Accelerate() { 
     _position += 10; 
    } 
} 

, pueden incluirse como:

<class name="Car" table="Cars" > 
    ... 
    <property name="_position" column="Position" /> 
    ... 
</class> 
+0

Sí, sé que NH lo permite, por lo que estoy sorprendido de por qué Entity Framework no hace el mismo reclamo. –

3

Su "Otro ejemplo" no es correcto. No está obligado a exponer ambos extremos de una relación como propiedades en sus clases modelo. En su ejemplo puede quitar public IList<User> Users { get; set; } de su clase Role y definir la asignación de este modo:

modelBuilder.Entity<User>() 
      .HasMany(x => x.Roles) 
      .WithMany() 
      .Map(a => 
      { 
       a.MapLeftKey("UserId"); 
       a.MapRightKey("RoleId"); 
       a.ToTable("UserRoles"); 
      }); 

Este es el caso también para las relaciones de uno a muchos. Siempre hay una sobrecarga sin parámetros With..., WithMany(), WithRequired(), WithOptional(), etc. para definir asignaciones para las relaciones donde no se exponen ambos extremos en el modelo.

+0

¿Pero si genero una base de datos desde Entity Framework? ¿Estás seguro de que no necesitaré exponer ambas propiedades? –

+0

tienes razón, yo hago la prueba. gracias, actualizaré la pregunta. –

+0

@Acaz: Probablemente EF creará ambas colecciones de manera predeterminada cuando se crea el modelo desde DB. Pero puede eliminar un lado de la relación de las clases modelo generadas. – Slauma

1

Creo que lo que busca es la siguiente:

public class Car 
{ 
    public int Id { get; set; } 
    public int ActualPosition { get; private set; } 

    public void Accelerate() 
    { 
     this.ActualPosition += 10; 
    } 
} 

La unidad de prueba es el siguiente:

[TestMethod] 
    public void AccellerateWillAddTenUnitsToTheActualPosition() 
    { 
     //Arrange 
     var car = new Car(); 
     var actualPosition = car.ActualPosition; 
     var expectedPosition = actualPosition + 10; 

     //Act 
     car.Accelerate(); 

     //Assert 
     Assert.AreEqual(car.ActualPosition, expectedPosition); 
    } 
Cuestiones relacionadas