Patrón de repositorio en óxido -- rust campo con repository camp codereview Relacionados El problema

Repository pattern in Rust


19
vote

problema

Español

Estoy buscando agregar una fina capa de abstracción para la interacción de la base de datos en mi solicitud. Realmente no estoy buscando un constructor de consultas Full Blown ORM o Avanzado.

Estoy familiarizado con diesel , pero sus limitaciones actuales (para mi caso de uso específico) requieren casi tanto esfuerzo para Solución al usar el uso de adaptadores de bases de datos de Rust.

Lo que tengo

MI INTERNA DE RESPOSITORIO PARECE:

  skew.CalibrationDate.Date == (from skew2 in db.Skew                                where skew2.CalibrationDate.Date <= date.Date                                select skew2.CalibrationDate).Max() 2  

que puedo implementar como:

  skew.CalibrationDate.Date == (from skew2 in db.Skew                                where skew2.CalibrationDate.Date <= date.Date                                select skew2.CalibrationDate).Max() 3  

y que puedo usar como:

  skew.CalibrationDate.Date == (from skew2 in db.Skew                                where skew2.CalibrationDate.Date <= date.Date                                select skew2.CalibrationDate).Max() 4  

Lo que me gusta de esto

  • Esta es la placa de caldera, pero una cantidad manejable, y mueve la lógica de SQL / Base de datos fuera de mis modelos de datos
  • Mantiene la consulta SQL y el Topalue / Devalue Data juntos. La mayoría de los adaptadores de base de datos de Rust proporcionan formas de buscar valores de fila mediante el nombre de la columna (por ejemplo, skew.CalibrationDate.Date == (from skew2 in db.Skew where skew2.CalibrationDate.Date <= date.Date select skew2.CalibrationDate).Max() 5 , pero estos suelen venir con una penalización de búsqueda (es ciertamente muy pequeña). Esto me permite usar el orden de selección sin Demasiado peligro de mezclar campos.

Preguntas

Esto parece una abstracción útil, pero no he visto nada como en mi exposición (ciertamente limitada) al código de óxido. ¿Falta un poco de defecto importante? ¿O es solo que las reglas de los huérfanos hacen que el empaque sea esto como una manera menos potente / útil?

¿Hay mejores formas de abstraer el patrón del repositorio? Me sentí como si estuviera inventando las firmas a medida que avanzaba.

Original en ingles

I'm looking to add a thin layer of abstraction for database interaction in my application. I'm not really looking for a full blown ORM or advanced query builder.

I am familiar with Diesel, but its current limitations (for my specific use case) require almost as much effort to workaround as using Rust's database adapters directly.

What I have

My repository interface looks like:

pub trait Repository {                  // I know I could probably add the `Sized` constraint to the trait directly     // but wasn't sure if I wanted to do that yet     fn find<T, Find>(conn: &mut T, id: u64)          -> ::err::Result<Find> where T: ::db::GenConn, Find: Findable<Self>, Self: Sized {         let tup: Find::RowType = conn.prep_exec(Find::SQL, (id,))?             .flat_map(|x| x)             .flat_map(|x| ::mysql::from_row_opt(x))             .next()             .ok_or("No results")?;         Ok(Find::from_row(tup))     }      fn insert<T, Ins>(conn: &mut T, obj: Ins)          -> ::err::Result<u64> where T: ::db::GenConn, Ins: Insertable<Self>, Self: Sized {         let res = conn.prep_exec(Ins::SQL, obj.to_params())?;         let id = res.last_insert_id();         Ok(id)     }      fn update<T, Up>(conn: &mut T, obj: Up)          -> ::err::Result<()> where T: ::db::GenConn, Up: Updatable<Self>, Self: Sized {         let res = conn.prep_exec(Up::SQL, obj.to_params())?;         Ok(())     }      fn delete<T, Del>(conn: &mut T, obj: Del)          -> ::err::Result<()> where T: ::db::GenConn, Del::Deletable<Self>, Self: Sized {         let res = conn.prep_exec(Del::SQL, obj.to_params())?;         Ok(())     }     }  pub trait Insertable<R: Repository> {     const SQL: &'static str;     fn to_params(&self) -> ::mysql::Params; }  pub trait Updatable<R: Repository> {     const SQL: &'static str;     fn to_params(&self) -> ::mysql::Params; }  pub trait Findable<R: Repository> {     const SQL: &'static str;     type RowType: ::mysql::value::FromRow;     fn from_row(row: Self::RowType) -> Self; }  pub trait Deletable<R: Repository> {     const SQL: &'static str;     fn to_params(&self) -> ::mysql::Params; } 

Which I can implement like:

pub enum ClipRepository {}  impl Repository for ClipRepository {}  impl<'a> Insertable<ClipRepository> for &'a NewClip {     const SQL: &'static str =          "INSERT INTO clips(slug, data, interview_id) VALUES (?, ?, ?)";     fn to_params(&self) -> ::mysql::Params {         (&self.slug, &self.data, &self.interview_id).into()     } }  impl<'a> Updatable<ClipRepository> for &'a Clip {     const SQL: &'static str =          "UPDATE clips SET slug = ?, data = ?, interview_id = ? WHERE id = ?";     fn to_params(&self) -> ::mysql::Params {         (&self.slug, &self.data, &self.interview_id, &self.id).into()     } }  impl Findable<ClipRepository> for Clip {     const SQL: &'static str =          "SELECT id, slug, data, interview_id FROM clips WHERE id = ? LIMIT 1";     type RowType = (u64, String, clips::Data, u64);     fn from_row(tup: Self::RowType) -> Self {         Clip { id: tup.0, slug: tup.1, data: tup.2, interview_id: tup.3 }     } }  impl Deletable<ClipRepository> for Clip {     const SQL: &'static str =          "DELETE FROM clips WHERE id = ?";     fn to_params(&self) -> ::mysql::Params {         (&self.id,).into()     } } 

And which I can use like:

let mut conn = ::db::DB.get()?; let clip: Clip = Repository::find(&mut *conn, id)?; 

What I like about this

  • This is boilerplate, but a manageable amount, and it moves the SQL/database logic out of my data models
  • It keeps the query SQL and the ToValue/FromValue data together. Most of the Rust database adapters provide ways to look up row values by column name (e.g. row.get("id"), but these typically come with a lookup penalty (admittedly very small). This allows me to use the select order without too much danger of mixing up fields.

Questions

This seems like a useful abstraction, but I haven't seen anything like it in my (certainly limited) exposure to Rust code. Am I missing some major flaw? Or is it just that orphan rules make packaging this as a crate way less powerful/useful?

Are there better ways to abstract the repository pattern? I kind of felt like I was making up the signatures as I went along.

     
   
   

Lista de respuestas


Relacionados problema

27  Unidad reutilizable de la interfaz de trabajo / fábrica  ( Reusable unit of work interface factory ) 
Dado mi IUnitOfWork Interfaz using System; public interface IUnitOfWork : IDisposable { void Commit(); } Luego creo una interfaz de fábrica abst...

8  Construyendo repositorios y unidades de trabajo multi-fuente  ( Building multi source repositories and units of work ) 
Estoy trabajando en una tienda donde vamos a múltiples proveedores diferentes para compartir datos. También estoy encargado de "traer la base de código hasta ...

5  Repositorio genérico para aplicaciones web  ( Generic repository for web apps ) 
Estaba desarrollando una aplicación web usando entity Framework 6 y MVC 5. Para la capa de acceso a datos, alivié el trabajo y escribí un repositorio genérico...

7  Implementando patrón de repositorio y DAL con procedimientos almacenados  ( Implementing repository pattern and dal with stored procedures ) 
Uso de .NET 3.5, implementación del patrón de repositorio junto con Enterprise Library 5 y procedimientos almacenados. No usó EF, los procedimientos almacenad...

3  Unidad de trabajo MVC4 con marco de entidades  ( Mvc4 unit of work with entity framework ) 
Estoy diseñando una nueva aplicación ASP.NET MVC4. Sé que la clase de contexto generada por EF es unidad de trabajo y DBSET es la clase de repositorio. Solo e...

6  API web utilizando repositorio / unitofwork  ( Web api using repository unitofwork ) 
Estoy buscando comentarios en un repositorio que configuré en función de una publicación de blog, leí aquí . Recientemente he tenido más tiempo en .NET en ...

2  Entregando múltiples flujos de trabajo limpiamente en la aplicación LOB  ( Handing multiple workflows cleanly in lob application ) 
Fondo Estoy a punto de construir una aplicación LOB que se asemeja a una solución de comercio electrónico (sin pagar y estar abierto al público). Se utili...

2  Patrón irepositivo - Principio de segregación de la interfaz  ( Irepository pattern interface segregation principle ) 
Tengo una clase 9988776655544336 que uso mucho. Pero noté que para muchos de mis repositorios no implemento la mayoría de los métodos. Además, generalmente ...

4  Guardar un orden de venta - ¿Demasiados repositorios?  ( Saving a sales order too many repositories ) 
Me pregunto si lo he exagerado con los repositorios en el siguiente código, que es ahorrar un pedido de ventas. Entiendo que el propósito de un repositorio ...

1  Uso de la unidad de trabajo DAPPER en la arquitectura de 3 niveles  ( Using dapper unit of work in 3 tier architecture ) 
El Unit of Work : public class UnitOfWork : IUnitOfWork { private IDbConnection _connection; private IDbTransaction _transaction; private bo...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos