Pergunta

Eu tenho uma hierarquia que eu gostaria de consulta com LinqToSql:

Country -> Região -> City -> ZipCode

Cada entidade detém tanto uma referência a ele de pai (ex. Region.Country) e uma coleção de TI de crianças (por exemplo. Region.Cities).

Eu gostaria de carga ansioso pai de cada entidade, juntamente com os países e regiões, mas cidades carga lenta e códigos postais.

Para coisas complicar, cada entidade está sendo localizada antes de ser projetada para o modelo. Então Country.Name muda de acordo com o idioma.

Eis alguns trechos do que eu tenho até agora:

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

O teste falha com:

System.NotSupportedException:. Método 'System.Linq.IQueryable`1 [Beeline.EducationCompass.Model.Region] getRegions (Int32)' não tem apoiado tradução para SQL

Como você recomendaria eu vou sobre isso? Seria mais simples (ou possível) se cada nível da hierarquia estava na mesma mesa em vez dos separados?

Foi útil?

Solução

Você vai querer usar o designer LINQ para configurar as relações entre seus objetos. Isso você fica fora da escrita aderir após aderir após juntar-se através da criação de propriedades.

  • Entre um país e suas regiões
  • entre uma região e suas Cidades
  • entre um país e sua Localizações
  • entre uma região e suas Localizações

Você vai querer usar ToList para separado as operações que pretende ser traduzido em SQL, e às operações que pretende ser feito em código local. Se você não fizer isso, você vai continuar vendo aqueles "não pode traduzir o seu método em SQL" exceções.

Você também vai querer usar DataLoadOptions para carregar ansiosamente essas propriedades em alguns casos. Aqui está a minha facada nele.

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

Outras dicas

Isso é um pedaço pegajoso de código, e eu não teria respondido isso devido à falta de habilidade relevante se mais alguém teve, mas desde que você não tinha respostas ...

Eu posso dizer o que os meios de mensagens de erro. Isso significa que os getRegions função não pode ser traduzido em sql pelo provedor LINQ to SQL. Algumas funções internas podem ser, porque o provedor entende-los, aqui está uma href="http://msdn.microsoft.com/en-us/library/bb882657.aspx" rel="nofollow lista . Caso contrário, você pode fornecer traduções ver aqui .

Na sua situação você precisa 'inline' a lógica desta consulta, a lógica não irá atravessar a fronteira de uma chamada de função, porque você está lidando com um árvore de expressão, o servidor SQL não pode chamar de volta em seu getRegions método.

Quanto à maneira exata de fazer isso, você terá que ter um ir, eu não tenho o tempo para obrigá-lo no momento. (A menos que alguém tem tempo e habilidade?)

Boa sorte.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top