-
-
Save claudiosanchez/a663c5625d98df22d49c to your computer and use it in GitHub Desktop.
... | |
// _repository is a GenericRepository<Villa>. | |
var villas = _repository | |
.Get( | |
q => q.Select(s => new VillaDto { | |
Id = s.PodId, | |
Name = s.Name, | |
Price=s.Price, | |
Location = s.Location }), | |
f => f.Location == "Punta Cana", | |
o => o.OrderBy(i => i.Price) | |
); | |
... | |
public IEnumerable<TResult> SuperGet<TResult>( | |
Func<IQueryable<TEntity>, IQueryable<TResult>> transform, | |
Expression<Func<TEntity, bool>> filter = null, | |
Func<IQueryable<TResult>, IOrderedQueryable<TResult>> orderBy = null) | |
{ | |
// _dbSet is DbSet<TEntity> property of a DbContext | |
var query = (filter == null) ? _dbSet : _dbSet.Where(filter); | |
var notSortedResults = transform(query); | |
return orderBy == null ? (IEnumerable<TResult>) notSortedResults : orderBy(notSortedResults).ToList(); | |
} |
Le hice una pequeña corrección (obtenia los datos de la base de datos antes de paginar) y le agregue los comentarios en inglés.
Ahí va:
//Apply the filter if there is any
var query = filter == null ? this._dbSet : this._dbSet.Where(filter);
//Transform the query to search exactly the data that we want
var notSortedResults = transform(query);
//Apply the sorting.
//NOTE: Don't cast to an Enumerable or List, this will fetch all the data before paging.
var sortedResults = orderby == null ? notSortedResults : orderby(notSortedResults);
//Check if the data is going to be paged
if (pageSize.HasValue)
{
//Page the data
var excludedRows = (currentPage - 1) * pageSize.Value;
sortedResults = sortedResults.Skip(excludedRows).Take(pageSize.Value);
}
//Fetch the data from the Database
return sortedResults.ToList();
Aqui les dejo una implementacion generica para llamar los SP del repositorio:
public virtual IEnumerable<TResult> ExecuteStoredProc<TResult>(Func<IEnumerable<TEntity>, IEnumerable<TResult>> transform, object[] parameters) where TResult : class
{
Type contextType = typeof(MyContext); /*Class of your DbContext goes there*/
string procedureName = typeof(TEntity).Name.Replace("_Result", "");
try
{
var procedureResult = contextType.GetMethod(procedureName).Invoke(_context, parameters) as IEnumerable<TEntity>;
if (procedureResult == null) return null;
var result = transform(procedureResult);
return result.ToList();
}
catch (Exception ex)
{
//TODO: Implement your own error handling
}
return null;
}
Juan Manuel, no he utilizado muchos SP desde EF asi que tal vez me equivoque en lo siguiente.
Pq se necesita el Func transform
?, en el super get era necesario pq asi se filtraban los resultados, pero como esto es un SP, no usariamos LINQ para filtrar, sino que los filtros estarian dentro del SP y serian aplicados de acuerdo a los parametros.
Y creo que no se necesitaria usar Reflexion.. habia un metodo ExecuteFunction
en la version vieja del context ObjectContext
con la que uno llamaba procedures y funciones.. no se realmente pq la quitaron en el nuevo DbContext
.. pero uno puede aun utilizarla haciendo unos amarres..
El codigo se veria asi entonces.. tuve que cambiar el tipo de dato del parametro "parameters" para poder utilizarlo en el ExecuteFunction
..
public virtual IEnumerable<TResult> ExecuteStoredProc<TResult>(string procedureName, ObjectParameter[] parameters) where TResult : class
{
try
{
var procedureResult = ((IObjectContextAdapter)_context).ObjectContext.ExecuteFunction<TResult>(procedureName, parameters);
if (procedureResult == null) return null;
return procedureResult.ToList();
}
catch (Exception ex)
{
//TODO: Implement your own error handling
}
return null;
}
Wilson, actualmente trabajo para una compania que distribuye productos de telefonicas y las cosas cambian muchas veces. Solo estoy yo en la parte Web y hay muchas cosas que la hacen con procedures porque el tiempo no me permite hacerlas. El factor comunicacion aqui es una basura y solo por eso los usamos.
Mira como llamo uno de los SP bajo el metodo que coloque mas arriba (omito el nombre real del SP y las propierdades :P):
//storeId es STRING, dateFrom es DateTime, dateTo es DateTime
using (UnitOfWork uow = new UnitOfWork())
{
//Sps_ProcedimientoX_Result
var result = uow.
GetProcedureRepository<ProcedureX_Result>().
ExecuteStoredProc<ProcedureXDTO>(x => x.Select(r => new ProcedureXDTO
{
Prop1 = r.Prop1,
Prop2 = r.Prop2,
Prop3 = r.Prop3,
...
}), new object[] { storeId, dateFrom, dateTo });
return result;
}
En la BD se ejecutaria lo siguiente:
EXEC ProcedureX @iStoreId = 'storeId', @iTxnDateFrom = 'dateFrom', @iTxnDateTo = 'dateTo'
No tengo que ocupar mi tiempo instanciando los object parameters :)
Oh ok, ya entiendo.. el transform es el mapper.. como con el ExecuteFunction
se mapean las propiedades automaticamente de acuerdo al nombre.. no pense era necesario el transform.. pero eso de objectParameters si es un lio..^_^
Reflexion for the win! ^_^
Javier, haz el pull request para aceptar tu aporte en el repo.
No he realizado el Pull Request con el cambio porque se me queda en el trabajo :P Manana lo hago.
Tengo varias preguntas para ustedes dos. Si yo tengo 5 condiciones en el filtro pero son opcionales, como le hago para que en el Query que se ejecuta en la base de datos, las que son opcionales ni siquiera aparezcan en el WHERE? Pienso que es lo que hace falta ahora mismo.
A que te refieres cuando dices que tus condiciones son opcionales?
Puedes proveer un ejemplo concreto?
Esta nitido este metodo.. aunque seria util agregarle paging, asi en caso de que la data se este cargando en un grid o algo asi, solo se extraen los records de la pagina actual..
algo asi: