Domanda

Ho una gerarchia che vorrei interrogare con LinqToSql:

Paese - > Regione - > Città - > ZipCode

Ogni entità detiene sia un riferimento al suo genitore (ad es. Region.Country) che una raccolta dei suoi figli (ad es. Region.Cities).

Vorrei caricare con impazienza il genitore di ciascuna entità insieme a Paesi e regioni, ma caricare città e codici postali in modo pigro.

Per complicare le cose, ogni entità viene localizzata prima di essere proiettata nel modello. Quindi Country.Name cambia in base alla lingua.

Ecco alcuni frammenti di ciò che ho finora:

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;
}

... ecc.

[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);
}

Il test ha esito negativo con:

System.NotSupportedException: il metodo 'System.Linq.IQueryable`1 [Beeline.EducationCompass.Model.Region] GetRegions (Int32)' non ha una traduzione supportata in SQL.

Come mi consiglieresti di fare questo? Sarebbe più semplice (o possibile) se ogni livello della gerarchia fosse nella stessa tabella invece di quelli separati?

È stato utile?

Soluzione

Ti consigliamo di utilizzare il linq designer per impostare relazioni tra i tuoi oggetti. Ciò ti impedisce di scrivere join dopo join dopo join creando proprietà.

  • tra un Paese e le sue regioni
  • tra una regione e le sue città
  • tra un Paese e le sue localizzazioni
  • tra una regione e le sue localizzazioni

Ti consigliamo di utilizzare ToList per separare quelle operazioni che intendi essere tradotte in SQL e quelle che intendi eseguire nel codice locale. Se non lo fai, continuerai a vedere quelli che non possono tradurre il tuo metodo in SQL " eccezioni.

Ti consigliamo di utilizzare DataLoadOptions per caricare avidamente queste proprietà in alcuni casi. Ecco la mia pugnalata.

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();
  }
}

Altri suggerimenti

Questo è un pezzo di codice appiccicoso, e non avrei risposto a questo a causa della mancanza di abilità pertinenti se qualcun altro avesse avuto, ma dal momento che non hai avuto risposte ...

Posso dirti cosa significa il messaggio di errore. Significa che la funzione GetRegions non può essere tradotta in sql dal provider linq in sql. Alcune funzioni integrate possono essere, poiché il provider le comprende, ecco un elenco . Altrimenti puoi fornire traduzioni vedi qui .

Nella tua situazione devi "incorporare" la logica di questa query, la logica non attraverserà il limite di una chiamata di funzione, poiché hai a che fare con un albero di espressioni, il server sql non può richiamare nel tuo Metodo GetRegions.

Per quanto riguarda il modo esatto per farlo, dovrai provarci, non ho il tempo di obbligarti al momento. (A meno che qualcun altro non abbia tempo e abilità?)

Buona fortuna.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top