Пытается написать запрос LINQ, используя содержит и начинается с списка

Используя VS 2017 в C#, у меня есть рабочее решение с расширенной областью применения. Первоначально нужно было только вернуть набор данных, для которого goodRMA_flag был установлен в true, если line.RMANumber содержал или начинал с двух разных переменных.

Это оригинальный код, который отлично работает:

string mask1 = "/078";
string mask2 = "078";

        //start with all of them, flag the good and bad
        var RMA_stops_all = (from rma in rDb.DistributionStopInformations
                             join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
                             where line.RmaNumber != null
                             &&
                             (line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
                             line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
                             && rma.CustomerNo == TNGCustNo
                             select new
                             {
                                 DatetimeCreated = line.DatetimeCreated,
                                 UniqueIdNo = rma.UniqueIdNo,
                                 RmaNumber = line.RmaNumber,
                                 RmaOriginalUniqueId = line.RmaOriginalUniqueId,
                                 ItemSequenceNo = line.ItemSequenceNo,
                                 ItemNumber = line.ItemNumber,
                                 goodRMA_flag = (line.RmaNumber.Contains(mask1) || line.RmaNumber.StartsWith(mask2)),
                                 RMA_cleanedUp = line.RmaNumber.Substring(line.RmaNumber.IndexOf("/") + 1)
                             }).ToArray();

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

  int startingMask = 75;
            int numberNumberNeeded = 10;
            List<string> masksContains = new List<string>();
            List<string> masksStartsWith = new List<string>();

            while (numberNumberNeeded > 0)
            {
            string newMask = (++startingMask).ToString().PadLeft(3, '0');
                masksStartsWith.Add(newMask);
                newMask = newMask.PadLeft(4, '/');
                masksContains.Add(newMask);
                numberNumberNeeded--;
            }

Теперь, когда у меня есть список, я хотел изменить строку для goodRMA_flag на что-то вроде:

goodRMA_flag = (line.RmaNumber.Contains(masksContains) || line.RmaNumber.StartsWith(masksStartsWith)),

Я предполагаю, что мне понадобится какая-то Lamda, но я не смог получить правильный синтаксис, несмотря на многочисленные колебания.

РЕДАКТИРОВАТЬ: Использование -

    goodRMA_flag = (masksContains.Any(masks => line.RmaNumber.Contains(masks)) ||
 masksStartsWith.Any(masks => line.RmaNumber.StartsWith(masks))),

Выдает ошибку времени выполнения: "System.NotSupportedException:" Локальная последовательность не может использоваться в реализации операторов запросов LINQ to SQL, кроме оператора Contains() ".

Решение - вот окончательный рабочий код для справки:

 //a integer that we are starting with - this needs to come from
 //a database at some point
        int startingMask = 75;
        //how far ahead to look
        int qtyNeeded= 10;
        //will hold the strings that RMA_Number needs to contain
        List<string> masksContains = new List<string>();
        //will hold the strings that RMA_Number needs to start with
        List<string> masksStartsWith = new List<string>();

        //Build the two lists
        while (qtyNeeded> 0)
        {
        string newMask = (++startingMask).ToString().PadLeft(3, '0');
            masksStartsWith.Add(newMask);
            newMask = newMask.PadLeft(4, '/');
            masksContains.Add(newMask);
            qtyNeeded--;
        }

        //start with all of them, flag them all as bad -- will then step through and fix
        var RMA_stops_all = (from rma in rDb.DistributionStopInformations
                             join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
                             where line.RmaNumber != null
                             &&
                             (line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
                             line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
                             && rma.CustomerNo == TNGCustNo
                             select new RMA_Items
                             {
                                 DatetimeCreated = Convert.ToDateTime(line.DatetimeCreated),
                                 UniqueIdNo = rma.UniqueIdNo,
                                 RmaNumber = line.RmaNumber,
                                 RmaOriginalUniqueId = Convert.ToDecimal(line.RmaOriginalUniqueId),
                                 ItemSequenceNo = Convert.ToDecimal(line.ItemSequenceNo),
                                 ItemNumber = line.ItemNumber,
                                 goodRMA_flag = false,
                                 RMA_cleanedUp = line.RmaNumber.Substring(line.RmaNumber.IndexOf("/") + 1)
                             }).ToArray();

        //convert it to a new list that we can step through
        var rmaStopsAllList = RMA_stops_all.ToList();

        //go through the new list, set the goodRMA_flag for the items that meet our criteria
        rmaStopsAllList.ForEach(x => x.goodRMA_flag =
            (masksContains.Any(masks => x.RmaNumber.Contains(masks))
             || masksStartsWith.Any(masks => x.RmaNumber.StartsWith(masks)))&&
             x.RMA_cleanedUp.Length==10);

        //flip it back into our original array
        RMA_stops_all = rmaStopsAllList.ToArray();

        //pull out the good ones
        var RMA_Stops_GoodRMA = (from R in RMA_stops_all
                    where R.goodRMA_flag == true
                    select R).ToArray();

        //pull out the bad ones
        var RMA_Stops_BadRMA = (from B in RMA_stops_all
                             where B.goodRMA_flag == false
                             select B).ToArray();

Вместе с новым классом:

class RMA_Items
{
    public DateTime DatetimeCreated { get; set; }
    public decimal UniqueIdNo { get; set; }
    public decimal ItemSequenceNo { get; set; }
    public string RmaNumber { get; set; }
    public decimal RmaOriginalUniqueId { get; set; }
    public string ItemNumber { get; set; }
    public bool goodRMA_flag { get; set; }
    public string RMA_cleanedUp { get; set; }

}

3 ответа

Решение

Я предполагаю что line.RmaNumber должен содержать любую из масок, доступных в masksContains, Или, альтернативно line.RmaNumber должен начинаться с одной из масок, определенных в masksStartsWith, Если какое-либо из этих условий выполнено, то goodRMA_flag будет установлен в true, Надеюсь, это то, что вы ищете:

goodRMA_flag = (masksContains.Any(masks => line.RmaNumber.Contains(masks))
    || masksStartsWith.Any(masks => line.RmaNumber.StartsWith(masks)))

РЕДАКТИРОВАТЬ: Однако, поскольку LINQ to SQL не позволяет этого, потому что line.RmaNumber идет от SQL напрямую, мы можем вместо этого обновить флаг на следующем шаге, так как RmaNumber имеется в объекте.

var rmaStopsAllList = RMA_stops_all.ToList();
rmaStopsAllList.Foreach(x => x.goodRMA_flag = 
    (masksContains.Any(masks => x.RmaNumber.Contains(masks))
     || masksStartsWith.Any(masks => x.RmaNumber.StartsWith(masks))));
RMA_stops_all = rmaStopsAllList.ToArray();

РЕДАКТИРОВАТЬ 2: Также необходимо объявить фактический класс вместо использования анонимного типа, чтобы он мог быть назначен позже (как показано выше). goodRMA_flag должно быть свойством в классе и может иметь любое значение на предыдущем шаге.

select new SomeClass    
{
    // Other initialization here
    goodRMA_flag = false,
}).ToArray();

вместо

select new
{
    // Other initialization here
    goodRMA_flag = false,
}).ToArray();

