Estoy usando LinqKit biblioteca que permite combinar expresiones sobre la marcha.¿Existe algún motivo particular por el que el expansor de LinqKit no pueda recoger expresiones de los campos?
Esto es una bendición pura para escribir la capa de acceso a datos de Entity Framewok porque varias expresiones pueden reutilizarse y combinarse opcionalmente, lo que permite un código legible y eficiente.
Considerar siguiente fragmento de código:
private static readonly Expression<Func<Message, int, MessageView>> _selectMessageViewExpr =
(Message msg, int requestingUserId) =>
new MessageView
{
MessageID = msg.ID,
RequestingUserID = requestingUserId,
Body = (msg.RootMessage == null) ? msg.Body : msg.RootMessage.Body,
Title = ((msg.RootMessage == null) ? msg.Title : msg.RootMessage.Title) ?? string.Empty
};
Declaramos una expresión que se proyecta hacia Message
MessageView
(He quitado los detalles para mayor claridad).
Ahora, el código de acceso de datos puede utilizar esta expresión para obtener mensaje individual:
var query = CompiledQueryCache.Instance.GetCompiledQuery(
"GetMessageView",
() => CompiledQuery.Compile(
_getMessagesExpr
.Select(msg => _selectMessageViewExpr.Invoke(msg, userId)) // re-use the expression
.FirstOrDefault((MessageView mv, int id) => mv.MessageID == id)
.Expand()
)
);
Esta es bella porque la misma expresión se puede reutilizar para obtener una lista de mensajes, así:
var query = CompiledQueryCache.Instance.GetCompiledQuery(
"GetMessageViewList",
() => CompiledQuery.Compile(
BuildFolderExpr(folder)
.Select(msg => _selectMessageViewExpr.Invoke(msg, userId))
.OrderBy(mv => mv.DateCreated, SortDirection.Descending)
.Paging()
.Expand()
),
folder
);
Como puede ver, la expresión de proyección se almacena en _selectMessageViewExpr
y se utiliza para crear varias consultas diferentes.
Sin embargo, pasé mucho tiempo rastreando un extraño error donde este código se bloqueó en Expand()
llamada.
El error dice:
Unable to cast object of type
System.Linq.Expressions.FieldExpression
to typeSystem.Linq.Expressions.LambdaExpression
.
Es sólo después de un tiempo me di cuenta de que todo funciona cuando la expresión se hace referencia en una variable local antes de ser llamado Invoke
en:
var selector = _selectMessageViewExpr; // reference the field
var query = CompiledQueryCache.Instance.GetCompiledQuery(
"GetMessageView",
() => CompiledQuery.Compile(
_getMessagesExpr
.Select(msg => selector.Invoke(msg, userId)) // use the variable
.FirstOrDefault((MessageView mv, int id) => mv.MessageID == id)
.Expand()
)
);
Este el código funciona como se espera
Mi pregunta es:
Is there any specific reason why LinqKit doesn't recognize
Invoke
on expressions stored in fields? Is it just an omission by developer, or is there some important reason why expressions need to be stored in local variables first?
Esta pregunta probablemente pueden ser respondidas por mirar el código generado y comprobación de las fuentes LinqKit, sin embargo pensé que tal vez alguien relacionada con el desarrollo LinqKit podría responder a esta pregunta.
Gracias.
¿Encontró una respuesta a esto? –
@Lawrence: no, todavía tengo curiosidad. Actualmente uso la solución descrita. –
Acabo de toparme con esto en mi proyecto. Por lo que puedo decir mirando el origen de LinqKit, parece ser porque ExpressionExpander no está programado para manejar campos de propiedad. No sabe cómo llamar a 'get' ya que su funcionamiento se basa en la reflexión. Estoy tratando de encontrar una solución que no marque este – Charles