致电任务<> LINQ内部的方法和返回数据

时间:2014-04-18 10:45:33

标签: c# xml linq windows-phone-8 task-parallel-library

我有一个问题,我在LINQ语句中调用Task<>方法并尝试从Task<>方法返回数据(图像)。我无法使用Task<>方法返回数据(图片),我收到以下错误:

  

无法隐式转换类型System.Threading.Tasks.Task&#39;   到&#39; System.Uri&#39;。

这是带有LINQ语句的DownloadStringCompleted方法,其中我调用了GetTest Task&lt;&gt;方法:

private async void GetGamesListRequestCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error == null)
    {
    var feedXml = XDocument.Parse(e.Result);

    var gameData = feedXml.Root.Descendants("Game").Select(x => new GetGamesList
    {
        ID = (int)x.Element("id"),
        GameTitle = (string)x.Element("GameTitle"),
        ReleaseDate = (string)x.Element("ReleaseDate"),
        Platform = (string)x.Element("Platform"),
        Front = GetTest((int)x.Element("id")), // THE METHOD WITH PROBLEM.
    })
    .ToList();

    foreach (var item in gameData)
    {
        GetGamesListItems.Add(item);
    }
}
}

任务&lt;&gt;尝试返回数据(图像)时遇到问题的GetTest方法:

public Task<string> GetTest(int id)
{
    var tcs = new TaskCompletionSource<string>();
    var client = new WebClient();
    client.DownloadStringCompleted += (s, e) =>
    {
        if (e.Error == null)
        {
            var feedXml = XDocument.Parse(e.Result);

            var gameData = feedXml.Root.Descendants("Images").Select(x => new GetArt
            {
                BoxArtFrontThumb = new Uri(GetBoxArtFront(x)),
            })
            .ToList();

            foreach (var item in gameData) GetArtItems.Add(item);
            foreach (var i in GetArtItems)
            {
                tcs.SetResult("http://thegamesdb.net/banners/" + i.BoxArtFrontThumb.ToString());
            }
        }
        else
        {
            tcs.SetException(e.Error);
        }
    };

    client.DownloadStringAsync(new Uri("http://thegamesdb.net/api/GetArt.php?id=" + id.ToString()));
    return tcs.Task;
}

我存储图像的ObservableCollection:     private ObservableCollection _GetArtItems = new ObservableCollection();

public ObservableCollection<GetArt> GetArtItems
{
    get
    {
        return this._GetArtItems;
    }
}

我从XML获取图像:

private static string GetBoxArtFront(XElement gameNode)
{
    return "http://thegamesdb.net/banners/" + (string)gameNode.Descendants("boxart")
        .FirstOrDefault(b => (string)b.Attribute("side") == "front");
}

这是我用来存储数据(图像)的类:

public class GetArt
{
    public Uri BoxArtFrontThumb { get; set; }
}

这是我的LongListSelector我想在下面显示数据(图像):

<phone:LongListSelector Name="llsGameList" Background="#242424" ItemsSource="{Binding}" Tap="llsGameList_Tap" Margin="0,90,0,0">
                    <phone:LongListSelector.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <toolkit:ContextMenuService.ContextMenu>
                                    <toolkit:ContextMenu Name="ContextMenu">
                                        <toolkit:MenuItem 
                            Name="addToFavorite"  
                            Header="Add to favorite" 
                            Click="addToFavorite_Click"/>
                                    </toolkit:ContextMenu>
                                </toolkit:ContextMenuService.ContextMenu>
                                <StackPanel>
                                    <Border Background="{StaticResource PhoneAccentBrush}" 
        Padding="{StaticResource PhoneTouchTargetOverhang}"
        Margin="{StaticResource PhoneTouchTargetOverhang}">
                                        <TextBlock Name="tblGameTitle" Style="{StaticResource PhoneTextGroupHeaderStyle}" ManipulationStarted="tblGameTitle_ManipulationStarted" ManipulationCompleted="tblGameTitle_ManipulationCompleted">
                                        <Run Text="{Binding GameTitle}"></Run>
                                        </TextBlock>
                                    </Border>
                                    <Image Source="{Binding Front}" Height="200" Width="200"></Image> // HERE IM BINDING TO MY FRONT PROPERTY TO SHOW THE IMAGES
                                    <TextBlock TextWrapping="Wrap" Foreground="YellowGreen" Style="{StaticResource PhoneTextNormalStyle}" Padding="{StaticResource PhoneTouchTargetOverhang}" 
        FontSize="{StaticResource PhoneFontSizeNormal}">
                                        <Run Text="Platform: "></Run>
                                        <Run Text="{Binding Platform}"></Run>
                                    </TextBlock>
                                    <TextBlock Foreground="YellowGreen" Style="{StaticResource PhoneTextNormalStyle}" Padding="{StaticResource PhoneTouchTargetOverhang}" 
        FontSize="{StaticResource PhoneFontSizeNormal}">
                                        <Run Text="Release Date: "></Run>
                                        <Run Text="{Binding ReleaseDate}"></Run>
                                    </TextBlock>
                                </StackPanel>
                            </Grid>
                        </DataTemplate>
                    </phone:LongListSelector.ItemTemplate>
                </phone:LongListSelector>

