NO usando el patrón de repository, use el ORM como está (EF)

Siempre utilicé el patrón Repository, pero para mi último proyecto quería ver si podía perfeccionar el uso del mismo y mi implementación de “Unit Of Work”. Cuanto más comencé a cavar, comencé a hacerme la siguiente pregunta: “¿Realmente lo necesito?”

Ahora todo esto comienza con un par de comentarios sobre Stackoverflow con un rastro de la publicación de Ayende Rahien en su blog, con 2 específicos,

  • repository-is-the-new-singleton
  • ask-ayende-life-without-repositories-are-they-worth-living

Esto probablemente podría hablarse para siempre y depende de diferentes aplicaciones. Lo que me gusta saber

  1. ¿Sería este enfoque adecuado para un proyecto de Entity Framework?
  2. El uso de este enfoque es la lógica de negocios que todavía está en una capa de servicio, o métodos de extensión (como se explica a continuación, lo sé, el método de extensión utiliza la sesión de NHib).

Eso se hace fácilmente usando métodos de extensión. Limpio, simple y reutilizable.

public static IEnumerable GetAll( this ISession instance, Expression<Func> where) where T : class { return instance.QueryOver().Where(where).List(); } 

Usando este enfoque y Ninject como DI, ¿necesito hacer del Context una interfaz e inyectar eso en mis controladores?

He recorrido muchos caminos y he creado muchas implementaciones de repositorys en diferentes proyectos y … He tirado la toalla y abandonado, aquí está el por qué.

Codificación para la excepción

¿Codifica el 1% de posibilidades de que su base de datos cambie de una tecnología a otra? Si está pensando en el estado futuro de su negocio y dice que sí, es una posibilidad, entonces a) deben tener una gran cantidad de dinero para permitirse realizar una migración a otra tecnología de DB o b) está eligiendo una tecnología de DB para divertirse o c ) algo ha ido terriblemente mal con la primera tecnología que decidiste usar.

¿Por qué tirar la rica syntax de LINQ?

LINQ y EF se desarrollaron para que pudieras hacer cosas buenas con él para leer y recorrer gráficos de objetos. Crear y mantener un repository que pueda darle la misma flexibilidad para hacerlo es una tarea monstruosa. En mi experiencia cada vez que he creado un repository SIEMPRE he tenido una fuga de lógica de negocios en la capa de repository para hacer las consultas más efectivas y / o reducir el número de visitas a la base de datos.

No quiero crear un método para cada permutación de una consulta que tengo que escribir. También podría escribir procedimientos almacenados. No quiero GetOrder, GetOrderWithOrderItem, GetOrderWithOrderItemWithOrderActivity, GetOrderByUserId, y así sucesivamente … Solo quiero obtener la entidad principal y la poligonal e incluir el gráfico de objetos como me gusta.

La mayoría de los ejemplos de repositorys son tonterías

A menos que esté desarrollando algo REALMENTE escueto como un blog o algo así, sus consultas nunca serán tan simples como el 90% de los ejemplos que encuentre en Internet en torno al patrón del repository. ¡No puedo enfatizar esto lo suficiente! Esto es algo que uno tiene que arrastrarse por el lodo para descubrirlo. Siempre habrá una consulta que rompe su repository / solución perfectamente pensada que ha creado, y no es hasta ese punto en el que comienza a adivinar usted mismo y comienza la erosión / deuda técnica.

No me pruebes la unidad, hermano

Pero ¿qué pasa con las pruebas unitarias si no tengo un repository? ¿Cómo me burlaré? Simple tu no. Veamos desde ambos angularjs:

Sin repository: puede simular el DbContext utilizando un IDbContext u otros trucos, pero entonces realmente está probando unidades LINQ a objetos y no LINQ a entidades porque la consulta se determina en tiempo de ejecución … OK, ¡eso no es bueno! Entonces ahora depende de la prueba de integración para cubrir esto.

Con repository: ahora puedes simularte con tus repositorys y probar la capa (es) en medio. ¿Bien, verdad? Bueno, en realidad no … En los casos anteriores donde tiene que filtrar la lógica en la capa de repository para realizar consultas más efectivas y / o menos visitas a la base de datos, ¿cómo pueden cubrir las pruebas de su unidad eso? Ahora está en la capa de repository y no quieres probar IQueryable ¿verdad? También seamos honestos, las pruebas de su unidad no cubrirán las consultas que tienen una línea de 20 líneas. .Where() y .Include() es un grupo de relaciones y golpea la base de datos nuevamente para hacer todo esto, bla, bla, bla, de todos modos, porque la consulta se genera en tiempo de ejecución. También desde que creó un repository para mantener la persistencia de las capas superiores ignorante, si ahora desea cambiar la tecnología de su base de datos, lo siento, las pruebas de su unidad definitivamente no garantizarán los mismos resultados en tiempo de ejecución, de vuelta a las pruebas de integración. Entonces, el objective del repository parece extraño.

2 centavos

Ya hemos perdido mucha funcionalidad y syntax al utilizar EF sobre procedimientos almacenados simples (inserciones masivas, eliminaciones masivas, CTE, etc.) pero también código en C # para no tener que escribir en binario. Utilizamos EF para que podamos tener la posibilidad de utilizar diferentes proveedores y trabajar con gráficos de objetos de una manera muy relacionada entre muchas cosas. Ciertas abstracciones son útiles y otras no.

Espero que esto ayude a alguien en los internets en alguna parte …

El patrón de repository es una abstracción . Su propósito es reducir la complejidad y hacer que el rest del código permanezca ignorante. Como beneficio adicional, le permite escribir pruebas unitarias en lugar de pruebas de integración .

El problema es que muchos desarrolladores no comprenden el propósito de los patrones y crean repositorys que filtran información específica de la persistencia hasta la persona que llama (generalmente al exponer IQueryable ). Al hacerlo, no obtienen ningún beneficio sobre el uso de OR / M directamente.

Actualizar para abordar otra respuesta

Codificación para la excepción

El uso de repositorys no se trata de poder cambiar la tecnología de persistencia (es decir, cambiar la base de datos o usar un servicio web, etc.). Se trata de separar la lógica empresarial de la persistencia para reducir la complejidad y el acoplamiento.

Pruebas unitarias vs pruebas de integración

No escribe pruebas unitarias para repositorys. período.

Pero al introducir repositorys (o cualquier otra capa de abstracción entre la persistencia y el negocio) puede escribir pruebas unitarias para la lógica comercial. es decir, no tiene que preocuparse de que sus pruebas fallen debido a una base de datos mal configurada.

En cuanto a las consultas. Si usa LINQ, también debe asegurarse de que sus consultas funcionen, al igual que con los repositorys. y eso se hace usando pruebas de integración.

La diferencia es que si no ha mezclado su empresa con las declaraciones LINQ, puede estar 100% seguro de que es su código de persistencia el que está fallando y no es otra cosa.

Si analiza sus pruebas, verá que son mucho más limpias si no ha mezclado las inquietudes (es decir, la lógica empresarial LINQ +).

Ejemplos de repository

La mayoría de los ejemplos son tonterías. eso es muy cierto. Sin embargo, si busca en Google cualquier patrón de diseño, encontrará muchos ejemplos de mierda. Esa no es una razón para evitar el uso de un patrón.

Construir una implementación de repository correcta es muy fácil. De hecho, solo debes seguir una sola regla:

No agregue nada en la clase de repository hasta el momento en que lo necesite

Muchos codificadores son flojos e intentan crear un repository genérico y usan una clase base con muchos métodos que podrían necesitar. YAGNI. Usted escribe la clase de repository una vez y la conserva mientras la aplicación viva (pueden ser años). ¿Por qué joderlo siendo flojo? Mantenlo limpio sin herencia de clase base. Hará que sea mucho más fácil de leer y mantener.

(La statement anterior es una guía y no una ley. Una clase base puede estar muy motivada. Solo piense antes de agregarla, para que la agregue por las razones correctas)

Cosas viejas

Conclusión:

Si no le importa tener declaraciones LINQ en su código comercial ni le importan las pruebas unitarias, no veo ninguna razón para no usar Entity Framework directamente.

Actualizar

He escrito un blog sobre el patrón de repository y lo que realmente significa “abstracción”: http://blog.gauffin.org/2013/01/repository-pattern-done-right/

Actualización 2

Para el tipo de entidad única con más de 20 campos, ¿cómo diseñarás el método de consulta para admitir cualquier combinación de permutación? No desea limitar la búsqueda solo por nombre, qué ocurre con las propiedades de navegación, enumerar todas las órdenes con ítem con código de precio específico, 3 niveles de búsqueda de propiedades de navegación. La razón por la que se inventó IQueryable fue poder componer cualquier combinación de búsqueda con base de datos. Todo se ve muy bien en teoría, pero la necesidad del usuario gana por encima de la teoría.

De nuevo: una entidad con más de 20 campos está modelada incorrectamente. Es una entidad DIOS Descomponerlo.

No estoy argumentando que IQueryable no fue hecho para quering. Estoy diciendo que no es correcto para una capa de abstracción como el patrón Repositorio, ya que tiene fugas. No hay un proveedor LINQ to Sql 100% completo (como EF).

Todos ellos tienen cosas específicas de implementación, como cómo usar la carga ansiosa / diferida o cómo hacer sentencias SQL “IN”. Exponer IQueryable en el repository obliga al usuario a saber todas esas cosas. Por lo tanto, todo el bash de abstraer la fuente de datos es una falla completa. Simplemente agrega complejidad sin obtener ningún beneficio sobre el uso de OR / M directamente.

Implemente el patrón Repositorio correctamente o simplemente no lo use en absoluto.

(Si realmente desea manejar entidades grandes, puede combinar el patrón Repositorio con el patrón de Especificación . Eso le brinda una abstracción completa que también se puede probar).

IMO tanto la abstracción de Repository como la abstracción de UnitOfWork tienen un lugar muy valioso en cualquier desarrollo significativo. La gente discutirá sobre los detalles de la implementación, pero así como hay muchas maneras de despellejar a un gato, hay muchas maneras de implementar una abstracción.

Su pregunta es específicamente para usar o no usar y por qué.

Como sin duda se habrá dado cuenta de que ya tiene estos dos patrones integrados en Entity Framework, DbContext es UnitOfWork y DbSet es el Repository . Por lo general, no es necesario probar la UnitOfWork o Repository sí mismos, ya que simplemente están facilitando entre sus clases y las implementaciones subyacentes de acceso a datos. Lo que encontrará que necesita hacer, una y otra vez, es burlarse de estas dos abstracciones cuando la unidad prueba la lógica de sus servicios.

Puede simular, falsificar o lo que sea, con bibliotecas externas que agregan capas de dependencias de código (que no controla) entre la lógica que realiza la prueba y la lógica que se prueba.

Por lo tanto, un punto menor es que tener su propia abstracción para UnitOfWork and Repository le brinda el máximo control y flexibilidad cuando se burla de las pruebas de su unidad.

Todo muy bien, pero para mí, el verdadero poder de estas abstracciones es que proporcionan una forma simple de aplicar las técnicas de Progtwigción Orientada a Aspectos y adherirse a los principios SÓLIDOS .

Entonces tienes tu IRepository :

 public interface IRepository where T : class { T Add(T entity); void Delete(T entity); IQueryable AsQueryable(); } 

Y su implementación:

 public class Repository : IRepository where T : class { private readonly IDbSet _dbSet; public Repository(PPContext context) { _dbSet = context.Set(); } public T Add(T entity) { return _dbSet.Add(entity); } public void Delete(T entity) { _dbSet.Remove(entity); } public IQueryable AsQueryable() { return _dbSet.AsQueryable(); } } 

Nada fuera de lo común hasta el momento, pero ahora queremos agregar algo de registro, fácil con un decorador de registro.

 public class RepositoryLoggerDecorator : IRepository where T : class { Logger logger = LogManager.GetCurrentClassLogger(); private readonly IRepository _decorated; public RepositoryLoggerDecorator(IRepository decorated) { _decorated = decorated; } public T Add(T entity) { logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() ); T added = _decorated.Add(entity); logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); return added; } public void Delete(T entity) { logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); _decorated.Delete(entity); logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); } public IQueryable AsQueryable() { return _decorated.AsQueryable(); } } 

