【问题标题】:Linq Array List order changesLinq 数组列表顺序更改
【发布时间】:2013-09-25 07:41:42
【问题描述】:

您好,我有一个包含制造商和国家/地区的数组,由于某种原因,当数组返回时,数组的顺序有时会发生变化。

这是 Linq 查询:

  var array = (from xx in _er.UserRoles
                         join xy in _er.Countries on xx.CountryId equals xy.Id
                         join xz in _er.Manufacturers on xx.ManufacturerId equals xz.Id
                         where xx.UserId == userId
                         select new List<string> { xz.Description, xy.Name }).ToArray();

在哪里: xz.描述是制造商 xy.Name 是国家

在我的数组中,我希望得到以下内容:

[0]    Count = 2
   [0] Dove
   [1] Uk
[1]    Count = 2
   [0] Dove
   [1] France
[2]    Count = 2
   [0] Sure
   [1] UK
...

但在某些情况下,我会得到以下信息:

[0]    Count = 2
   [0] Dove
   [1] Uk
[1]    Count = 2
   [0] France
   [1] Dove
[2]    Count = 2
   [0] UK
   [1] Sure
...

当我在数据库中运行查询以检查每个制造商是否有他们所做的国家时,我最初认为可能是这样。

有人可以就为什么会发生这种情况提出建议吗?

编辑

这是 sql 查询和一些示例数据:

select m.Description, c.Name from UserRoles ur
join Countries c on ur.CountryId = c.Id
join Manufacturers m on ur.ManufacturerId = m.Id
where ur.userid = 435

示例数据:

Description     Name
Lynx        United Kingdom
Persil      United Kingdom
Dove        Brazil
Dove        Canada
Dove        Germany
Dove        France
Dove        United Kingdom
Dove        Netherlands
Dove        United States
Surf        United Kingdom
Comfort     United Kingdom
Sure        United Kingdom
Bertolli        United Kingdom
Bertolli        United States

编辑 2

这里是对我正在做的事情的更多解释,这样可能会更多地解释我最终需要什么:

在我的控制器中,我将数组放入会话中:

控制器代码:

  var userManuCountry = _userRoleRepository.GetCountryAndManufacturerForUser(u.Id);
  Session["userManuCountry"] = userManuCountry;

存储库代码:

/// <summary>
        /// 
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public string[,] GetCountryAndManufacturerForUser(int userId)
        {

            var array = (from xx in _er.UserRoles
                         join xy in _er.Countries on xx.CountryId equals xy.Id
                         join xz in _er.Manufacturers on xx.ManufacturerId equals xz.Id
                         where xx.UserId == userId
                         select new List<string> { xz.Description, xy.Name }).ToArray();
            return CreateRectangularArray(array);

        }


        static T[,] CreateRectangularArray<T>(IList<T>[] arrays)
        {
            // TODO: Validation and special-casing for arrays.Count == 0
            int minorLength = arrays[0].Count();
            T[,] ret = new T[arrays.Length, minorLength];
            for (int i = 0; i < arrays.Length; i++)
            {
                var array = arrays[i];
                if (array.Count != minorLength)
                {
                    throw new ArgumentException
                        ("All arrays must be the same length");
                }
                for (int j = 0; j < minorLength; j++)
                {
                    ret[i, j] = array[j];
                }
            }
            return ret;
        }

另一个控制器 - 我正在使用会话来列出制造商的国家/地区:

 /// <summary>
        /// et the specific countries for user and manufacturer
        /// </summary>
        /// <returns></returns>
        [AcceptVerbs(HttpVerbs.Get)]
        //  [ValidateAntiForgeryToken]
        // [Authorize(Roles = "ReportingDashboardAccess")]
        public ActionResult GetListOfCountriesForUserManufacturer(int userId, string manu)
        {
            manu = manu.Trim();
            // get the specific countries for user and manufacturer
            var countries = new List<string>();              

            //here we want to use the manu to get the countries from seesion rather than db - this is a multidimensional array
            string[,] manuCountry = (string[,])Session["userManuCountry"];

            var addCountry = false;
            //loop through to find countries for each manufacturer
            for (int row = 0; row < manuCountry.GetLength(0); row++)
            {
                for (int col = 0; col < manuCountry.GetLength(1); col++)
                {
                    string result = manuCountry[row, col];
                    result.Trim();
                    if (addCountry == true && col == 1)
                    {
                        //addcountry has been set to true so add it
                        countries.Add(result);
                        addCountry = false;
                    }
                    else if (addCountry == true && col == 0)
                    {
                        addCountry = false;
                    }
                    if (result == manu)
                    {
                        //the next one that comes through is the country
                        addCountry = true;

                    }


                }
            }

            countries.Sort();
            ViewData["allCountries"] = new SelectList(countries);


            return View("CountriesParam");

        }

