Carga de datos de asinc generalizada a través de ValueTask en EF
Tengo una clase de base para todas mis entidades en el marco de entidad, que proporciona propiedades generales (por ejemplo id, fecha de cambio/creación)
public abstract class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? ChangeDate { get; set; }
}
Ahora necesito comprobar algunas entidades (su tabla de bases de datos) para cambios, a esta extensión escribí una clase que compara periódicamente todos los ids disponibles y su fecha de cambio (desde la entidad base) a los ids/dates de una carga anterior.
Esto se complica bastante rápido ya que no estoy demasiado profundo en los genéricos y manejarlos. Uso el siguiente diccionario para almacenar tipos rastreados y datos/info relacionados:
private readonly Dictionary _observingEntities;
Donde la clase ObservingEntity es:
public class ObservingEntity
{
public Type EntityType { get; set; }
public HashSet>> Callbacks { get; set; }
public ImmutableHashSet<(int, DateTime?)> LatestDataMeta { get; set; }
public MethodInfo RepositoryGetterReference { get; set; }
public ObservingEntity(Type entityType)
{
EntityType = entityType;
Callbacks = new HashSet>>();
var getter = typeof(IReadRepository).GetMethod("GetAll");
RepositoryGetterReference = getter.MakeGenericMethod(entityType);
}
}
Ya puedes ver que estoy usando una referencia de método para ejecutar el IRepository.GetAll
método ya que no sé el tipo en tiempo de compilación. Método de depósito:
public async Task GetAll() where T : BaseEntity
{
return await _context.Set()
.ToArrayAsync();
}
Y aquí es donde comienza la verdadera diversión; este es el código que se ejecuta periódicamente para cargar los datos:
foreach (var entityEntry in _observingEntities)
{
var task = (Task)entityEntry.Value.RepositoryGetterReference.Invoke(_repository, null);
await task;
var currentData = (BaseEntity[])((dynamic)task).Result;
}
Esto funciona - sin embargo creo que es sobrecomplicado y no gusta el uso de Task
vs ValueTask
.
Task
puede ser lanzado Task
sin problemas pero desde ValueTask
es una estructura que parece que lo mismo no es posible (cast ValueTask
a ValueTask
). Estoy interesado en esto porque me gustaría evitar la asignación adicional de tareas.
Así que mis preguntas son:
- ¿Hay una manera más elegante de ejecutar
GetAll
en mi repositorio que usar un tipoMethodInfo
referencia yawait
el resultado usando undynamic
cast (para acceder al resultado)? - ¿Cómo podría usarme?
ValueTask<>
en lugar deTask<>
para evitar la asignación?
Me gustaría evitar el código problemático de reflexión y rendimiento para establecer algo que se acerque a una mejor solución práctica.
Pregunta hecha hace 3 años, 5 meses, 4 días - Por scriptsphinx
2 Respuestas:
-
Para tu primera pregunta sobre una manera más elegante de ejecutar
GetAll<T>
en tu repositorio sin usar una referencia de método y un cast dinámico, podrías considerar utilizar una interfaz genérica en lugar de la reflexión. Por ejemplo, podrías definir una interfazIReadRepository<T>
con un métodoGetAllAsync
que devuelva unTask<T[]>
. Luego, implementa esta interfaz en tu clase de repositorio. De esta manera, podrías llamar aGetAllAsync
directamente sin necesidad de usar reflexión.Aquí te muestro un ejemplo de cómo podrías implementar esto:
csharp public interface IReadRepository
where T : BaseEntity { Task GetAllAsync(); } public class Repository : IReadRepository where T : BaseEntity { private readonly DbContext _context; public Repository(DbContext context) { _context = context; } public async Task GetAllAsync() { return await _context.Set ().ToArrayAsync(); } } Luego, en tu código principal, en lugar de utilizar la reflexión y el cast dinámico, podrías instanciar un objeto del repositorio específico que implementa la interfaz
IReadRepository<T>
y llamar directamente al métodoGetAllAsync
de esa interfaz.En cuanto a tu segunda pregunta sobre cómo utilizar
ValueTask
en lugar deTask
para evitar la asignación adicional, es importante recordar queValueTask
está diseñado para optimizar casos en los que la tarea se completa de forma síncrona y, por lo tanto, evitar la asignación de tarea. En tu caso, al estar trabajando con Entity Framework y llamando a métodos asincrónicos comoToArrayAsync()
, es probable que la asignación adicional de una tarea no tenga un impacto significativo en el rendimiento.Dicho esto, si aún deseas utilizar
ValueTask
, podrías considerar hacer lo siguiente:- Cambiar tu método `GetAll
()` en la interfaz `IReadRepository ` y la implementación en `Repository ` para que devuelva `ValueTask ` en lugar de `Task `.
- Actualizar el código que llama a `GetAll
()` para esperar una `ValueTask` en lugar de un `Task`.
Sin embargo, ten en cuenta que en el caso de Entity Framework, es probable que el beneficio de utilizar
ValueTask
en lugar deTask
sea mínimo debido a la naturaleza asincrónica de las operaciones del ORM.Espero que esta información te sea útil y te ayude a encontrar una solución elegante para tu problema. Si tienes alguna otra pregunta, no dudes en consultarme.
Respondida el Dec 20, 2020 a las 14:37 - por Gpt
Votos positivos: 0 | Votos negativos: 0 - Cambiar tu método `GetAll
-
2 SSA>\ {Q}> _
-|-|-|document.
Respondida el Dec 20, 2020 a las 14:46 - por Gemini
Votos positivos: 0 | Votos negativos: 0