Proper way to handle NULL's with IQueryable<> - c#

I have been trying to research best practices for handling NULL's, specifically using IQueryable when dealing with result sets from something like SQL Server using Dapper.
Take this for example:
static void Main(string[] args)
{
List<User> userList = new List<User>();
userList.Add(new User(1, "John", "Doe", "555-5555"));
userList.Add(new User(2, "Jane", "Doe", "123-4567"));
userList.Add(new User(3, "Fred", "Smith", null));
//output list
DisplayList(userList);
//query
var filteredList = userList.AsQueryable().Where(ul => ul.PhoneNumber.Contains("555")); //throws an exception!
//output filtered list
DisplayList(filteredList);
Console.WriteLine("Done");
Console.ReadKey();
}
static void DisplayList(IEnumerable<User> users)
{
foreach (var u in users)
{
Console.WriteLine(u.UserID + " | " + u.FirstName + " | " + u.LastName + " | " + u.PhoneNumber);
Console.WriteLine("-------------------------------------");
}
}
}
public class User
{
public User(int UserID, string FirstName, string LastName, string PhoneNumber)
{
this.UserID = UserID;
this.FirstName = FirstName;
this.LastName = LastName;
this.PhoneNumber = PhoneNumber;
}
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
}
I know that this will return a null reference exception and I can resolve it by changing the null phone number to a blank string, but my question that I am struggling with is how I should best handle these. While I could ideally not return any nulls from the database, doing something like an ISNULL check on each field seems rather heavy handed. Is there a simple solution I am overlooking?

Related

Inner join with two select clauses LinQ MVC

Query result from search
Greetings, i am new using linq syntax and i need help translating the query in the picture to get the needed result in c#. I have two questions. First of all How do i do inner joins using linq syntax in c# in order to get the desired result showed in the image. Second, in order to show the data obtained from the query, do i need to create a ViewModel that has 3 ViewModels from the different tables used in the query search?
Thank you so very much for your help.
As levelonehuman said, linq is designed to query data. lets say you have a couple classes:
public class Person
{
public static class Factory
{
private static int currentId = 0;
public static Person Create(string firstName, string lastName, string phoneNumber, int companyId)
{
return new Person()
{
Id = ++currentId,
FirstName = firstName,
LastName = lastName,
PhoneNumber = phoneNumber,
CompanyId = companyId
};
}
}
public int Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string PhoneNumber { get; private set; }
public int CompanyId { get; private set; }
}
public class Company
{
public static class Factory
{
private static int companyId=0;
public static Company Create(string name, string city, string state, string phoneNumber)
{
return new Company()
{
Id = ++ companyId,
City = city,
State = state,
Name = name,
PhoneNumber = phoneNumber
};
}
}
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PhoneNumber { get; set; }
}
and then you want to see only people from a certain area code you could do something like this:
class Program
{
static void Main(string[] args)
{
var companies = new[]
{
Company.Factory.Create("ABC", "Indianapolis", "In", "(317) 333 5555"),
Company.Factory.Create("Def", "Bloominton", "In", "(812) 333 5555"),
};
var people = new[]
{
Person.Factory.Create("Jane", "Doe", "(317) 555 7565", 1),
Person.Factory.Create("Paul", "Smith", "(812) 555 7565", 2),
Person.Factory.Create("Sean", "Jackson", "(317) 555 7565", 2),
Person.Factory.Create("Jenny", "Gump", "(812) 555 7565", 1)
};
var peopleFromIndianapolis =
(
from company in companies
join person in people on company.Id equals person.CompanyId
where person.PhoneNumber.StartsWith("(317)")
orderby person.LastName, person.FirstName
select new
{
person.FirstName,
person.LastName,
company.Name
}
).ToList();
foreach (var person in peopleFromIndianapolis)
{
Console.WriteLine($"PersonName: {person.LastName}, {person.FirstName} - Company:{person.Name}");
}
}
}
Hope this helps!

Using Dictionary Parameters as Optional Arguments when Calling Method

I'm presently trying to use a dictionary values to name optional parameters when invoking a method. I'm not sure this is possible with c# but I do something similar with queries using dynamic SQL.
string[] dobArrayKey = {"dob: "};
string[] dobArrayValue = {txtDob.Text};
string[] ptntNumArrayKey = { "PatientID: " };
string[] ptntNumArrayValue = { txtOfficeMR.Text};
string[] nameArrayKey = { "FirstName: ", "LastName: " };
string[] nameArrayValue = { txtFirstname.Text, txtLastname.Text };
List<List<string>> searchResults = new List<List<string>>();
Dictionary<string[], string[]> searchCriteria = new Dictionary<string[], string[]>
{
{dobArrayKey,dobArrayValue}
,{ptntNumArrayKey,ptntNumArrayValue}
,{nameArrayKey,nameArrayValue}
};
foreach (var item in searchCriteria)
{
if (item.Value[0] != "" && item.Value[0] != null)
{
searchResults.Add(new List<string>());
for (int x = 0; x <= item.Key.Count(); x++)
{
string strJSON = doPatientSearch(Convert.ToInt32(au.UserID)
, Convert.ToInt32(Session["PracticeID"]), au.SessionID, item.Key[x].ToString() : item.Value[x].ToString() );
PatientSearchResponse ptLi = JsonConvert.DeserializeObject<PatientSearchResponse>(json2);
foreach (PatientList3 patient in ptLi.PatientList)
{
searchResults[x].Add(patient.PatientNumber);
}
}
}
}
public static string doPatientSearch(int UserID, int PracticeID, string SessionID, string PatientID = null,
,string first = null, string last = null, string dob = null, string social = null)
{
//search
}
My colleague suggested I change the method itself by removing all of the optional parameters and instead passing through a dictionary that contains all of the parameters and handling them inside the method.
I think that would work, but for curiosities sake I wanted to get some feedback and find out whether or not something like I'm attempting to do in the above code is possible.
If it is impossible but there is another way of achieving the desired outcome I'd love to see your suggestions.
Thank you in advance.
Pass an expression
Since the criteria are used post-hoc (i.e. by filtering a complete resultset), you can use LINQ to filter the results. For maximum flexibility, the caller can pass in an Expression to be used as a callback on each item to determine if it should be included.
To get a filtered resultset:
public IEnumerable<Patient> FindPatients(Func<Patient,bool> criteria)
{
return sourceData
.Where (criteria);
}
To return a single result:
public Patient FindPatient(Func<Patient,bool> criteria)
{
return sourceData
.Single(criteria);
}
The criteria expression is just a function that accepts a patient and returns a Boolean. The caller can write this any way desired, or insert it as a lambda expression.
var results = patients.FindPatients( p => p.LastName == "Doe" );
Or
var results = patients.FindPatients
(
p =>
p.LastName.Contains("Doe") &&
p.PracticeID == 12
);
Or
var singleResult = patients.FindPatient( p => p.UserID == 1);
As you can see, the caller can provide literally any criteria desired, and has the benefit of type safety and early binding. This is far superior to using a Dictionary which has neither.
Full example code:
class Patient
{
public int UserID { get; set; }
public int PracticeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DOB { get; set; }
public string Social { get; set; }
public override string ToString()
{
return string.Format("{0} {1} {2}", UserID, FirstName, LastName);
}
}
class PatientRepository
{
static private readonly List<Patient> sourceData = new List<Patient>
{
new Patient
{
UserID = 1, PracticeID = 10, FirstName = "John", LastName = "Doe", DOB = DateTime.Parse("1/2/1968"), Social="123456789"
},
new Patient
{
UserID = 2, PracticeID = 10, FirstName = "Jane", LastName = "Doe", DOB = DateTime.Parse("1/2/1958"), Social="123456790"
},
new Patient
{
UserID = 3, PracticeID = 10, FirstName = "John", LastName = "Carson", DOB = DateTime.Parse("4/1/1938"), Social="123456791"
}
};
public IEnumerable<Patient> FindPatients(Func<Patient,bool> criteria)
{
return sourceData
.Where (criteria);
}
public Patient FindPatient(Func<Patient,bool> criteria)
{
return sourceData
.Single(criteria);
}
}
public class Program
{
public static void Main()
{
//Get a reference to the data store
var patients = new PatientRepository();
Console.WriteLine("Multiple record search");
var results = patients.FindPatients
(
p => p.LastName == "Doe"
);
foreach (var p in results)
{
Console.WriteLine(p);
}
Console.WriteLine("Single record search");
var singleResult = patients.FindPatient
(
p => p.UserID == 1
);
Console.WriteLine(singleResult);
}
}
Output:
Multiple record search
1 John Doe
2 Jane Doe
Single record search
1 John Doe
See the working code on DotNetFiddle

