【问题标题】:Can JSON be returned "live" using yield return?可以使用收益返回“实时”返回 JSON 吗?
【发布时间】:2012-08-27 06:04:15
【问题描述】:

情况是这样的。

我正在处理的内容: - ASP.NET MVC 4 Web API - IIS 7.0 托管应用程序 - C#

我有 Web API,可以从 Intranet 上的远程计算机获取性能数据,并且正在测试通过 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;   
    }

感谢您的时间和考虑。

【问题讨论】:

  • 使用 yield 关键字创建的迭代器函数旨在根据请求提取下一个项目。它们不像彗星,它将数据从服务器推送到客户端。也许你应该对彗星技术做一点研究。
  • 我认为解决方案并不那么容易,因为客户端代码也会等待完整的响应。我同意@KendallFrey,如果你想要这个,你必须使用彗星。
  • 谢谢。我会把彗星编码放在Controller中吗?
  • 你试过流式输出吗?

标签: c# asp.net-mvc json asp.net-web-api


【解决方案1】:

yield 仅在刷新响应时创建要返回的内存对象。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    • 2011-07-01
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 1970-01-01
    • 2010-10-26
    相关资源
    最近更新 更多