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

多个人可以住在一个地址。我传入一个 Search 模型,并填充 results 属性:

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

搜索“J Smith 123 Main”应该只返回:

123 Main St  SticksVille   Joe Smith
                           Jane Smith

只搜索“J Smith”应该会返回:

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>按名字和姓氏开头过滤的一组人( ,不立即执行)

  • 通过 加入两组AddressMap_ID。生成的人员和地址集仅包含满足地址人员过滤条件的那些对。如果未提供人员或地址的过滤条件之一(问题底部的第一个和第三个示例),则连接发生在所有人员/地址的未过滤集上,即连接对始终包含所有人被过滤的地址(或被过滤的人的所有地址)

  • 按以下方式对加入的人员和地址对进行分组Address.ID

  • 将组投影到您的Results收藏中。组键是AddressID. StreetName并且StreetNumber可以从每个组中的第一个地址获取,并且从每个组中people的人投影。

  • 执行查询

以下代码未具体涵盖未提供四个过滤条件的情况。它在这种情况下有效,但只会将所有地址与这些地址的所有人一起加载。也许你想在这种情况下抛出一个异常。或者什么都不返回(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表正确解释为一种多对多关系的连接表(Address可以有很多人,Person可以有很多地址),但上面的代码在您的示例中产生了三个查询的三个结果正如预期的那样,如果表格是这样填充的:

在此处输入图像描述

AddressMap表实际上并未在查询中使用,因为Addresses表是通过列People直接连接的。AddressMap_ID

于 2012-07-24T20:03:02.723 回答