Is it possible to split a full name selected in a dropdown list and save it into 2 separate columns as first name and last name?

I have the following columns in Entity Framework Code first approach:
public string LastName { get; set; }
public string FirstName { get; set; }
And I combine them into a full name:
public string FullName
{
get { return LastName + " " + FirstName; }
}
I don't even know if it's possible, but how I can generate a setter for this in order to send the values from the fullname to the other 2 columns when selecting it from e.g a dropdown list?
Say you would do a FullName.Split(' '); getting an array of names. It's all good when first and last names are single word. But how about John Billy Doe? Where does the LastName end and the FirstName begin?
Instead, you could use a different separator, like a comma: John, Billy Doe.
That way, doing a FullName.Split(','); would yield the correct Last Name and First Name.
public string FullName
{
get
{
return LastName + ", " + FirstName;
}
set
{
string[] names = value.Split(", ");
LastName = names[0];
FirstName = names[1];
}
}
EDIT: Of course, some validation is required for the value, but it's pretty hard to type code on the Android app (as I am doing). So, unless you need help with that, I leave it up to you.
The simpler approach would be to split on the first space you see, but it would break if anyone has a first or last name with a space in it. Broken first-names are rare, but last-names with spaces are common, e.g. "Jean-Claude Van Damme".
public String FullName {
set {
Int32 spaceIdx = value.IndexOf(" ");
if( spaceIdx == -1 ) {
this.FirstName = value;
this.LastName = "";
}
else
{
this.FirstName = value.Substring(0, spaceIdx);
this.LastName = value.Substring(spaceIdx + 1);
}
}
}
A better approach is to identify the names uniquely in your system and use that from your name-dropdown. Assuming this is ASP.NET MVC:
public class YourViewModel {
public IEnumerable<SelectListItem> Names { get; set; }
public Int32 SelectedNameByPersonId { get; set; }
}
In your controller:
public IActionResult YourAction() {
List<SelectListItem> names = db.People.Select( dbPerson => new SelectListItem() {
Text = dbPerson.FirstName + " " + dbPerson.LastName,
Value = dbPerson.Id.ToString()
} ).ToList(); // ToList so the DB is only queried once
return this.View( new YourViewModel() { Names = names } );
}
[HttpPost]
public IActionResult YourAction(YourViewModel model) {
DBPerson dbPerson = db.People.GetPerson( model.SelectedNameByPersonId );
// do stuff with person
}
In your view (aspx syntax):
<%= Html.DropDownFor( m => m.SelectNameByPersonId, this.Model.Names ) %>
I'm not exactly sure what you are asking, but I think this might help(plus input error handling).
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName
{
get
{
return LastName + " " FirstName;
}
series
{
var nameParts = value.Split(' ');
LastName = string.IsNullOrEmpty(nameParts[0]) ? string.Empty : nameParts[0];
FirstName = string.IsNullOrEmpty(nameParts[1]) ? string.Empty : nameParts[1];
}
}
I have tried for some workaround. Please try this and let me know of it works.
public string LastName { get; set; }
public string FirstName { get; set; }
[NotMapped] //This will make sure that entity framework will not map this to a new column
public string FullName
{
get { return LastName + " " + FirstName; }
// Here comes your desired setter method.
set
{
string[] str = value.Split(' ');
if (str.Length == 2)
{
FirstName = str[1];
LastName = str[0];
}
else if (str.Length >= 0 && value.Length > 0)
{
LastName = str[0];
}
else
throw new Exception("Invalid name");
}
}

How to show only part of text in CheckedListBox

