C# - String to list used in Linq Where Any statement - c#

I would like to use this string as a filter to remove some Ids in a linq query
public class ProductKitMakerDto
{
public int Id { get; set; }
public string TitleShort { get; set; }
public string Media { get; set; }
}
[HttpPost]
public ActionResult KitItemSelect(string culture)
{
string productMakerIds = "4174,2196,2201,2460,2508,2204";
//create a list
var productMakerList = new List<ProductKitMakerDto>();
foreach (int i in productMakerIds)
{
productMakerList.Add(new ProductKitMakerDto { Id = i });
}
var itemselects = (from p in _context.Products
where p.Matrix == 2400
select new ProductKitMakerDto()
{
Id = p.Id,
TitleShort = culture == "de" ? p.TitleShortDe :
culture == "fr" ? p.TitleShortFr :
p.TitleShortEn,
Media = "/img/" + p.Photo,
}).ToList();
//From this query I get 40 results.
//Then I want to remove the ones from the list:
//itemselects = itemselects.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id));
//1st (above) I get an Error CS0266 asking for explicit cast. So aplly the modification
itemselects = (List<ProductKitMakerDto>)itemselects.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id));
return Json(itemselects, JsonRequestBehavior.AllowGet);
}
I get 500 (Internal Server Error) - xhr.send( options.hasContent && options.data || null );
I guess the list is empty.
Any idea? Thanks

this does not work
string productMakerIds = "4174,2196,2201,2460,2508,2204";
var productMakerList = new List<ProductKitMakerDto>();
foreach (int i in productMakerIds)
{
productMakerList.Add(new ProductKitMakerDto { Id = i });
}
because you need to split on comma first and parse the string to int:
foreach (string i in productMakerIds.Split(',')) // and parse i to int with int.Parse
but since it's a string literal, initialize it correctly in the first place. Don't use a List<ProductKitMakerDto> because you just need a List<int>, then you can use Contains:
var productMakerList = new List<int>
{
4174, 2196, 2201, 2460, 2508 , 2204
};
you can not cast to a list if it's not a list and Enumerable.Where does not return one:
itemselects = (List<ProductKitMakerDto>)itemselects.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id));
you need to append ToList after the Where
itemselects = itemselects
.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id))
.ToList();
but as mentioned, you could also use this Where before you create that list the first time, so include the condition witha Contains which should be supported:
var itemselects = (from p in _context.Products
where p.Matrix == 2400
&& !productMakerList.Contains(p.Id)
select new ProductKitMakerDto()
{
Id = p.Id,
TitleShort = culture == "de"
? p.TitleShortDe
: culture == "fr" ? p.TitleShortFr : p.TitleShortEn,
Media = "/img/" + p.Photo,
}).ToList();

foreach (string i in productMakerIds.Split(','))
{
productMakerList.Add(new ProductKitMakerDto { Id = int.Parse(i) });
}

Related

LINQ - Condition with .Contains() is not working as expected

I cannot seem to get the desirable filtered result from my query.
Data
public class fdp_1115
{
public string Id{ get; set; }
public string Number{ get; set; }
public string Type{ get; set; }
}
List<fdp_1115> fdpList = new List<fdp_1115>
{
new fdp_1115 { Id = "1", Number = "Lot123", Type = "D14MWT" },
new fdp_1115 { Id = "2", Number = "Lot123", Type = "E12WBC7W1" }
};
List<string> searchValues = new List<string> { "MLE12WBC7W1 A R" };
LINQ:
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => d.Type.Contains(s)));
if (LocType != null)
{
Console.WriteLine("Matching record found:");
Console.WriteLine($"Id: {LocType.Id}, Number: {LocType.Number}, Type: {LocType.Type}");
}
else
{
Console.WriteLine("No matching records found.");
}
The result I wanted is:
Matching record found:
Id: 2, Number: Lot123, Type: E12WBC7W1
But I got "No matching records found." which indicates that LocType == null.
I already tried trimming and ignoring case sensitive:
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => d.Type.Contains(s.Trim().Replace(" ", ""))));
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => d.Type.Contains(s, StringComparison.InvariantCultureIgnoreCase)));
But still no luck. Any idea how do I match "MLE12WBC7W1 A R" with "E12WBC7W1"?
You have your contains the other way around.
d.Type = "E12WBC7W1"
and
s = "MLE12WBC7W1 A R"
Then "E12WBC7W1" does not Contains "MLE12WBC7W1 A R"
It is the other way around.
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => s.Contains(d.Type)));
Your current logic checks whether there is any object with Type value that contains the value for each string in the searchValues array.
From your requirement:
You want to filter the object that fulfills there is any string in searchValues containing the value of Type.
Thus it should be:
var LocType = fdpList.FirstOrDefault(d => searchValues.Any(s => s.Contains(d.Type)));

