Las clases de entidad se desacoplaron de LINQ al proveedor de SQL para implementar el patrón Repository. ¿Cómo?

StackOverflow https://stackoverflow.com/questions/229627

Pregunta

He revisado el patrón del Repositorio y reconocí algunas ideas que estaba usando en el pasado que me hicieron sentir bien.

Sin embargo, ahora me gustaría escribir una aplicación que usaría este patrón PERO ME GUSTARÍA DESACOPLAR LAS CLASES DE ENTIDAD del proveedor del repositorio.

Yo crearía varios ensamblados:

  1. una " Interfaces " conjunto que alojaría interfaces comunes, incluida la interfaz IRepository
  2. una " Entidades " conjunto que alojaría las clases de entidad como Producto, Usuario, Orden, etc. Este ensamblaje sería referenciado por la " Interfaces " montaje ya que algunos métodos devolverían tales tipos o matrices de ellos. También sería referenciado por el conjunto de la aplicación principal (como la aplicación web)
  3. uno o más ensamblados / ensamblajes del proveedor del repositorio. Cada uno incluiría (al menos) una clase que implementa la interfaz IRepository y funcionaría con un determinado Data Store. Los almacenes de datos podrían incluir un servidor SQL, un servidor Oracle, MySQL, archivos XML, servicios web / WCF, etc.

El estudio de LINQ to SQL que parece muy productivo en términos de tiempo para implementar todo parece bien hasta que descubro la profunda dependencia entre las clases generadas y la clase CustomDataContext.

¿Cómo puedo usar LINQ to SQL en tal escenario?

¿Fue útil?

Solución

No sé si esto es exactamente lo que quieres, pero es posible que desees echar un vistazo al código MVC Storefront de Rob Conery. Utiliza una variante del patrón de repositorio con un proveedor de linq. Asigna los objetos LINQ a Sql a objetos de dominio y luego devuelve los objetos de dominio desde el proveedor del repositorio a una capa de servicio que envuelve al proveedor, lo que le permite trabajar algo de lógica en los datos devueltos antes de que llegue a la capa empresarial.

Webcasts de MVC Storefront
Código

A mí me parece que quiere que los proveedores devuelvan DTO y luego que asignen los DTO a los objetos de dominio en la capa de servicio / repositorio. Si este es el caso, puede asignar su LINQ al proveedor de SQL a los DTO, hacer que los devuelva y luego asignar los DTO a los objetos de dominio en la capa de servicio / repositorio. Esto debería funcionar bien, pero puede volverse tedioso, ya que ahora tendría 2 capas de mapeo.

En este caso tendrías: ProductService, que toma un IProductRepository. Evoca métodos en el IProductRepository para recuperar tus DTOs. A continuación, asigna los DTO a los objetos comerciales reales y los devuelve al código de llamada.

Otros consejos

Puede crear un archivo XML externo que asigne la base de datos a cualquier clase:

 <?xml version="1.0" encoding="utf-8"?>
 <Database Name="DbName" 
           xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
    <Table Name="DbTableName">
       <Type Name="EntityClassName" >
           <Column Name="ID" Type="System.Int64" Member="Id"
                   DbType="BigInt NOT NULL IDENTITY" IsPrimaryKey="true"
                   CanBeNull="false" />
           <Column Name="ColumnName" Type="System.String" Member="PropertyA"
                   DbType="VarChar(1024)" CanBeNull="true" />
       </Type>
    </Table>
 </Database>

Y luego pase el XML a una clase de DataContext:

 using (var cn = GetDbConnection())
  { var mappingSrc = XmlMappingSource.FromReader(xmlReader);

    using (var db = new DataContext(cn, mappingSrc))
     { var q = from entity in db.GetTable<EntityClassName>()
               where entity.PropertyA = "..."
               select entity.ID;
     }
  }

Encontré una fantástica publicación de blog (con un montón de código bueno) sobre esto aquí: http://iridescence.no/post/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx

Creo que quieres la compatibilidad con POCO (objetos CLR antiguos). LINQ to SQL tiene un adaptador llamado Close2Poco .

Pero recomendaría hacer el cambio a Entity Framework, en este momento también tienen un adaptador POCO , pero en v2 se espera que sea compatible fuera de la caja .

No tiene que usar el código LINQ to SQL generado, puede decorar sus propias clases con los atributos de columna necesarios o usar un archivo de asignación XML externo.

La forma más sencilla sería desacoplar las entidades del campo de datos: cargar la entidad necesaria, desacoplarla del DataContext, usarla como quieras, luego usar Attach () para vincularla con un DataContext para guardar.

Lamentablemente, LINQ no tiene un método para separar las entidades de un contexto de datos, pero puedes clonarlas, eso funciona bien. La forma más simple sería algo como esto:

public static T CloneEntity<T>(T source)
{
  DataContractSerializer dcs = new DataContractSerializer(typeof(T));
  using (Stream stream = new MemoryStream())
  {
    dcs.WriteObject(stream, source);
    stream.Seek(0, SeekOrigin.Begin);
    return (T)dcs.ReadObject(stream);
  }
}

Hice algo similar con WCF

1 En su DBML, configure su modo de serialización en Unidireccional

2 Establezca TODAS las columnas de sus tablas en UpdateCheck = false

3 Escriba su servicio como el siguiente:

   public class Service1 : IService1
    {
        public Company GetCompany(int companyId)
        {
            using (DataClasses1DataContext dc = new DataClasses1DataContext())
            {
                return (from c in dc.Companies where c.CompanyId == companyId select c).Single();
            }
        }

    public void SaveCompany(Company company)
    {
        using (DataClasses1DataContext dc = new DataClasses1DataContext())
        {
            dc.Companies.Attach(company, true);
            dc.SubmitChanges();
        }
    }

    public void InsertCompany(Company company)
    {
        using (DataClasses1DataContext dc = new DataClasses1DataContext())
        {
            dc.Companies.InsertOnSubmit(company);
            dc.SubmitChanges();
        }
    }
}

4 Agregar una referencia de servicio

No es exactamente el mismo escenario, pero estoy trabajando para crear una herramienta personalizada que, basada en un archivo XML, genere un modelo OO. Mi enfoque es usar LINQ to SQL detrás de escena y, como estoy generando el código automáticamente, sería fácil usar otro mecanismo para, digamos, la fuente de datos de MySQL. Como LINQ to SQL no lo admite, tendrá que escribir el código de acceso a los datos manualmente, pero el código del cliente que usará el modelo OO cambiará de cualquier manera.

¿Sus clases de Entidad podrían implementar interfaces de IProduct, IUser, IOrder, etc. que se declararían en sus " Interfaces " ¿montaje? De esta manera, la interfaz de IRepository solo hace referencia a las interfaces de objetos de negocios (es decir, devuelve colecciones de IProduct, etc.) y las " Interfaces " el ensamblaje se desacopla de los otros ensamblados específicos de la implementación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top