I'm creating an WindowsForms application that is using a list of persons with 4 parameters (ID, Name, Surname, Permissions):
public List<Osoba> ListaOsoba()
{
Osoba nr1 = new Osoba(1, "Name", "Surname", Permissions.Administrator);
Osoba nr2 = new Osoba(2, "Name2", "Surname2", Permissions.Użytkownik);
Osoba nr3 = new Osoba(3, "Name3", "Surname3", Permissions.Użytkownik);
listaOsób.Add(nr1);
listaOsób.Add(nr2);
listaOsób.Add(nr3);
return listaOsób;
}
I would like to post all those Parameters to CheckedListBox, but show only name and surname to the user. The ID and Permissions should be hidden, but they need to exist, because I want to use them later.
Every help will be appreciated.
public static bool CheckBoxListPopulate(CheckBoxList CbList, IList<T> liSource, string TextFiled, string ValueField)
{
try
{
CbList.Items.Clear();
if (liSource.Count > 0)
{
CbList.DataSource = liSource;
CbList.DataTextField = TextFiled;
CbList.DataValueField = ValueField;
CbList.DataBind();
return true;
}
else { return false; }
}
catch (Exception ex)
{ throw ex; }
finally
{
}
}
here Cb list is the control name and
List item Ilist is the list source name
Text field (should be concatination ) ="Name" + "Surname"
Value field will be Hidden it can be "1,2,3"
so only Text field will be visible to user
To bind only name and surname to checkedboxlist first store name and surname together and then try this:
NameS = "Name" + "Surname";
((ListBox)checkedListBox).DataSource = listaOsób;
((ListBox)checkedListBox).DisplayMember = "NameS";
try this, here you have to make arbitrary compound properties for display and value member like DisplayName and HiddenId and then you can easily bound with checkedlistbox.
public class Osoba
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Permissions Permission { get; set; }
public string DisplayName { get; set; }
public string HiddenId { get; set; }
public Osoba()
{ }
public Osoba(int id, string fname, string lname, Permissions p)
{
Id = id;
FirstName = fname;
LastName = lname;
Permission = p;
DisplayName = FirstName + " " + LastName;
HiddenId = Id + "_" + Permission.GetHashCode();
}
public void ListaOsoba()
{
List<Osoba> objList = new List<Osoba>();
Osoba nr1 = new Osoba(1, "Name", "Surname", Permissions.Administrator);
Osoba nr2 = new Osoba(2, "Name2", "Surname2", Permissions.Uzytkownik);
Osoba nr3 = new Osoba(3, "Name3", "Surname3", Permissions.Uzytkownik);
objList.Add(nr1);
objList.Add(nr2);
objList.Add(nr3);
((ListBox)checkedListBox1).DataSource = objList;
((ListBox)checkedListBox1).DisplayMember = "DisplayName";
((ListBox)checkedListBox1).ValueMember = "HiddenId";
MessageBox.Show(((ListBox)checkedListBox1).Text);
MessageBox.Show(((ListBox)checkedListBox1).SelectedValue.ToString());
}
}
public enum Permissions
{
Administrator,
Uzytkownik
}
I had a similar thing with SQL. I returned many columns, but only wanted one to show.
Anyway
ArrayList arr = new ArrayList();
foreach (object o in ListaOsoba)
{
arr.Items.Add(o[1].ToString()+" "+o[2].ToString());
}
foreach (var item in arr)
{
chkNames.Items.Add(arr.ToString()); //chkNames is your CheckListBox
}
Then later when querying which ID and such goes where, loop through you original list, and see who was ticked based on the name and surname combo, find the ID related to that person and you should be sorted

C# | Linq | SubSonic - Class object

I have this class/object below:
public class Person
{
public string FirstName;
public string MI;
public string LastName;
}
Person p=new Person();
p.FirstName = "Jeff";
p.MI = "A";
p.LastName = "Price";
Is there any built in in linq or c# or in subsonic that will create an output of this?:
string myString = "FirstName=\"Jeff\" p.MI=\"A\" p.LastName=\"Price\"";
It seems you need a ToString overload in Person. Also, don't expose public fields like that. Use properties.
public class Person
{
public string FirstName { get; set; }
public string MI { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return "FirstName=\"" + FirstName + "\" p.MI=\"" + MI + "\" p.LastName=\"" + LastName + "\"";
}
}
(edit)
Here's your request (but it requires properties):
public static class ObjectPrettyPrint
{
public static string ToString(object obj)
{
Type type = obj.GetType();
PropertyInfo[] props = type.GetProperties();
StringBuilder sb = new StringBuilder();
foreach (var prop in props)
{
sb.Append(prop.Name);
sb.Append("=\"");
sb.Append(prop.GetValue(obj, null));
sb.Append("\" ");
}
return sb.ToString();
}
}
Usage:
Console.WriteLine(ObjectPrettyPrint.ToString(new Person { FirstName, = "A", MI = "B", LastName = "C" }));
Well, as for LINQ and C#, not by default.
However, in the Person class you can override the ToString() event to do it for you.
public override string ToString()
{
return string.Format("p.Firstname={0} p.MI={1} p.LastName={2}", FirstName, MI, LastName);
}
And then you would just call it as follows:
string myString = p.ToString();
Which will give you the output you are looking for.

Categories

Resources