这是我的DataContext,它获取gd.GetGamesListItems,其中包含我想在LongListSelector中显示的图像:

public MainPage()
    {
        InitializeComponent();
        llsGameList.DataContext = gd.GetGamesListItems;
    }

我希望有人可以帮助我:)。感谢。

3 个答案:

答案 0 :(得分:3)

每当你有Task<T>时,你需要花一点时间思考你想要(异步)等待结果的方式。请注意,Task<T>代表 future 结果,而非当前结果。

根据@Sriram的答案,有一个选项,它将(异步)一次一个地等待结果,并在它们到达时将它们添加到列表中。另一种选择是做这样的事情:

var feedXml = XDocument.Parse(e.Result);
var gameDataTasks = feedXml.Root.Descendants("Game").Select(
    async x => new GetGamesList
    {
      ID = (int)x.Element("id"),
      GameTitle = (string)x.Element("GameTitle"),
      ReleaseDate = (string)x.Element("ReleaseDate"),
      Platform = (string)x.Element("Platform"),
      Front = new Uri(await GetTestAsync((int)x.Element("id"))),
    }).ToList();
var gameData = await Task.WhenAll(gameDataTasks);
foreach (var item in gameData)
{
  GetGamesListItems.Add(item);
}

这将启动所有元素的所有GetTestAsync调用,然后(异步)等待它们全部完成(并检索结果)。

其他说明:

  • 我确实将您的方法重命名为GetTestAsync以符合Task-based Asynchronous Pattern
  • 如果您使用较新的HttpClient,我会发现代码更清晰,await内置了对HttpClient的支持。

以下是使用public async Task<string> GetTestAsync(int id) { var client = new HttpClient(); var result = await client.GetStringAsync("http://thegamesdb.net/api/GetArt.php?id=" + id); var feedXml = XDocument.Parse(result); var gameData = feedXml.Root.Descendants("Images").Select(x => new GetArt { BoxArtFrontThumb = new Uri(GetBoxArtFront(x)), }).ToList(); foreach (var item in gameData) GetArtItems.Add(item); return "http://thegamesdb.net/banners/" + gameData.Single().BoxArtFrontThumb.ToString() }

的示例
{{1}}

答案 1 :(得分:1)

要解决此问题,您可以调用Task.Result但是方法将同步执行,等待异步操作。

您必须将调用方法也标记为异步并等待GetTest异步。

var feedXml = XDocument.Parse(e.Result);

List<GetGamesList> gameData = new List<GetGamesList>();
foreach (var item  in feedXml.Root.Descendants("Game"))
{
    var gl = new GetGamesList();
    gl.ID = (int)x.Element("id");
    gl.GameTitle = (string)x.Element("GameTitle");
    gl.ReleaseDate = (string)x.Element("ReleaseDate");
    gl.Platform = (string)x.Element("Platform");
    gl.Front = new Uri(await GetTest((int)x.Element("id")));//Note the await keyword

    gameData.Add(gl);
}
foreach (var item in gameData)
{
    GetGamesListItems.Add(item);
}

答案 2 :(得分:0)

我不确定我认为调用这样的任务是个好主意但是......问题是你没有从任务中得到结果。

您需要:

Front = GetTest().Result

Front = await GetTest()