非常感谢!

【问题讨论】:

  • 您需要明确对结果进行排序,否则排序将交给 SQL。根据表上是否有任何主键,它可能并不总是返回相同的顺序
  • 请提供 DB schama 和一些示例数据。
  • @RGraham 应该添加 orderby xz.Description 升序就够了吗?
  • @Sunny 我已将数据添加到问题和 sql 查询中
  • @RGraham,正确,它将取决于用于为查询提供服务的查询计划,并且可能会受到并行化的极大影响。

标签: c# sql arrays linq entity-framework


【解决方案1】:

Collection Initializers (new List&lt;string&gt; { xz.Description, xy.Name }) 应该保持表达式中指定的项目的顺序,因此您的代码应该可以工作。

我猜想有什么东西在创建的列表上运行,并以某种方式混淆了排序。

也就是说,对具有不同含义的值使用列表(或任何集合)是不直观的。即使它们都是字符串,这些值也不具有相同的上下文。给它们明确不同的容器会更好。例如现在,如果你用数据填充几个文本框,你会使用:

txtName.Text = list[0];
txtCountry.Text = list[1];

而且很难发现和诊断错误。如果您使用

将结果放在单独的实体(例如匿名类)中
select new { Name = xz.Description, Country = xy.Name }

你可以使用

txtName.Text = myObject.Name;
txtCountry.Text = myObject.Country

关于edit2:如果我正确理解您的情况,您需要获取制造商允许的国家/地区列表。此类数据的一个很好的容器是Dictionary&lt;string, IEnumerable&lt;string&gt;&gt;,而不是string[,]

我会像这样重构 LINQ:

//gets the data from the database
var data = (from xx in _er.UserRoles
                     join xy in _er.Countries on xx.CountryId equals xy.Id
                     join xz in _er.Manufacturers on xx.ManufacturerId equals xz.Id
                     where xx.UserId == userId
                     select new { Name = xz.Description, Country = xy.Name });
//formats the data into a dictionary
var result = data.GroupBy(a => a.Name)
                 .ToDictionary(// the name of the product
                               g => g.Key, 
                               // the list of countries for the product
                               g => g.Select(a => a.Country).ToList());
return result;

然后像这样使用它(在GetListOfCountriesForUserManufacturer):

public ActionResult GetListOfCountriesForUserManufacturer(int userId, string manu)
{
   manu = manu.Trim();

   //I'm not too crazy about sesiion usage, but that's a whole other issue
   var manuCountry = (Dictionary<string, List<string>>)Session["userManuCountry"];

   // get the specific countries for user and manufacturer
   var countries = manuCountry[manu];
   countries.Sort();
   ViewData["allCountries"] = new SelectList(countries);
   return View("CountriesParam");
}

【讨论】:

  • 如果您查看编辑 2,我已经更新了我的问题,您认为我还应该使用您描述的方法吗?谢谢
  • 字典的声明是什么,它不像 public Dictionary> GetCountryAndManufacturerForUser(int userId)
  • 试试Dictionary&lt;string, List&lt;string&gt;&gt;
  • 我目前遇到错误 5 无法将类型 'System.Collections.Generic.Dictionary>' 隐式转换为 'System.Collections。 Generic.Dictionary>' D:\Dev2010\emsTest\ReportingAdmin\Models\UserRoleRepository.cs 206 20 ReportingAdmin 错误
  • 重写了.ToDictionary调用,现在就试试吧。
【解决方案2】:

在您的选择语句中,您正在创建一个本质上是无序的列表。 我建议您创建一个带有描述、名称(可能还有更多属性)的新类,因为在我看来,列表中的每个项目都有一个特定的来源,而不是一个列表。

如果你不想创建一个新类,你总是可以把它放在这样的元组中:

var array = (from xx in _er.UserRoles
                         join xy in _er.Countries on xx.CountryId equals xy.Id
                         join xz in _er.Manufacturers on xx.ManufacturerId equals xz.Id
                         where xx.UserId == userId
                         select new Tuple<string,string> (xz.Description, xy.Name)).ToArray();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 1970-01-01
    • 2020-02-22
    相关资源
    最近更新 更多