Filtre todas las propiedades de navegación antes de que se carguen (perezosas o ansiosas) en la memoria

Para futuros visitantes: para EF6 probablemente sea mejor usar filtros, por ejemplo, a través de este proyecto:https://github.com/jbogard/EntityFramework.Filters

En la aplicación que estamos creando, aplicamos el patrón de "eliminación suave" donde cada clase tiene un bool 'Eliminado'. En la práctica, cada clase simplemente hereda de esta clase base:

public abstract class Entity
{
    public virtual int Id { get; set; }

    public virtual bool Deleted { get; set; }
}

Para dar un breve ejemplo, supongamos que tengo las clasesGymMember yWorkout:

public class GymMember: Entity
{
    public string Name { get; set; }

    public virtual ICollection<Workout> Workouts { get; set; }
}

public class Workout: Entity
{
    public virtual DateTime Date { get; set; }
}

Cuando busco la lista de miembros del gimnasio de la base de datos, puedo asegurarme de que ninguno de los miembros del gimnasio 'eliminados' sea recuperado, como este

var gymMembers = context.GymMembers.Where(g => !g.Deleted);

Sin embargo, cuando itero a través de estos miembros del gimnasio, suWorkouts se cargan desde la base de datos sin tener en cuenta suDeleted bandera. Si bien no puedo culpar a Entity Framework por no aceptar esto, me gustaría configurar o interceptar la carga perezosa de propiedades para que las propiedades de navegación eliminadas nunca se carguen.

He estado revisando mis opciones, pero parecen escasas:

Caminante aDatabase First y use la asignación condicional para cada objeto para cada propiedad de uno a muchos.

Esto simplemente no es una opción, ya que sería demasiado trabajo manual. (Nuestra aplicación es enorme y cada día es más enorme). Tampoco queremos renunciar a las ventajas de usar Code First (de los cuales hay muchos)

Siempre cargando ansiosamente las propiedades de navegación.

De nuevo, no es una opción. Esta configuración solo está disponible por entidad. La carga de entidades siempre con impaciencia también impondría una penalización de rendimiento grave.

Aplicando el patrón de Expression Visitor que se inyecta automáticamente..Where(e => !e.Deleted) en cualquier lugar encuentra unIQueryable<Entity>, tal como se describeaquí yaquí.

De hecho, probé esto en una aplicación de prueba de concepto, y funcionó de maravilla. Esta fue una opción muy interesante, pero, por desgracia, no aplica el filtrado a las propiedades de navegación cargadas perezosamente. Esto es obvio, ya que esas propiedades perezosas no aparecerían en la expresión / consulta y, por lo tanto, no se pueden reemplazar. Me pregunto si Entity Framework permitiría un punto de inyección en algún lugar de suDynamicProxy Clase que carga las propiedades perezosas. También temo por otras consecuencias, como la posibilidad de romper elInclude Mecanismo en EF.

Escribiendo una clase personalizada que implementa ICollection pero filtra laDeleted Entidades automáticamente.

Este fue en realidad mi primer enfoque. La idea sería usar una propiedad de respaldo para cada propiedad de colección que internamente use una clase de Colección personalizada:

public class GymMember: Entity
{
    public string Name { get; set; }

    private ICollection<Workout> _workouts;
    public virtual ICollection<Workout> Workouts 
    { 
        get { return _workouts ?? (_workouts = new CustomCollection()); }
        set { _workouts = new CustomCollection(value); }
     }

}

Si bien este enfoque no es realmente malo, todavía tengo algunos problemas con él:

Todavía carga todo elWorkouts en la memoria y filtra elDeleted unos cuando se golpea el establecedor de propiedades. En mi humilde opinión, esto es demasiado tarde.

Existe una discrepancia lógica entre las consultas ejecutadas y los datos que se cargan.

Imagen de un escenario donde quiero una lista de los miembros del gimnasio que hicieron un entrenamiento desde la semana pasada:

var gymMembers = context.GymMembers.Where(g => g.Workouts.Any(w => w.Date >= DateTime.Now.AddDays(-7).Date));

Esta consulta puede devolver a un miembro del gimnasio que solo tiene entrenamientos que se eliminan pero que también satisfacen el predicado. Una vez que se cargan en la memoria, ¡parece que este miembro del gimnasio no tiene ningún entrenamiento en absoluto! Se podría decir que el desarrollador debe ser consciente de laDeleted y siempre incluyalo en sus consultas, pero eso es algo que realmente me gustaría evitar. Tal vez el ExpressionVisitor podría ofrecer la respuesta aquí de nuevo.

En realidad, es imposible marcar una propiedad de navegación comoDeleted cuando se utiliza el CustomCollection.

Imagina este escenario:

var gymMember = context.GymMembers.First();
gymMember.Workouts.First().Deleted = true;
context.SaveChanges();`

Usted esperaría que el apropiadoWorkout el registro se actualiza en la base de datos, y usted estaría equivocado! Desde elgymMember está siendo inspeccionado por elChangeTracker Para cualquier cambio, la propiedad.gymMember.Workouts De repente volverá 1 entrenamiento menos. Esto se debe a que CustomCollection filtra automáticamente las instancias eliminadas, ¿recuerdas? Así que ahora Entity Framework cree que el entrenamiento debe eliminarse, y EF intentará establecer el FK en nulo, o eliminar el registro. (Dependiendo de cómo esté configurada su base de datos). ¡Esto es lo que intentábamos evitar con el patrón de eliminación suave para empezar!

Me encontré con unblog interesante publicación que anula el valor predeterminadoSaveChanges método de laDbContext para que cualquier entrada con unaEntityState.Deleted se cambian de nuevo aEntityState.Modified pero esto de nuevo se siente 'hacky' y bastante inseguro. Sin embargo, estoy dispuesto a probarlo si resuelve problemas sin ningún efecto secundario no deseado.

Así que aquí estoy StackOverflow. He investigado mis opciones bastante extensamente, si puedo decirlo yo mismo, y estoy en mi punto final. Así que ahora me dirijo a ti. ¿Cómo ha implementado las eliminaciones de software en su aplicación empresarial?

Para reiterar, estos son los requisitos que estoy buscando:

Las consultas deben excluir automáticamente laDeleted entidades en el nivel DBEliminar una entidad y llamar a 'SaveChanges' debería simplemente actualizar el registro apropiado y no tener otros efectos secundarios.Cuando se cargan las propiedades de navegación, ya sean perezosas o ansiosas, elDeleted Unos deberían ser automáticamente excluidos.

Espero con interés cualquier y todas las sugerencias, gracias de antemano.

Respuestas a la pregunta(3)

Su respuesta a la pregunta