How do I fill a child list inside a parent list using linq2db in a single query in my .net core app?

I am trying to query my database to return turn reports with any attachments included. I need a list of turn report items which are returned by date, and then for each report I want it to also return all of the attachments associated with the turn reports. The only way to associate them is by the EntryId.
Here is my method to get the turn reports:
public List<TurnReportItem> GetTurnReportsByDateShiftAndDept(DateTime shiftStart, int shiftNum, int dept)
{
try
{
List<TurnReportItem> list;
using (connection)
{
list = (from r in connection.VTurnReports
join a in connection.TurnReportAreas on r.AreaId equals a.AreaId
where a.DeptId == dept && a.Enabled && r.ShiftDate == shiftStart && r.ShiftNum == shiftNum
select new TurnReportItem
{
areaId = r.AreaId,
areaName = a.Name,
author = r.Author,
comment = r.Comment,
datetime = r.Datetime,
id = r.EntryId,
ip = r.Ip,
shiftDate = r.ShiftDate,
shiftNum = r.ShiftNum,
sort_order = a.SortOrder,
attachment_count = r.AttachmentCount,
attachments = (
from at in connection.TurnReportAttachments where at.EntryId == r.EntryId
select new TurnReportAttachment
{
AttachmentId = at.AttachmentId,
FileName = at.FileName
}).ToList()
})
.OrderBy(r => r.sort_order)
.OrderBy(r => r.datetime)
.ToList();
return list;
}
}
Here is the TurnReportItem class that I am filling. If I do not have the subquery I do get all of the turnreports.
public class TurnReportItem
{
public int id;
public string comment;
public DateTime datetime;
public string author;
public int areaId;
public string areaName;
public DateTime shiftDate;
public int shiftNum;
public string ip;
public int? attachment_count;
public int sort_order;
public int area_rating;
public List<TurnReportAttachment> attachments;
public TurnReportItem() { }
}
I have a separate method that will return the all of the comments with the entry id. I have tried to fill the list using that method. I am converting this from a MVC app and I was able to use the method to fill the list however it will not work when I try it in this app, I also would prefer to only make one connection in the database to get what I need.
List<TurnReportItem> list;
using (connection)
{
list = (from r in connection.VTurnReports
join a in connection.TurnReportAreas on r.AreaId equals a.AreaId
where a.DeptId == dept && a.Enabled && r.ShiftDate == shiftStart && r.ShiftNum == shiftNum
select new TurnReportItem
{
areaId = r.AreaId,
areaName = a.Name,
author = r.Author,
comment = r.Comment,
datetime = r.Datetime,
id = r.EntryId,
ip = r.Ip,
shiftDate = r.ShiftDate,
shiftNum = r.ShiftNum,
sort_order = a.SortOrder,
attachment_count = r.AttachmentCount,
attachments = SelectAttachmentsByEntryId(r.EntryId)
})
.OrderBy(r => r.sort_order)
.OrderBy(r => r.datetime)
.ToList();
return list;
}
public List<TurnReportAttachment> SelectAttachmentsByEntryId(int EntryId)
{
using (connection)
{
// we do it this way so that we don't return the blob
var results = from p in connection.TurnReportAttachments
where p.EntryId == EntryId
select new TurnReportAttachment
{
EntryId = p.EntryId,
AttachmentId = p.AttachmentId,
FileName = p.FileName
};
return results.ToList();
}
}
In your case SelectAttachmentsByEntryId should be static, with additional parameter connection. To make it work, it is needed to use ExpressionMethod.
public static class ReportHelpers
{
[ExpressionMethod(nameof(SelectAttachmentsByEntryIdImpl))]
public static List<TurnReportAttachment> SelectAttachmentsByEntryId(MyConnection connection, int EntryId)
{
throw new InvalidOperationException(); // should never enter here
}
private static Expression<Func<MyConnection, int, List<TurnReportAttachment>>> SelectAttachmentsByEntryIdImpl()
{
return (connection, EntryId) =>
(from p in connection.TurnReportAttachments
where p.EntryId == EntryId
select new TurnReportAttachment
{
EntryId = p.EntryId,
AttachmentId = p.AttachmentId,
FileName = p.FileName
})
.ToList();
}
}
Then you can use this method in queries:
public List<TurnReportItem> GetTurnReportsByDateShiftAndDept(DateTime shiftStart, int shiftNum, int dept)
{
using (connection)
{
var list = (from r in connection.VTurnReports
join a in connection.TurnReportAreas on r.AreaId equals a.AreaId
where a.DeptId == dept && a.Enabled && r.ShiftDate == shiftStart && r.ShiftNum == shiftNum
select new TurnReportItem
{
areaId = r.AreaId,
areaName = a.Name,
author = r.Author,
comment = r.Comment,
datetime = r.Datetime,
id = r.EntryId,
ip = r.Ip,
shiftDate = r.ShiftDate,
shiftNum = r.ShiftNum,
sort_order = a.SortOrder,
attachment_count = r.AttachmentCount,
attachments = ReportHelpers.SelectAttachmentsByEntryId(connection, r.EntryId)
})
.OrderBy(r => r.sort_order)
.ThenBy(r => r.datetime)
.ToList();
return list;
}
}
Note that OrderBy.OrderBy has no sense. It should be OrderBy.ThenBy

Problem returning entity grouped with LINQ in HTTP GET

What I'm doing wrong in this method below? I created a group with linq because I need to group the list by 2 columns and for this grouping I will have a list of files.
[HttpGet]
[Route("versions-by-period")]
public IActionResult GetVersionsByPeriodId(int entityId, int periodId)
{
var versionsInvoiceBillet = db.RemittanceInvoiceBilletVersionsCompacts
.Where(x => x.LegalEntityId == entityId && x.PeriodId == periodId && x.IsCurrent && x.DownloadHash != null)
.GroupBy(x => new { x.LifePolicyNumber, x.LegalEntityGroupNumber },
i => new { i.DownloadHash, i.FileTypeEnum, i.DueDate }, (key, group) => new
{
LifePolicyNumber = key.LifePolicyNumber,
LegalEntityGroupNumber = key.LegalEntityGroupNumber,
Files = group.ToList()
});
return Ok(versionsInvoiceBillet.Select(x => new {
lifePolicyNumber = x.LifePolicyNumber,
legalEntityGroupNumber = x.LegalEntityGroupNumber,
invoiceAndBillet = x.Files.Select(f => new {
downloadHash = f.DownloadHash,
fileTypeEnum = f.FileTypeEnum,
dueDatet = f.DueDate
})
}));
}
If I try to call this method with Postman, the body comes empty. The problem is in invoiceAndBillet information that is returned, if I change to below, the body comes filled.
return Ok(versionsInvoiceBillet.Select(x => new {
lifePolicyNumber = x.LifePolicyNumber,
legalEntityGroupNumber = x.LegalEntityGroupNumber,
invoiceAndBillet = x.Files.Select
}));
If I try to debug the selection that I'm trying to return, I get this message below:

Accessing C# Dictionary and return value

I have a dictionary:
Dictionary<ICD_Map2, string> maps = new Dictionary<ICD_Map2, string>();
I add to the dictionary via button click:
private void button2_Click(object sender, EventArgs e)
{
maps.Clear();
// Load mapping file.
var reader = new StreamReader(File.OpenRead(#"Call_Details_Map.csv"));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
maps.Add(new ICD_Map2(values[0].Replace("\"",""), values[1].Replace("\"","")), values[2].Replace("\"",""));
}
}
I want to use LINQ and map my keys to the "string" in maps.
How do I do it?
var File001 = from line in File.ReadLines(ICD_process)
let l = line.Split(',')
where l[0] != "\"Statement Date\""
select new
{
CallType = maps.ToLookup(p => l[5], p => l[3]),
Calls = l[11] == "\"\"" ? "0" : (maps.ToLookup(p => l[5], p => l[3]) == "Mobile Data" || maps.ToLookup(p => l[5], p => l[3]) == "Mobile SMS") ? "0" : l[11].Replace("\"","").ToString())
};
I am getting error in Calls variable in File001 Linq method
It's not clear what you are trying to achieve, but here is my advice. Now you are working with spitted lines array like this l[0] != "\"Statement Date\"". I think only you know what data should be at index 9. It's not very readable, error-prone (typo in Statemnet Date, wrong index), and it's very hard to maintain. Instead this create an object, which will parse line for you and provide data via strongly typed properties with nice names.
public class ICDEntry
{
public static ICDEntry CreateFrom(string line)
{
string[] values = line.Split(',');
var entry = new ICDEntry();
// assign values to properties:
// if (values[0] == "\"Statement Date\"")
// entry.StatementDate = DateTime.Parse(values[1]);
// entry.IsSomething = values[11] == "\"\""
return entry;
}
public DateTime? StatementDate { get; private set; }
public string MobileSMS { get; private set; }
public bool IsSomething { get; private set; }
}
Now you can parse each line, and then work in strongly typed world making queries to your ICD entries:
var entries = File.ReadLines(ICD_process).Select(l => ICDEntry.CreateFrom(l));
var File001 = from e in entries
where e.StatementDate.HasValue
select new {
Calls = e.IsSomething ? "0" : e.MobileSMS; // use ICDEntry here
};

Refactoring C# code - doing more within Linq

The code below is what I currently have and works fine. I feel that I could do more of the work I am doing in Linq instead of C# code.
Is there is anyone out there who can accomplish the same result with more Linq code and less C# code.
public List<Model.Question> GetSurveyQuestions(string type, int typeID)
{
using (eMTADataContext db = DataContextFactory.CreateContext())
{
List<Model.Question> questions = new List<Model.Question>();
List<Linq.Survey_Question> survey_questions;
List<Linq.Survey> surveys = db.Surveys
.Where(s => s.Type.Equals(type) && s.Type_ID.Equals(typeID))
.ToList();
if (surveys.Count > 0)
{
survey_questions = db.Survey_Questions
.Where(sq => sq.Survey_ID == surveys[0].ID).ToList();
foreach (Linq.Survey_Question sq in survey_questions)
{
Model.Question q = Mapper.ToBusinessObject(sq.Question);
q.Status = sq.Status;
questions.Add(q);
}
}
else
{
questions = null;
}
return questions;
}
}
Here is my Mapper function from my Entity to Biz Object
internal static Model.Question ToBusinessObject(Linq.Question q)
{
return new Model.Question
{
ID = q.ID,
Name = q.Name,
Text = q.Text,
Choices = ToBusinessObject(q.Question_Choices.ToList())
};
}
I want my mapper funciton to map the Question Status like so.
internal static Model.Question ToBusinessObject(Linq.Question q)
{
return new Model.Question
{
ID = q.ID,
Name = q.Name,
Text = q.Text,
Choices = ToBusinessObject(q.Question_Choices.ToList()),
Status = q.Survey_Questions[?].Status
};
}
? the issue is this function does not know which survey to pull the status from.
Instead of creating the biz object then setting the Status property in a foreach loop like so
foreach (Linq.Survey_Question sq in survey_questions)
{
Model.Question q = Mapper.ToBusinessObject(sq.Question);
q.Status = sq.Status;
questions.Add(q);
}
I would like to somehow filter the EntitySet<Survey_Question> in the q object above in the calling method, such that there would only be one item in the q.Survey_Questions[?] collection.
below is my database schema and business object schema
What I needed to do was setup a join.
public List<Model.Question> GetSurveyQuestions(string type, int typeID)
{
using (eMTADataContext db = DataContextFactory.CreateContext())
{
return db.Survey_Questions
.Where(s => s.Survey.Type.Equals(type) && s.Survey.Type_ID.Equals(typeID))
.Join(db.Questions,
sq => sq.Question_ID,
q => q.ID,
(sq, q) => Mapper.ToBusinessObject(q, sq.Status)).ToList();
}
}
And then overload my Mapper Function
internal static Model.Question ToBusinessObject(Linq.Question q, string status)
{
return new Model.Question
{
ID = q.ID,
Name = q.Name,
Text = q.Text,
Status = status,
Choices = ToBusinessObject(q.Question_Choices.ToList()),
};
}
from question in db.Survey_Questions
let surveys = (from s in db.Surveys
where string.Equals(s.Type, type, StringComparison.InvariantCultureIgnoreCase) &&
s.Type_ID == typeID)
where surveys.Any() &&
surveys.Contains(s => s.ID == question.ID)
select new Mapper.Question
{
ID = question.Id,
Name = question.Name,
Text = question.Text,
Choices = ToBusinessObject(question.Question_Choices.ToList()),
Status = question.Status
}
Does that get you on the right track?
Why are you duplicating all your classes? You could just extend the LINQ to SQL classes with your business logic - they are partial classes. This is somewhat against the purpose of an OR mapper - persisting business entities.

Categories

Resources