1

エンティティフレームワークを使用した動的クエリの作成のパターンに従って、いくつかのテーブルにわたって検索機能を作成しようとしています

私は3つのテーブルを持っています:

   People:
        pk ID
        varchar FirstName
        varchar LastName
        fk AddressMap_ID

   AddressMap:
        pk ID

   Address:
        pk ID
        varchar StreetName
        varchar StreeNumber
        fk AddressMap_ID

1つの住所に複数の人が住むことができます。検索モデルを渡し、結果プロパティにデータを入力します。

public class Search
{
   public string streetname { get; set; }
   public string streetnumber { get; set; }
   public string fname { get; set; }
   public string lname { get; set; }
   public IEnumerable<Results> results { get; set; }
}
public class Results
{
   public int AddressID { get; set; }
   public string StreetNumber { get; set; }
   public string StreetName { get; set; }
   public IEnumerable<PeopleResults> people { get; set; }
}
public class PeopleResults
{
   public int personID { get; set; }
   public string First { get; set; }
   public string Last { get; set; }
}

これは、アドレス、または名前+アドレスでフィルタリングすると機能します。

public void GetResults(Search model)
{
  Entities _context;
  _context = new Entities();

  var addr = from a in _context.Addresses
             select a;
  addr = addr.Where(filter => filter.StreetNumber == model.streetnumber);
  addr = addr.Where(filter => filter.StreetName == model.streetname);
  addr = from a in addr
         group a by a.AddressMap_ID into addrs
         select addrs.FirstOrDefault();

  var ppl = from p in addr.SelectMany(p => p.AddressMap.People) select p;
  ppl = ppl.Where(filter => filter.FirstName.StartsWith(model.fname));
  ppl = ppl.Where(filter => filter.LastName.StartsWith(model.lname));

  model.results = from a in addr
                  select new Results
                  {
                     AddressID = a.ID,
                     StreetName = a.StreetName,
                     StreetNumber = a.StreetNumber,
                     people = from p in ppl
                              select new PeopleResults
                              {
                                 First = p.FirstName,
                                 Last = p.LastName
                              }
                  };
}

しかし、名前でフィルタリングしようとすると、デカルト結合が返されます。つまり、一致したすべての人が含まれるすべてのアドレスです。

検索には3つの方法があります。アドレスのみでフィルタリングする、アドレス+名前でフィルタリングする、または名前のみでフィルタリングする。

したがって、誰かが「123 Main」を検索すると、結果は次のようになります。

123 Main St  SticksVille   Joe Smith
                           Jane Smith
                           Mary Smith

123 Main St  Bedrock       Fred Flintstone
                           Wilma Flintstone

「JSmith123Main」を検索すると、次のようになります。

123 Main St  SticksVille   Joe Smith
                           Jane Smith

そして、「JSmith」だけを検索すると次のようになります。

123 Main St  SticksVille   Joe Smith
                           Jane Smith

456 Another St Sometown    Jerry Smith
4

2 に答える 2

1

このようなアプローチはおそらくうまくいくようです:

IQueryable<Person> ppl = _context.People;
ppl = addr.Where(filter=>filter.First.StartsWith(model.fname));
ppl = addr.Where(filter=>filter.Last.StartsWith(model.lname));
var pplIds = ppl.Select(p => p.PersonId);

model.results = from a in addr
                where a.AddressMap.People.Any(p => pplIds.Contains(p.PersonId))
                select new Results {
                          AddressID = a.ID,
                          StreetName = a.StreetName,
                          StreetNumber = a.StreetNumber,
                          people = from p in a.People
                                   select new PeopleResults {
                                         First = p.FirstName,
                                         Last = p.LastName
                                   }
                };

プロパティを一致する人に基づくのではなく、peopleアドレスセット全体を一致する人に基づいて設定する必要があります。

于 2012-07-16T15:30:49.683 に答える
1

あなたのクエリは、人と住所に関して私には「対称的」に見えますが、最終的に予測される結果では「非対称的」になるだけです。したがって、私の考えは、クエリでこの対称性を可能な限り表現することです。

  • IQueryable<Address>通りの名前と通りの番号でフィルタリングされた住所のセット(、一度に実行されない)を取得します

  • IQueryable<Person>名の先頭と姓でフィルタリングされた人々のセット(、一度に実行されない)を取得します

  • で2つのセットを結合しAddressMap_IDます。結果として得られる人物と住所のセットには、住所人物のフィルター基準を満たすペアのみが含まれます。人または住所のフィルター基準の1つが指定されていない場合(質問の下部にある例の1番目と3番目)、結合はすべての人/アドレスのフィルターされていないセットで発生します。つまり、結合されたペアには常にすべての人が含まれますフィルタリングされたアドレス(またはフィルタリングされた人々のすべてのアドレス)の

  • 結合された人と住所のペアを次のようにグループ化しますAddress.ID

  • グループをResultsコレクションに投影します。グループキーはAddressIDです。StreetName各グループの最初のアドレスからStreetNumber取得でき、各グループpeopleの人々から投影されます。

  • クエリを実行します

次のコードは、4つのフィルター基準のいずれも提供されていない場合を具体的にカバーしていません。その場合は機能しますが、すべてのアドレスをそれらのアドレスのすべての人と一緒にロードするだけです。その場合、例外をスローしたいと思うかもしれません。または、何も返さない(またはかmodel.Results = nullそこら)場合は、メソッドからジャンプします。

public void GetResults(Search model)
{
    using (var _context = new Entities())
    {
        // "All" addresses
        IQueryable<Address> addresses = _context.Addresses;
        // "All" people
        IQueryable<Person> people = _context.People;

        // Build a Queryable with filtered Addresses
        if (!string.IsNullOrEmpty(model.streetname))
            addresses = addresses.Where(a => a.StreetName
                                              .StartsWith(model.streetname));
        if (!string.IsNullOrEmpty(model.streetnumber))
            addresses = addresses.Where(a => a.StreetNumber
                                              .StartsWith(model.streetnumber));

        // Build a Queryable with filtered People
        if (!string.IsNullOrEmpty(model.fname))
            people = people.Where(p => p.FirstName == model.fname);
        if (!string.IsNullOrEmpty(model.lname))
            people = people.Where(p => p.LastName == model.lname);

        // Join the two Queryables with AddressMap_ID
        // and build group by Address.ID containing pairs of address and person
        // and project the groups into the Results collection
        var resultQuery = from a in addresses
                          join p in people
                            on a.AddressMap_ID equals p.AddressMap_ID
                          group new { a, p } by a.ID into g
                          select new Results
                          {
                              AddressID = g.Key,
                              StreetName = g.Select(ap => ap.a.StreetName)
                                            .FirstOrDefault(),
                              StreetNumber = g.Select(ap => ap.a.StreetNumber)
                                              .FirstOrDefault(),
                              people = g.Select(ap => new PeopleResults
                              {
                                  First = ap.p.FirstName,
                                  Last = ap.p.LastName
                              })
                          };

        // Execute query (the whole code performs one single query)
        model.results = resultQuery.ToList();
    }
}

テーブルを多対多の関係(多くの人がいる可能性があり、多くのアドレスを持つことができる)AddressMapの一種の結合テーブルとして正しく解釈されるかどうかはわかりませんが、上記のコードは、例の3つのクエリの3つの結果を生成しますテーブルが次のように埋められている場合は、予想どおりです。AddressPerson

ここに画像の説明を入力してください

とテーブルは列を介して直接結合されるため、テーブルは実際AddressMapにはクエリで使用されません。AddressesPeopleAddressMap_ID

于 2012-07-24T20:03:02.723 に答える