views:

853

answers:

3

How do you specify the Func signature for anonymous objects?

new Func<DataSet, **IEnumerable<int>>**

I am having a trouble with return type where I have specified as IEnumerable<> in the Func declaration

Error I am getting from the expression is

Cannot convert expression type 'System.Collections.Generic.IEnumerable<{ParentNodeId:int}>' to returnt ype 'System.Collections.Generic.IEnumerable'

How can I specify IEnumerable<{ParentNodeId:int}> in func?

    public int GetCachedRootNodeId(IList<int> fromNodeIds, int forNodeId)
    {
        var result = forNodeId;

        const string spName = "spFetchAllParentNodeIDs";
        using (var ds = _df.ExecuteDatasetParamArray(_ConnectionString, spName, forNodeId))
        {
            if (DataAccessUtil.DataSetIsEmpty(ds)) return result;

            var orderByLevelDesc = new Func<DataSet, IEnumerable<int>>(resultSet =>
                from DataRow row in DataAccessUtil.GetFirstTableRows(resultSet) 
                orderby DataAccessUtil.GetInt32(row, "Level") descending 
                select new { ParentNodeId = DataAccessUtil.GetInt32(row, "ParentNodeID") });

            //// Get top-most parent's node ID first (higher the level, the more top-most the parent is)
            //var query = from DataRow row in DataAccessUtil.GetFirstTableRows(ds)
            //            orderby DataAccessUtil.GetInt32(row, "Level") descending
            //            select new { ParentNodeId = DataAccessUtil.GetInt32(row, "ParentNodeID") };
            //foreach (var nodeInfo in query)
            foreach (var nodeInfo in orderByLevelDesc(ds))
            {
                if (fromNodeIds.Contains(nodeInfo.ParentNodeId))
                    return nodeInfo.ParentNodeId;
            }
        }

        return result;
    }

By the way, I could have used commented code "query" and be done with it. But wanted to be more expressive and try something new after looking at this answer Why doesn't C# have lexically nested functions?

EDIT: Final Result

    public int GetCachedRootNodeId(IList<int> fromNodeIds, int forNodeId)
    {
        var result = forNodeId;

        const string spName = "spFetchAllParentNodeIDs";
        using (var ds = _df.ExecuteDatasetParamArray(_ConnectionString, spName, forNodeId))
        {
            if (DataAccessUtil.DataSetIsEmpty(ds)) return result;

            var orderParentNodeIDByLevelDesc = new Func<DataSet, IEnumerable<int>>(resultSet =>
                from DataRow row in DataAccessUtil.GetFirstTableRows(resultSet) 
                orderby DataAccessUtil.GetInt32(row, "Level") descending
                select DataAccessUtil.GetInt32(row, "ParentNodeID"));

            foreach (var parentNodeId in orderParentNodeIDByLevelDesc(ds))
            {
                if (fromNodeIds.Contains(parentNodeId))
                    return parentNodeId;
            }
        }

        return result;
    }
+2  A: 

There is no way to specify anonymous types in declarations. You have to make a named class for it.

Jon Skeet has a related blog entry on this issue.

Mehrdad Afshari
I do indeed have a blog entry, but I don't think we need to despair this time :)
Jon Skeet
Actually I enjoyed it when I read it. I was just looking for a place to share ;)
Mehrdad Afshari
Feel free then :)
Jon Skeet
+3  A: 

Here's an idea - call a static method and let type inference do it:

public static Func<T, TResult> FuncOf(Func<T, TResult> func)
{
    return func;
}

then just call it:

var orderByLevelDesc = FuncOf(resultSet =>
    from DataRow row in DataAccessUtil.GetFirstTableRows(resultSet) 
    orderby DataAccessUtil.GetInt32(row, "Level") descending 
    select new { ParentNodeId = DataAccessUtil.GetInt32(row, "ParentNodeID")
    });

Basically you only need the "Func" part to tell the compiler that it needs to convert the lambda expression into a delegate instead of an expression, and the type of the delegate. It should be able to work out the type given the signature of FuncOf.

Worth a try, anyway.

I should say, by the way, that I find your commented out version easier to understand. Why introduce an extra function? Or was your plan to not have that as a local variable (which would make more sense, but then you couldn't use var)? Or perhaps a local variable declared outside the loop instead of inside?

Jon Skeet
This worked like a charm. I just had to change the signature of FuncOf to "public static Func<T, TResult> FuncOf<T, TResult>(Func<T, TResult> func)". And also when calling it, "FuncOf(resultSet" to "FuncOf((DataSet resultSet" since "GetFirstTableRows" method expects a DataSet and it wasn't inferred.
Sung Meister
Why make this so complicated? See my answer above.
TheSoftwareJedi
@TheSoftwareJedi: I was trying to address the question behind the current situation rather than just solving this particular case (which I didn't look into in much detail, I have to admit). Both useful answers in different ways :)
Jon Skeet
Your answer will come in very handy very soon when I have to return multiple properties. Thank you again.
Sung Meister
+2  A: 

In this case all you really want is as int... so just change your select to:

select DataAccessUtil.GetInt32(row, "ParentNodeID"));

And change your foreach to:

foreach (var nodeInfo in orderByLevelDesc(ds))
{
    if (fromNodeIds.Contains(nodeInfo))
        return nodeInfo;
}
TheSoftwareJedi
Ha!, this was my original intention. Thank you.
Sung Meister
Jon Skeet's answer is great if someone really needs a multiproperty anony class. But in this case, it's just an int!
TheSoftwareJedi
I would have picked Jon Skeet's response as answer was I returning multiple properties as you mentioned.
Sung Meister