使用yield return可以实时返回JSON吗?

7
这是情况。
我正在处理以下内容: - ASP.NET MVC 4 Web API - IIS 7.0 用于托管应用程序 - C#
我有一个Web API,它从内部网络上的远程机器获取性能数据,并测试通过URL调用API。这将返回JSON。但是,在返回JSON之前,必须完成执行。例如,如果我返回10秒的性能监视,则必须等待10秒钟才能显示所有数据值。
我想要实时获取它,因此当它每秒读取一次性能计数器并在JSON中显示它时,它将返回一个值,而不是等待检索到所有内容后再一次列出所有内容。我尝试使用YIELD关键字来实现此目的,但仍未成功。在方法完全完成之前,浏览器不会显示JSON。
以下是存储库方法和协调控制器操作的代码:
来自LogDBRepository.cs:
public IEnumerable<DataValueInfo> LogTimedPerfDataLive(string macName, string categoryName, string counterName,
                                          string instanceName, string logName, long? seconds)
    {
        iModsDBRepository modsDB = new iModsDBRepository();
        List<MachineInfo> theMac = modsDB.GetMachineByName(macName);

        if (theMac.Count == 0)
            yield break;

        else if (instanceName == null)
        {
            if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
                !PerformanceCounterCategory.CounterExists(counterName, categoryName, macName))
            {
                yield break;
            }
        }
        else if (instanceName != null)
        {
            if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
                !PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) ||
                !PerformanceCounterCategory.InstanceExists(instanceName, categoryName, macName))
            {
                yield break;
            }
        }
        else if (logName == null)
        {
            yield break;
        }

        // Check if entered log name is a duplicate for the authenticated user
        List<LogInfo> checkDuplicateLog = this.GetSingleLog(logName);
        if (checkDuplicateLog.Count > 0)
        {
            yield break;
        }

        PerformanceCounterCategory category = new PerformanceCounterCategory(categoryName, theMac[0].MachineName);
        if (category.CategoryName == null || category.MachineName == null)
        {
            yield break;
        }

        List<LogInfo> logIt = new List<LogInfo>();
        if (category.CategoryType != PerformanceCounterCategoryType.SingleInstance)
        {
            List<InstanceInfo> instances = modsDB.GetInstancesFromCatMacName(theMac[0].MachineName, category.CategoryName);

            foreach (InstanceInfo inst in instances)
            {
                if (!category.InstanceExists(inst.InstanceName))
                {
                    continue;
                }
                else if (inst.InstanceName.Equals(instanceName, StringComparison.OrdinalIgnoreCase))
                {
                    PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
                                                                        inst.InstanceName, theMac[0].MachineName);

                    //CounterSample data = perfCounter.NextSample();
                    //double value = CounterSample.Calculate(data, perfCounter.NextSample());
                    string data = "";
                    List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);

                    string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");

                    List<string> dataValues = new List<string>();
                    for (int i = 0; i < seconds; i++)
                    {
                        data = "Value " + i + ": " + perfCounter.NextValue().ToString();
                        DataValueInfo datItUp = new DataValueInfo
                        {
                            Value = data
                        };
                        yield return datItUp;
                        dataValues.Add(data);
                        Thread.Sleep(1000);
                    }
                    string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");

                    Log log = new Log
                    {
                        LogName = logName,
                        CounterName = perfCounter.CounterName,
                        InstanceName = perfCounter.InstanceName,
                        CategoryName = perfCounter.CategoryName,
                        MachineName = perfCounter.MachineName,
                        TimeStarted = timeStarted,
                        TimeFinished = timeFinished,
                        PerformanceData = string.Join(",", dataValues),
                        UserID = currUser[0].UserID
                    };
                    this.CreateLog(log);
                    break;
                }
            }
        }
        else
        {
            PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
                                                                        "", theMac[0].MachineName);


            string data = "";
            List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);

            string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");

            List<string> dataValues = new List<string>();

            for (int i = 0; i < seconds; i++)
            {
                data = "Value " + i + ": " + perfCounter.NextValue().ToString();
                DataValueInfo datItUp = new DataValueInfo
                {
                    Value = data
                };
                yield return datItUp;
                dataValues.Add(data);
                Thread.Sleep(1000);
            }
            string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");

            Log log = new Log
            {
                LogName = logName,
                CounterName = perfCounter.CounterName,
                InstanceName = perfCounter.InstanceName,
                CategoryName = perfCounter.CategoryName,
                MachineName = perfCounter.MachineName,
                TimeStarted = timeStarted,
                TimeFinished = timeFinished,
                PerformanceData = string.Join(",", dataValues),
                UserID = currUser[0].UserID
            };
            this.CreateLog(log);
        }

    }

从LogController.cs文件:

[AcceptVerbs("GET", "POST")]
    public IEnumerable<DataValueInfo> Log_Perf_Data(string machine_name, string category_name, string counter_name, string instance_name,
                                   string log_name, long? seconds, string live, string enforceQuery)
    {
        LogController.CheckUser();

        // POST api/log/post_data?machine_name=&category_name=&counter_name=&instance_name=&log_name=&seconds=
        if (machine_name != null && category_name != null && counter_name != null && log_name != null && seconds.HasValue && enforceQuery == null)
        {
            List<DataValueInfo> dataVal = logDB.LogTimedPerfDataLive(machine_name, category_name, counter_name, instance_name,
                                   log_name, seconds).ToList();
            logDB.SaveChanges();
            foreach (var val in dataVal)
                yield return val;
        }

        yield break;   
    }

感谢您抽出时间考虑此事。

1
使用 yield 关键字创建的迭代器函数旨在按需获取下一个项目。它们不像彗星一样,从服务器推送数据到客户端。也许您应该对彗星技术进行一些研究。 - Kendall Frey
我认为解决方案并不那么简单,因为客户端代码也将等待完整的响应。 我同意@KendallFrey的观点,如果你想要这个功能,你必须使用comet。 - Bojan Bjelic
谢谢。我应该把Comet编码放在控制器中吗? - praetor
你尝试过流式输出吗? - Kubi
1个回答

2

yield只是创建内存对象以在响应刷新时返回。

可能最简单的方法是在服务器上保持json同步(静态?)变量,当您请求显示性能数据时,创建json,启动后台工作程序(谷歌web backgrounder nuget),将数据填充到json对象中,设置运行标志并返回json对象(可能在开始时为空)。

然后,使用浏览器的setInterval每秒刷新此数据,通过调用服务器来获取每次获得越来越多的数据的响应。当后台线程完成时,将运行标志设置为false,并在下一次调用中返回json中的信息,因此可以停止客户端刷新。

这不是最好的方法,但可能是最容易实现的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接