Question

J'essaie de construire des requêtes de jointure extérieure gauche avec des expressions Linq, mais maintenant j'ai vraiment frappé le mur.

Ce que je veux accomplir, c'est la requête suivante:

var q =
    from i in ProcessInstances

    join dof1 in Queries.DataObjectFieldsQuery(this) on new { instanceId = i.ObjectID, dataId = fields[0,0], fieldId = fields[0,1] } equals new { instanceId = dof1.ProcessInstanceObjectID, dataId = dof1.DataID, fieldId = dof1.FieldID } into dofs1
    from dof1 in dofs1.DefaultIfEmpty()

    join dof2 in Queries.DataObjectFieldsQuery(this) on new { instanceId = i.ObjectID, dataId = fields[1,0], fieldId = fields[1,1] } equals new { instanceId = dof2.ProcessInstanceObjectID, dataId = dof2.DataID, fieldId = dof2.FieldID } into dofs2
    from dof2 in dofs2.DefaultIfEmpty()

    join dof3 in Queries.DataObjectFieldsQuery(this) on new { instanceId = i.ObjectID, dataId = fields[2,0], fieldId = fields[2,1] } equals new { instanceId = dof3.ProcessInstanceObjectID, dataId = dof3.DataID, fieldId = dof3.FieldID } into dofs3
    from dof3 in dofs3.DefaultIfEmpty()

    select new WorkitemListModel
    {
        InstanceId = i.ObjectID,
        FormFieldValue1 = dof1.FieldValue,
        FormFieldValue2 = dof2.FieldValue,
        FormFieldValue3 = dof3.FieldValue,
    };

J'ai défini les classes suivantes:

public class WorkitemListModel
{
    public string InstanceId { get; set; }
    public string FormFieldValue1 { get; set; }
    public string FormFieldValue2 { get; set; }
    public string FormFieldValue3 { get; set; }
}

public class DataObjectField
{
    public string ProcessInstanceObjectID { get; set; }
    public string DataID { get; set; }
    public string FieldID { get; set; }
    public string FieldValue { get; set; }
}

public class ModelWithFields<TModel>
{
    public IEnumerable<DataObjectField> DataObjectFields { get; set; }
    public TModel Model { get; set; }
}

public class OuterKeySelector
{
    public string instanceId { get; set; }
    public string dataId { get; set; }
    public string fieldId { get; set; }
}

J'ai créé l'expression de GroupJoin qui ne donne aucune erreur de compilation. (J'ai laissé de côté le code ici):

var q = dc.Instances;

System.Type modelType = typeof(ModelWithFields<>);
System.Type outerKeyType = typeof(WorkitemListModel);
System.Type resultType = modelType.MakeGenericType(outerKeyType);

//... MemberInitExpression and Expression.Bind

q = q.Provider.CreateQuery(
    Expression.Call(
        typeof(Queryable), 
        "GroupJoin",
        new[] 
        { 
            typeof(WorkitemListModel), 
            typeof(DataObjectField), 
            typeof(OuterKeySelector),
            resultType,
        },
        query.Expression,
        Expression.Constant(Queries.DataObjectFieldsQuery(dc)),
        Expression.Quote(outerLambda),
        Expression.Quote((Expression<Func<DataObjectField,OuterKeySelector>>)(
            (DataObjectField dof) => 
                new OuterKeySelector
                {
                    instanceId = dof.ProcessInstanceObjectID, 
                    dataId = dof.DataID, 
                    fieldId = dof.FieldID
                })),
        Expression.Quote(resultLambda)));


// selectmany expression
// collectionSelector lambda -- temp.DataObjectFields.DefaultIfEmpty()
ParameterExpression collectionParameter = Expression.Parameter(resultType, "temp");

// This throw an exception
MethodCallExpression collectionCallExpression = 
    Expression.Call(
        typeof(Queryable),
        "DefaultIfEmpty",
        new System.Type[]
        {
            typeof(IQueryable<>).MakeGenericType(typeof(DataObjectField))
        },
        Expression.Property(collectionParameter, resultType.GetProperty("DataObjectFields")));

Mais dans la méthode SelectMany, j'essaie d'ajouter defaultIfEmpty, mais j'obtiens une exception disant:

Aucune méthode générique «defaultIFEMPTY» sur Type «System.Linq.Queryable» est compatible avec les arguments et arguments de type fourni. Aucun argument de type ne doit être fourni si la méthode n'est pas générale.

J'ai essayé les TypeParams commutés de iQueryable à Ienumerable et l'événement a essayé d'appeler énumérable.DefaulFexpty sans chance. Peut-être que quelque chose ne va pas avec l'Expression Property?

Était-ce utile?

La solution 2

J'avais défini le paramètre de type incorrect de la méthode Expression.Call. Ce n'était pas IQueryable<DataObjectField> mais, seulement DataObjectField. C'est ce qui l'a corrigé.

MethodCallExpression collectionCallExpression = 
    Expression.Call(
        typeof(Enumerable),
        "DefaultIfEmpty",
        new System.Type[]
        {
            typeof(DataObjectField)
        },
        Expression.Property(collectionParameter, newResultType.GetProperty("DataObjectFields"))

Autres conseils

Je préfère une surcharge différente pour l'expression. MethodInfo, voici un exemple simple que je travaille.

Expression constant = Expression.Constant(new string[] { "a", "b" });
MethodInfo methodInfo = typeof(Enumerable).GetMethods().FirstOrDefault(c => (c as MethodInfo).Name == "DefaultIfEmpty");
methodInfo = methodInfo.MakeGenericMethod(typeof(string));

MethodCallExpression methodExpression = Expression.Call(methodInfo, constant);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top