Question

J'ai une hiérarchie que je voudrais interroger avec LinqToSql:

Pays - > Région - > Ville - > ZipCode

Chaque entité contient à la fois une référence à son parent (par exemple, Region.Country) et une collection de ses enfants (par exemple, Region.Cities).

Je voudrais charger avec impatience le nom de la société mère de chaque entité, ainsi que les pays et les régions, mais ne pas charger les villes et les codes postaux.

Pour compliquer les choses, chaque entité est localisée avant d’être projetée dans le modèle. Donc, Country.Name change en fonction de la langue.

Voici quelques extraits de ce que j'ai jusqu'à présent:

public IQueryable<Country> ListCountries()
{
  return ProjectCountry(dataContext.GetTable<ec_Country>());
}

private IQueryable<Country> ProjectCountry(IQueryable<ec_Country> query)
{
  var result = from country in query
  join localized in dataContext.GetTable<ec_CountryLocalization>() on country.CountryID equals localized.CountryID
  let regions = GetRegions(country.CountryID)
  where localized.StatusID == 4 && localized.WebSiteID == this.webSiteID
  select new Country(country.CountryID) {
    CreatedDate = country.CreatedDate,
    IsDeleted = country.IsDeleted,
    IsoCode = country.IsoCode,
    Name = country.Name,
    Regions = new LazyList<Region>(regions),
    Text = localized.Text,
    Title = localized.Title,
    UrlKey = country.UrlKey
  };

  return result;
}

private IQueryable<Region> GetRegions(Int32 countryID)
{
  var query = from r in dataContext.GetTable<ec_Region>()
  where r.CountryID == countryID
  orderby r.Name
  select r;

  return ProjectRegion(query);
}

private IQueryable<Region> ProjectRegion(IQueryable<ec_Region> query)
{
  var result = from region in query
  join localized in dataContext.GetTable<ec_RegionLocalization>() on region.RegionID equals localized.RegionID
  join country in ListCountries() on region.CountryID equals country.CountryID
  let cities = GetCities(region.RegionID)
  select new Region(region.RegionID) {
    Cities = new LazyList<City>(cities),
    Country = country,
    CountryID = region.CountryID,
    CreatedDate = region.CreatedDate,
    IsDeleted = region.IsDeleted,
    IsoCode = region.IsoCode,
    Name = region.Name,
    Text = localized.Text,
    Title = localized.Title,
    UrlKey = region.UrlKey
  };

  return result;
}

... etc.

[TestMethod]
public void DataProvider_Correctly_Projects_Country_Spike()
{
  // Act
  Country country = dataProvider.GetCountry(1);

  // Assert
  Assert.IsNotNull(country);
  Assert.IsFalse(String.IsNullOrEmpty(country.Description));
  Assert.IsTrue(country.Regions.Count > 0);
}

Le test échoue avec:

System.NotSupportedException: la méthode 'System.Linq.IQueryable`1 [Beeline.EducationCompass.Model.Region] GetRegions (Int32)' n'a pas de traduction prise en charge en SQL.

Comment recommanderiez-vous que je m'occupe de cela? Serait-il plus simple (ou possible) que chaque niveau de la hiérarchie se trouve dans le même tableau au lieu de ceux-ci?

Était-ce utile?

La solution

Vous allez utiliser le concepteur linq pour configurer les relations entre vos objets. Cela vous évite d'écrire, jointure après jointure après création de propriétés.

  • entre un pays et ses régions
  • entre une région et ses villes
  • entre un pays et ses localisations
  • entre une région et ses localisations

Vous allez utiliser ToList pour séparer les opérations que vous souhaitez traduire en SQL et celles que vous souhaitez effectuer en code local. Si vous ne le faites pas, vous verrez toujours ceux-ci "ne peuvent pas traduire votre méthode en SQL". exceptions.

Vous allez également vouloir utiliser DataLoadOptions pour charger ces propriétés avec impatience dans certains cas. Voici mon coup de poignard.

DataLoadOptions dlo = new DataLoadOptions();
//bring in the Regions for each Country
dlo.LoadWith<ec_Country>(c => c.Regions);
//bring in the localizations
dlo.AssociateWith<ec_Country>(c => c.Localizations
  .Where(loc => loc.StatusID == 4 && loc.WebSiteID == this.webSiteID)
);
dlo.AssociateWith<ec_Region>(r => r.Localizations);

//set up the dataloadoptions to eagerly load the above.
dataContext.DataLoadOptions = dlo;

//Pull countries and all eagerly loaded data into memory.
List<ec_Country> queryResult = query.ToList();

//further map these data types to business types
List<Country> result = queryResult
  .Select(c => ToCountry(c))
  .ToList();
public Country ToCountry(ec_Country c)
{
  return new Country()
  {
    Name = c.Name,
    Text = c.Localizations.Single().Text,
    Regions = c.Regions().Select(r => ToRegion(r)).ToList()
  }
}

public Region ToRegion(ec_Region r)
{
  return new Region()
  {
    Name = r.Name,
    Text = r.Localizations.Single().Text,
    Cities = r.Cities.Select(city => ToCity(city)).ToLazyList();
  }
}

Autres conseils

C'est un morceau de code collant, et je n'aurais pas répondu à cette question à cause du manque de compétences pertinentes si quelqu'un d'autre l'avait fait, mais puisque vous n'avez eu aucune réponse ...

Je peux vous dire ce que signifie le message d'erreur. Cela signifie que la fonction GetRegions ne peut pas être traduite en SQL par le fournisseur linq to SQL. Certaines fonctions intégrées peuvent être, voici une liste, car le fournisseur les comprend bien. . Sinon, vous pouvez fournir des traductions, voir ici .

Dans votre cas, vous devez "intégrer" la logique de cette requête. La logique ne franchira pas la limite d'un appel de fonction, car vous avez affaire à un arbre d'expression, le serveur SQL ne peut pas rappeler votre Méthode GetRegions.

Quant à la manière exacte de le faire, vous devrez essayer, je n'ai pas le temps de vous obliger pour le moment. (À moins que quelqu'un d'autre ait le temps et les compétences?)

Bonne chance.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top