Todo hecho y sin cambios en nuestro código existente . Existen muchas otras preocupaciones transversales que podemos agregar, como el manejo de excepciones, almacenamiento en memoria caché de datos, validación de datos o lo que sea, y en todo nuestro proceso de diseño y construcción lo más valioso que tenemos que nos permite agregar características simples sin cambiar nuestro código existente es nuestra abstracción de IRepository .

Ahora, muchas veces he visto esta pregunta en StackOverflow: “¿cómo se puede hacer que Entity Framework funcione en un entorno de múltiples inquilinos?”.

https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant

Si tiene una abstracción de Repository , la respuesta es “es fácil agregar un decorador”

 public class RepositoryTennantFilterDecorator : IRepository where T : class { //public for Unit Test example public readonly IRepository _decorated; public RepositoryTennantFilterDecorator(IRepository decorated) { _decorated = decorated; } public T Add(T entity) { return _decorated.Add(entity); } public void Delete(T entity) { _decorated.Delete(entity); } public IQueryable AsQueryable() { return _decorated.AsQueryable().Where(o => true); } } 

IMO siempre debe colocar una abstracción simple sobre cualquier componente de terceros al que se haga referencia en más de un puñado de lugares. Desde esta perspectiva, un ORM es el candidato perfecto ya que se hace referencia en gran parte de nuestro código.

La respuesta que normalmente viene a la mente cuando alguien dice “¿por qué debería tener una abstracción (por ejemplo, un Repository ) sobre esta o esa biblioteca de terceros?” Es “¿por qué no lo haría?”

Los decoradores PS son extremadamente simples de aplicar usando un contenedor IoC, como SimpleInjector .

 [TestFixture] public class IRepositoryTesting { [Test] public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository() { Container container = new Container(); container.RegisterLifetimeScope(); container.RegisterOpenGeneric( typeof(IRepository<>), typeof(Repository<>)); container.RegisterDecorator( typeof(IRepository<>), typeof(RepositoryLoggerDecorator<>)); container.RegisterDecorator( typeof(IRepository<>), typeof(RepositoryTennantFilterDecorator<>)); container.Verify(); using (container.BeginLifetimeScope()) { var result = container.GetInstance>(); Assert.That( result, Is.InstanceOf(typeof(RepositoryTennantFilterDecorator))); Assert.That( (result as RepositoryTennantFilterDecorator)._decorated, Is.InstanceOf(typeof(RepositoryLoggerDecorator))); } } } 

En primer lugar, como lo sugiere una respuesta, EF en sí es un patrón de repository, no hay necesidad de crear más abstracción solo para nombrarlo como repository.

Repositorio ficticio para pruebas unitarias, ¿realmente lo necesitamos?

Dejamos que EF se comunique con DB de prueba en pruebas unitarias para probar nuestra lógica de negocios directamente contra DB de prueba SQL. No veo ningún beneficio de tener simulacros de ningún patrón de repository en absoluto. ¿Qué está realmente mal haciendo pruebas unitarias contra la base de datos de prueba? Como las operaciones a granel no son posibles, terminamos escribiendo SQL sin procesar. SQLite en memoria es el candidato perfecto para hacer pruebas unitarias contra una base de datos real.

Abstracción innecesaria

¿Desea crear un repository solo para que en el futuro pueda reemplazar fácilmente a EF con NHbibernate, etc. o cualquier otra cosa? Suena genial plan, pero ¿es realmente rentable?

Linq mata las pruebas unitarias?

Me gustaría ver ejemplos sobre cómo puede matar.

Inyección de Dependencia, IoC

Wow, estas son palabras geniales, seguro que se ven geniales en teoría, pero a veces hay que optar entre un gran diseño y una gran solución. Usamos todo eso, y terminamos tirando todo a la basura y eligiendo un enfoque diferente. Tamaño vs Velocidad (Tamaño del código y Velocidad de desarrollo) es muy importante en la vida real. Los usuarios necesitan flexibilidad, no les importa si su código tiene un diseño excelente en términos de DI o IoC.

A menos que estés construyendo Visual Studio

Todos estos grandes diseños son necesarios si está construyendo un progtwig complejo como Visual Studio o Eclipse que será desarrollado por muchas personas y debe ser altamente personalizable. Todo el gran patrón de desarrollo entró en la imagen después de años de desarrollo que estos IDE han pasado, y han evolucionado en un lugar donde todos estos grandes patrones de diseño importan tanto. Pero si está haciendo una simple nómina basada en web, o una simple aplicación de negocios, es mejor que evolucione en su desarrollo con el tiempo, en lugar de dedicarle tiempo a construirlo para millones de usuarios, donde solo se desplegará para cientos de usuarios.

Repositorio como vista filtrada – ISecureRepository

Por otro lado, el repository debe ser una vista filtrada de EF que protege el acceso a los datos mediante la aplicación del relleno necesario en función del usuario / rol actual.

Pero hacerlo complica el repository aún más, ya que termina en una gran base de código para mantener. La gente termina creando repositorys diferentes para diferentes tipos de usuarios o una combinación de tipos de entidades. No solo esto, también terminamos con muchos DTO.

La siguiente respuesta es una implementación de ejemplo de Repositorio filtrado sin crear todo un conjunto de clases y métodos. Puede que no responda la pregunta directamente, pero puede ser útil para derivar una.

Descargo de responsabilidad: soy autor de Entity REST SDK.

http://entityrestsdk.codeplex.com

Teniendo en cuenta lo anterior, desarrollamos un SDK que crea un depósito de vista filtrada basado en SecurityContext que contiene filtros para operaciones CRUD. Y solo dos tipos de reglas simplifican cualquier operación compleja. Primero es el acceso a la entidad, y otra es la regla de Lectura / Escritura para la propiedad.

La ventaja es que no reescribe la lógica de negocios o repositorys para diferentes tipos de usuarios, simplemente bloquea o les otorga el acceso.

 public class DefaultSecurityContext : BaseSecurityContext { public static DefaultSecurityContext Instance = new DefaultSecurityContext(); // UserID for currently logged in User public static long UserID{ get{ return long.Parse( HttpContext.Current.User.Identity.Name ); } } public DefaultSecurityContext(){ } protected override void OnCreate(){ // User can access his own Account only var acc = CreateRules(); acc.SetRead( y => x=> x.AccountID == UserID ) ; acc.SetWrite( y => x=> x.AccountID == UserID ); // User can only modify AccountName and EmailAddress fields acc.SetProperties( SecurityRules.ReadWrite, x => x.AccountName, x => x.EmailAddress); // User can read AccountType field acc.SetProperties( SecurityRules.Read, x => x.AccountType); // User can access his own Orders only var order = CreateRules(); order.SetRead( y => x => x.CustomerID == UserID ); // User can modify Order only if OrderStatus is not complete order.SetWrite( y => x => x.CustomerID == UserID && x.OrderStatus != "Complete" ); // User can only modify OrderNotes and OrderStatus order.SetProperties( SecurityRules.ReadWrite, x => x.OrderNotes, x => x.OrderStatus ); // User can not delete orders order.SetDelete(order.NotSupportedRule); } } 

Estas reglas LINQ se evalúan en base a la base de datos en el método SaveChanges para cada operación, y estas reglas actúan como firewall frente a la base de datos.

Existe un gran debate sobre qué método es el correcto, así que lo miro porque ambos son aceptables, así que siempre uso el que más me gusta (que no es repository, UoW).

En EF UoW se implementa a través de DbContext y los DbSets son repositorys.

En cuanto a cómo trabajar con la capa de datos, simplemente trabajo directamente en el objeto DbContext, para consultas complejas haré métodos de extensión para la consulta que se pueden reutilizar.

Creo que Ayende también tiene algunos comentarios sobre cómo el resucitar las operaciones de CUD es malo.

Siempre hago una interfaz y heredo mi contexto para poder usar un contenedor IoC para DI.

Linq es un “Depósito” en la actualidad.

ISession + Linq ya es el repository, y no necesita métodos GetXByY ni GetXByY QueryData(Query q) . Siendo un poco paranoico con el uso de DAL, prefiero la interfaz de repository. (Desde el punto de vista de la mantenibilidad, también debemos tener cierta fachada sobre las interfaces de acceso a datos específicas).

Este es el repository que usamos, nos desvincula del uso directo de nhibernate, pero proporciona una interfaz linq (como el acceso de ISession en casos excepcionales, que eventualmente están sujetos a la refactorización).

 class Repo { ISession _session; //via ioc IQueryable Query() { return _session.Query(); } } 

El repository (o como se decida llamarlo) en este momento para mí se trata principalmente de abstraer la capa de persistencia.

Lo uso junto con objetos de consulta, por lo que no tengo un acoplamiento a ninguna tecnología en particular en mis aplicaciones. Y también facilita mucho las pruebas.

Entonces, tiendo a tener

 public interface IRepository : IDisposable { void Save(TEntity entity); void SaveList(IEnumerable entities); void Delete(TEntity entity); void DeleteList(IEnumerable entities); IList GetAll() where TEntity : class; int GetCount() where TEntity : class; void StartConversation(); void EndConversation(); //if query objects can be self sustaining (ie not need additional configuration - think session), there is no need to include this method in the repository. TResult ExecuteQuery(IQueryObject query); } 

Posiblemente agregue métodos asíncronos con devoluciones de llamadas como delegates. El repository es fácil de implementar genéricamente , así que no puedo tocar una línea de implementación desde la aplicación a la aplicación. Bueno, esto es cierto al menos cuando uso NH, también lo hice con EF, pero me hizo odiar a EF. 4. La conversación es el comienzo de una transacción. Muy bueno si unas pocas clases comparten la instancia del repository. Además, para NH, un informe en mi implementación equivale a una sesión que se abre en la primera solicitud.

Luego, los objetos de consulta

 public interface IQueryObject { /// Provides configuration options. ///  /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository. /// If not used through a repository, it can be useful as a configuration option. ///  void Configure(object parameter); /// Implementation of the query. TResult GetResult(); } 

Para la configuración que uso en NH solo para pasar en la ISession. En EF no tiene sentido más o menos.

Una consulta de ejemplo sería … (NH)

 public class GetAll : AbstractQueryObject> where TEntity : class { public override IList GetResult() { return this.Session.CreateCriteria().List(); } } 

Para hacer una consulta de EF, debe tener el contexto en la base de Abstract, no en la sesión. Pero, por supuesto, el ifc sería el mismo.

De esta forma, las consultas se encapsulan y son fácilmente comprobables. Lo mejor de todo es que mi código depende solo de las interfaces. Todo está muy limpio Los objetos de dominio (negocios) son solo eso, por ejemplo, no se mezclan responsabilidades como cuando se usa el patrón de registro activo que difícilmente se puede probar y se mezcla el código de acceso de datos (consulta) en el objeto de dominio y al hacerlo se mezclan preocupaciones (objeto que capta ¿¿sí mismo??). Todos todavía son libres de crear POCO para la transferencia de datos.

En general, se proporciona mucha reutilización de código y simplicidad con este enfoque sin perder nada de lo que pueda imaginar. ¿Algunas ideas?

Y muchas gracias a Ayende por sus excelentes publicaciones y dedicación continua. Son sus ideas aquí (objeto de consulta), no mío.

Lo que más se aplica sobre EF no es un patrón de repository. Es un patrón de fachada (que resume las llamadas a los métodos EF en versiones más simples y fáciles de usar).

EF es el que aplica el patrón de repository (y el patrón de unidad de trabajo también). Es decir, EF es el que abstrae la capa de acceso a datos para que el usuario no tenga idea de que está tratando con SQLServer.

Y en eso, la mayoría de los “repositorys” sobre EF no son ni siquiera buenas Fachadas, ya que simplemente trazan, de manera bastante directa, métodos únicos en EF, incluso hasta el punto de tener las mismas firmas.

Las dos razones, entonces, para aplicar este patrón denominado “Repositorio” sobre EF, es permitir una prueba más sencilla y establecer un subconjunto de llamadas “enlatadas”. No está mal en sí mismos, pero claramente no es un Repositorio.

Para mí, es una decisión simple, con relativamente pocos factores. Los factores son:

  1. Los repositorys son para clases de dominio.
  2. En algunas de mis aplicaciones, las clases de dominio son las mismas que mis clases de persistencia (DAL), en otras no lo son.
  3. When they are the same, EF is providing me with Repositories already.
  4. EF provides lazy loading and IQueryable. I like these.
  5. Abstracting/’facading’/re-implementing repository over EF usually means loss of lazy and IQueryable

So, if my app can’t justify #2, separate domain and data models, then I usually won’t bother with #5.