и что наиболее важно, обязательно добавьте новое определение класса для этой цели с правильными типами данных:

public class SomeClass
{
    public string DateTime DatetimeCreated {get; set;}
    public string UniqueIdNo {get; set;}
    public string RmaNumber {get; set;}
    public int RmaOriginalUniqueId {get; set;}
    public string ItemSequenceNo {get; set;}
    public string ItemNumber {get; set;}
    public bool goodRMA_flag {get; set;}
    public string RMA_cleanedUp {get; set;}
}

РЕДАКТИРОВАТЬ: Основная проблема заключается в том, что это LinqToSql, и он ссылается на локальную последовательность в операторе выбора. Вам нужно будет получить результаты и создать новый объект с локальными результатами.

Надеюсь, я не правильно понял ваш вопрос.

var db = new string[]{ "1", "2", "41", "C", "ka"};
var masks = new string[] { "a", "1" };
var query = from n in db
    select new
    {
       n = n,
       t = masks.Where(x=> n.Contains(x)).Count() > 0, //works
       t2 = masks.Any(x=> n.Contains(x)) //best approach
    };

Как видите, просто нужно проверить на Содержит.

1, 41 и ка, сбывается.

Вы можете попытаться создать метод, который возвращает логическое значение. Он примет список масок. Если маска не совпадает, вы возвращаете false.

Когда у вас есть этот метод, вы можете передать его в лямбду.

Другие вопросы по тегам