2013-09-25 2 views
2

Привет У меня есть массив, который собирает производителя и страну, по какой-то причине, когда массив возвращается, порядок массива иногда изменяется.Linq Array Список изменений заказа

Вот запрос 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.Description является производителем 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 
... 

Когда я запускаю запрос в db, чтобы проверить, что у каждого производителя есть страна, которую они делают, поскольку изначально я думал, что это возможно.

Может ли кто-нибудь сделать предложение о том, почему это может произойти?

Редактировать

Здесь запрос 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; 

Repository код:

/// <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"); 

     } 

Большое спасибо!

+8

Вы должны явно определить ваши результаты, в противном случае заказ будет передано к SQL. В зависимости от того, есть ли у вас первичные ключи в таблице, он может не всегда возвращать один и тот же порядок. – CodingIntrigue

+0

Просьба предоставить DB schama и некоторые примеры данных. – Sunny

+0

@RGraham должен добавить orderby xz. – anna

ответ

2

Коллекция Инициализаторы (new List<string> { 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<string, IEnumerable<string>>, а не 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"); 
} 
+0

Я обновил свой вопрос, если вы посмотрите на редактирование 2, как вы думаете, я все еще использую метод, который вы описали? спасибо – anna

+0

@anna - взгляните на мой рефактор вашего кода – SWeko

+0

, что бы объявление было для словаря, оно не является общедоступным. Словарь > GetCountryAndManufacturerForUser (int userId) – anna

0

В вашем заявлении выбора вы создаете список, который по существу неупорядочен. Я бы посоветовал вам создать новый класс с описанием, именем (и, возможно, большим количеством свойств), потому что мне кажется, что каждый элемент в списке имеет конкретный источник, а не список.

Если вы не хотите, чтобы создать новый класс, вы всегда можете поместить его в кортеже, как это:

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(); 
Смежные вопросы