C# LINQ - Loop through list to get Data - linq

Would you please show me how to put a comma delimited string with email addresses (abc#aa.com, def#ab.com, ghi#ac.com,...) into LINQ list then loop through each email address and go to SQL database to get name, address, phone number and return these back. EmailList below contains abc#aa.com, def#ab.com, ghi#ac.com,..
public class PersonalInfo
{
public string firstname {get; set;}
public string lastname {get; set;}
public string address {get; set;}
public string phonenumber {get; set;}
}
public PersonalInfo GetInfo(string EmailList)
{
....
}
I can do the part to get info from SQL table. Just need help to loop through EmailList, call method to get data then return the data back to PersonalInfo.

When you have a string that you want to break apart on a particular character, you can use the Split() method. The caveat is that if you don't have a clear delimiting character you might split something you don't intend to. The comma is not commonly found in email addresses so this should be fine. The Split() method will return an array of strings.
The next caveat is that "abc#aa.com, def#ab.com" will leave a space character before the "def" of the second email address. To remove these you can use the Trim() method of a string. Trim() will remove whitespace from the start and end of a string. Trim() is non-destructive so you need to store the results.
The below method uses split to get an array with each entry being an email address. Then the Select() linq method is used to iterate each email address and return the Trim() result. Now the emails variable is an IEnumerable<string> with each string being an email that can be used to do the database lookup. I changed the return type on the Function to be a List<PersonalInfo> as I assume you are wanting to look up each person and return all results.
public List<PersonalInfo> GetInfo(string EmailList)
{
List<PersonalInfo> output = new();
var emails = EmailLIst.Split(',').Select(x => x.Trim());
foreach (var email in emails)
{
// table lookup
PersonalInfo info = GetFromTable(email);
output.Add(info);
}
return output;
}

Related

Use LINQ to pull data where rows match other rows

I'm pretty new to LINQ, but I'm trying to find a fast way to take a set of data and pull out only rows where particular columns have duplicates in other rows. E.g. in a set of people, pull out only people who share a phone number with another person. Here's a breakdown of what I'm up to:
public class Person
{
public int Id { get; set; }
public string AddressLine1 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public int Province { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Fax { get; set; }
public string Web { get; set; }
}
And then I want to sort them in different ways to look for possible duplicates in my input values, so if I want to find where Address Line 1 and Postal Code match, I can sort it like so:
IOrderedEnumerable<Person> sortedPeople = people.OrderBy(x => x.AddressLine1).ThenByDescending(x => x.PostalCode);
And then just go through and bundle any matches together, but there will be a lot of data that doesn't match anything, so if I can cull it out in the first place, it could potentially save a lot of time.
I have a suspicion it will end up costing me more time than it would save, but I figured I'd ask if there's an efficient way.
Your sorting lines won't find duplicates. If you want to find duplicates, you need to make groups of Persons that share something, for instance Persons that have the same PostalCode, or that live in the same City.
Making groups of items that share something is done by using one of the overloads of Enumerable.GroupBy. The most important parameter of GroupBy is parameter keySelector. With this parameter you say what should be the common value for all Persons in the group.
The following will give you a sequence of Groups of Persons. All Persons in the group have the same City. The group is identified by the Key, which has the value of the common element. So you have a Group with all Parisians, with key "Paris"; Another group contains all Amsterdammers with key "Amsterdam", etc
var result = persons.GroupBy(person => person.City);
However, you don't want to keep all groups, you only want to keep groups that have more than one member: those are the groups that have duplicates.
var duplicateCitizens = persons.GroupBy(person => person.City)
.Where(group => group.Skip(1).Any());
In words: first make groups of persons that live in the same City. Then from every group, keep only those groups that have more than one element (= if you skip one element, there are still elements left).
I use the Skip(1).Any() method for efficiency reasons. If you already know after the 1st element that there are duplicates, why continue counting all hundred elements?
The result is a sequence of Groups of more than one Person. All Persons in the group live in the same city. The Key of the group is the City that they have in common.
You can group the results by the phone number:
var query = Persons.GroupBy(p => p.Phone)
.Where(p => p.Count() > 1)
.ToList();
After this you have a grouped list with the phone number as key and the persons with the matching number if this number is more than one time assigned.

Web Api 2 empty string parameter GET

i have setup the route to be:
[Route("{id}/users/search/{search}")]
and the associated action is:
SomeAction(int id, string text)
The service has the following function.
for the resource with id={id} and the users of this resource get the users that match with the {search} term (username, email etc).
the {search} can have a value so the service returns only the matching entities or does not have a value (empty string or null) so the service returns everything.
For the part with a value it works fine.
For the second part i cannot find something to set the get request that matches the empty string.
i tried the following:
1/users/search/null {search} = "null"
1/users/search/ does not match route
1/users/search does not match route
has anyone a hint how this could be done?
Update: i have tried to replace the action:
SomeAction(int id, string text)
with:
SomeAction(Model model) where model is
public class ApplicationUserSearchModel
{
[Required]
public int Id { get; set; }
[Required(AllowEmptyStrings = true)]
public string MatchText { get; set; }
}
with no luck since i don't know what to send in order to match the url to this.
You should tag your search parameter with ? to mark it as optional in the route, and set it to null by default in your action.
[Route("{id}/users/search/{search?}")]
public HttpResponseMessage Search(int id, string search = null)
I originally thought the route/action parameter names were the issue, but I was incorrect. Here is the previous answer:
The parameter names in your route definition and your action don't match, which is causing your problem.
[Route("{id}/users/search/{search}")]
public HttpResponseMessage Search(int id, string text)
You should update the string parameter in your action from text to search, to match the parameter name in your Route attribute.

MVC3 How To Phone Validation for model structure?

In my model structure PhoneNumber is not required but if user want to enter a value, it must be entered 10 digits.
I tried
[StringLength(10, MinimumLength = 10, ErrorMessage = "Girdiğiniz numara 10 karakter uzunluğunda olmalı")]
but it doesn't allow empty entry.
Is there anyone have an idea?
Use the Regular Expression Validator and then find/write a regular expression that validates the phone number. For example in the USA:
public class MyRegularExpressions
{
public const string USPhone = #"^[2-9]\\d{2}-\\d{3}-\\d{4}$|^[2-9]\\d{2}\\d{3}\\d{4}$";
}
And then the Atribute used in your model is:
[RegularExpression(MyRegularExpressions.USPhone)]
public string PhoneNumber { get; set; }
This way it is not required but when something is entered it has to match the specified regular expression.
If you need to write your own regular expression one of the best sites out there is: http://www.regexr.com/

How to making Display Template Work Correctly

I am trying to learn MVC. I had a very simple requirement that I wanted to insert commas in number displayed in a table. This is what I have.
Model
public partial class PACKAGE
{
public string PACKAGEID { get; set; }
[UIHint("withcommas")]
public Nullable<long> FILESIZE { get; set; }
}
Views\Shared\DisplayTemplates\withcommas.cshtml
#Model withcommas
#{
<span>[#Model.ToString("#,##0")]</span>
}
I did want to add the sqare brackets around it.
Views\Home\Packages.cshtml
#Html.DisplayFor(modelItem => item.FILESIZE)
What I am getting on the display is "2287097 withcommas [2,287,097];"
I do not understand why the original number as well as name of the hint is showing up in addition to the formatted number. I do not understand why there is an extra semicolon at the end.

Dynamic Linq Search Expression on Navigation Properties

We are building dynamic search expressions using the Dynamic Linq library. We have run into an issue with how to construct a lamba expression using the dynamic linq library for navigation properties that have a one to many relationship.
We have the following that we are using with a contains statement-
Person.Names.Select(FamilyName).FirstOrDefault()
It works but there are two problems.
It of course only selects the FirstOrDefault() name. We want it to use all the names for each person.
If there are no names for a person the Select throws an exception.
It is not that difficult with a regular query because we can do two from statements, but the lambda expression is more challenging.
Any recommendations would be appreciated.
EDIT-
Additional code information...a non dynamic linq expression would look something like this.
var results = persons.Where(p => p.Names.Select(n => n.FamilyName).FirstOrDefault().Contains("Smith")).ToList();
and the class looks like the following-
public class Person
{
public bool IsActive { get; set;}
public virtual ICollection<Name> Names {get; set;}
}
public class Name
{
public string GivenName { get; set; }
public string FamilyName { get; set; }
public virtual Person Person { get; set;}
}
We hashed it out and made it, but it was quite challenging. Below are the various methods on how we progressed to the final result. Now we just have to rethink how our SearchExpression class is built...but that is another story.
1. Equivalent Query Syntax
var results = from person in persons
from name in person.names
where name.FamilyName.Contains("Smith")
select person;
2. Equivalent Lambda Syntax
var results = persons.SelectMany(person => person.Names)
.Where(name => name.FamilyName.Contains("Smith"))
.Select(personName => personName.Person);
3. Equivalent Lambda Syntax with Dynamic Linq
var results = persons.AsQueryable().SelectMany("Names")
.Where("FamilyName.Contains(#0)", "Smith")
.Select("Person");
Notes - You will have to add a Contains method to the Dynamic Linq library.
EDIT - Alternatively use just a select...much more simple...but it require the Contains method addition as noted above.
var results = persons.AsQueryable().Where("Names.Select(FamilyName)
.Contains(#0", "Smith)
We originally tried this, but ran into the dreaded 'No applicable aggregate method Contains exists.' error. I a round about way we resolved the problem when trying to get the SelectMany working...therefore just went back to the Select method.

Resources