Фильтрация результатов на основе отдельного столбца или значения поля

#sql #asp.net-mvc-3 #entity-framework

#sql #asp.net-mvc-3 #entity-framework

Вопрос:

У меня есть приложение MVC 3, работающее с базой данных MS SQL 2008 с таблицей с именем Documents. Документы в базе данных разбиты по абзацам. В таблице Documents есть столбец DocText, содержащий текст каждого абзаца, столбец DocTitle, содержащий заголовок документа. В моем приложении MVC 3 есть функция поиска, которая может выполнять поиск слова или фразы в столбце DocText или в столбце DocTitle. Все работает нормально, за исключением того, что если в определенном документе слово для поиска встречается в нескольких абзацах, мой список возвращает несколько экземпляров этого документа. Например, если пользователь выполняет поиск по слову «Бюджет», а в одном из документов слово «бюджет» встречается в четырех разных абзацах, в моем возвращенном списке этот документ указан четыре раза.

Чего я хочу добиться, так это перечислить каждый документ, в котором есть искомое слово. Я хочу перечислить документ по названию только один раз, независимо от того, сколько раз поисковое слово встречается в этом документе. Единственный столбец, который действительно уникален, — это столбец RecordID, первичный ключ.

Мой контроллер:

     public class SearchController : Controller
{

    private GUICEEntities4 db = new GUICEEntities4();

    //
    // GET: /Search/

    public ActionResult Index(string Keyword)
    {
         #region Keyword Search
        if (!String.IsNullOrEmpty(Keyword)) {
            var SearchDoc = db.Documents.Where(r => r.DocText.ToUpper().Contains(Keyword.ToUpper()) || r.Title.ToUpper().Contains(Keyword.ToUpper()) || r.DocNum.ToUpper().Contains(Keyword.ToUpper()));

            ViewBag.CurrentKeyword = String.IsNullOrEmpty(Keyword) ? "" : Keyword;

            return View(SearchDoc.ToList());
        }
            else{
             return View();
            }
              #endregion
    }

 }
  

Мое представление имеет следующее:

 @foreach (var item in Model) {

    <tr>
        <td>
            <strong>AFI @Html.DisplayFor(modelItem => item.DocNum): @Html.DisplayFor(modelItem => item.Title)</strong>
            <br />
            @Html.DisplayFor(modelItem => item.DocSummary)
            <br />
            <span class="complianceitems">Number of compliance items:</span> (TBD)
        </td>

        <td>
            <a href="/Documents/Index/@(Html.DisplayFor(modelItem => item.DocNum))">Checklist
                Generator</a><br />
            <a href="/UploadedDocs/@Html.DisplayFor(modelItem => item.DocFileName)" target="_blank">
                Download PDF</a>
        </td>
  

Есть предложения о том, как я могу достичь своей цели?

ДОБАВЛЕНО: Каждый документ может быть идентифицирован по столбцу DocNum, который имеет уникальный номер документа для этого конкретного документа. Я попытался выполнить итерацию по списку, чтобы извлечь каждый не отвечающий требованиям документ, а затем попытаться сделать так, чтобы этот документ больше не появлялся в цикле … но мне это не удалось.

Следующий оператор SQL дает мне нужные результаты. В инструкции предполагается, что искомое слово — «бюджет». Я не знаю, как получить те же результаты, используя EF. Есть предложения?

 SELECT DISTINCT DocNum, Title FROM Documents
WHERE
DocText LIKE '%budget%'
OR
Documents.Title LIKE '%budget%'
OR
DocNum LIKE '%budget%'
  

Ответ №1:

Проблема здесь в вашем запросе EF, а не в чем-либо, связанном с MVC. Прошло некоторое время с тех пор, как я активно использовал EF, но самым простым способом, вероятно, было бы сначала вернуть только идентификаторы записей.

 var recordIds= db.Documents
                  .Where(r => 
                         r.DocText.ToUpper().Contains(Keyword.ToUpper()) ||
                         r.Title.ToUpper().Contains(Keyword.ToUpper()) ||
                         r.DocNum.ToUpper().Contains(Keyword.ToUpper()))
                  .Select(d => d.RecordId)
                  .Distinct();
  

Я не совсем уверен, что вы будете делать с каждой отдельной записью с этого момента, поскольку в вашем вопросе недостаточно информации. Но это должно помочь.

Обновить:

 var foundDocs = new List<YourType>();
recordIds.ToList().ForEach(r => foundDocs.Add(db.TblDocLists.Single(l => l.TheDocNum == r)));
//I must point out that I'm not familiar with the latest versions of EF so this might be overkill.
  

Комментарии:

1. Это приближает меня. Я использовал ваш код, но изменил RecordID на DocNum, что даст мне номер документа для каждого документа, в котором было найдено искомое слово.

2. Я исправил свой вопрос, чтобы показать изменения, которые я внес на основе вашего предложения.

3. Проблема здесь в том, что SearchDoc это IQueryable<> , а EF не поддерживает извлечение этих записей.