Override ToString of a Null Object? C# asp.net - c#

I have a List<> of say People, and people have an instance of a Thought object, I display People in a gridview, there is one column for each variable of the People Class and one for Thoughts, which displays the Thought's class Tostring Override, when I delete a thought, if it was used by any given "People" It would set that People's thought to "null" but when I try to retrieve that "null" from the db (ADO.NET) I get an error (which I think doesn't matter atm), how can I display a "NULL" on the gridview of People in which thoughts have been deleted?
EDIT:
Here is the method I use to save the DATASET:
public List<People> PeopleList()
{
ControllerClass cont = new ControllerClass();
List<People> list = new List<People>();
string strSQL = "SELECT * FROM People";
DataSet data = Select(strSQL);
if (data.Tables[0].Rows.Count > 0)
{
foreach (DataRow Row in data.Tables[0].Rows)
{
People p = new People();
p.IdPeople= Convert.ToInt32(Row.ItemArray[0].ToString());
p.NamePeople= Row.ItemArray[1].ToString();
p.ThoughtPeople= cont.SearchThought(Convert.ToInt32(Row.ItemArray[3].ToString()));
list.Add(p);
}
}
return list;
}
I use a method to search for thoughts because I save the thought's ID as a foreign key

Handle it like
p.NamePeople= Row.ItemArray[1] == DBNull.Value ? "NULL" : Row.ItemArray[1].ToString():

Related

Generalized DTO population method with different query select lists

For reasons that I do not quite understand, I have chosen not to use an ORM Framework and have gone with a generalized ADO.NET data access layer. I initially created a single database class from which all my controllers had access. As anyone but myself could have predicted, this access object has become a monstrosity.
In an attempt to refactor my data layer, I have created a 'database adapter' class as a DI injected service and have created a 'service layer' to utilize it. So each controller now has a 'domain service' that will use the database adapter to query the database and return a generic data table. The service will then populate the result of the queries and return the domain objects back to the controller where it can assemble the view models.
I am running into an issue where I cannot seem to abstract the code designed to map the DataSets returned from the database access layer because each query may select different fields. For example, a simple reference data service:
public class ReferenceDataService : IReferenceDataService
{
private IDatabaseAdapter _dbAdapter;
public ReferenceDataService(IDatabaseAdapter dbAdapter)
{
_dbAdapter = dbAdapter;
}
public IEnumerable<ReferenceData> GetReferenceData(string table)
{
List<ReferenceData> rdList = new List<ReferenceData>();
StringBuilder sb = new StringBuilder();
sb.Append("SELECT [CODE], [LABEL] FROM [dbo].");
sb.Append(table);
sb.Append(" WHERE END_DATETIME > GETDATE()");
DataSet ds = _dbAdapter.ExecuteDataSet(sb.ToString(), null);
foreach (DataRow row in ds.Tables[0].Rows)
{
rdList.Add(PopulateRecord(row));
}
return rdList;
}
private ReferenceData PopulateRecord(DataRow row)
{
return new ReferenceData
{
ReferenceId = (int)row["REFERENCE_ID"],
Code = (string)row["CODE"],
Label = (string)row["LABEL"],
Description = (string)row["DESCRIPTION"],
BeginDatetime = (DateTime)row["BEGIN_DATETIME"],
EndDatetime = (DateTime)row["END_DATETIME"],
UpdatedBy = (string)row["UPDATED_BY"],
UpdatedOn = (DateTime)row["UPDATED_ON"],
CreatedBy = (string)row["CREATED_BY"],
CreatedOn = (DateTime)row["CREATED_ON"]
};
}
}
In this example, I have an exception thrown from the populate method, because as you can see, I am only selecting code and label for this particular method. I'd like to avoid a custom mapping for every method but I also do not want to needlessly return ALL the data from each table row to the controller. I'd like to keep the populate method generic so that any query against that table will be mapped appropriately.
I realize I'm basically almost rolling my own ORM, but I'd like to use a service pattern without it because at this point I am way too invested.
After some digging around, it appears there was a very obvious and straightforward solution that I had been missing. The DataRow instance object has the ability to check it's parent table columns for existence. By wrapping each assignment from the table row in one of these checks, then the population method will not care what was actually selected into the DataTable and will be able to populate an object regardless of the amount of data returned from the query.
So in my example, if I want to keep a generic population method for ReferenceData but use a query that only retuns the CODE and LABEL columns, the following change would keep the population of the returned business object agnostic and error free:
private ReferenceData PopulateRecord(DataRow row)
{
return new ReferenceData
{
ReferenceId = row.Table.Columns.Contains("REFERENCE_ID") ? (int)row["REFERENCE_ID"] : default(int),
Code = row.Table.Columns.Contains("CODE") ? (string)row["CODE"] : default(string),
Label = row.Table.Columns.Contains("LABEL") ? (string)row["LABEL"] : default(string),
Description = row.Table.Columns.Contains("DESCRIPTION") ? (string)row["DESCRIPTION"] : default(string),
BeginDatetime = row.Table.Columns.Contains("BEGIN_DATETIME") ? (DateTime)row["BEGIN_DATETIME"] : default(DateTime),
EndDatetime = row.Table.Columns.Contains("END_DATETIME") ? (DateTime)row["END_DATETIME"] : default(DateTime),
UpdatedBy = row.Table.Columns.Contains("UPDATED_BY") ? (string)row["UPDATED_BY"] : default(string),
UpdatedOn = row.Table.Columns.Contains("UPDATED_ON") ? (DateTime)row["UPDATED_ON"] : default(DateTime),
CreatedBy = row.Table.Columns.Contains("CREATED_BY") ? (string)row["CREATED_BY"] : default(string),
CreatedOn = row.Table.Columns.Contains("CREATED_ON") ? (DateTime)row["CREATED_ON"] : default(DateTime)
};
}
This would allow me to use PopulateRecord on a select statement that only returned CODE and LABEL (as I would want to do if I was populating a SelectItemList for a dropdown for example).
I do not know what kind of performance hit this may or may not incur so that is something to possibly consider. But this allows for the flexibility I was looking for. I hope this post will help someone else who might be looking for the same type of solution.
If there are better ways to approach this please let me know. Thanks!

Iterate over Database table row and store values in a Dictionary?

I want to implement a Dictionary cache in my program. How can I store the database result seen in the image below in a Dictionary Collection ?
I want to iterate over the Database table and store the content of LanguageName and IsoCode columns in a Dictionary like this Dictionary<LanguageName,IsoCode>.
My database (ctlang) looks like this:
Here is my code:
private string GetLanguageForIsoCode(string isoCode)
{
//check the isocode column and return the corresponding language
using (var unitOfWork = dataAccessUnitOfWorkFactory.Create())
{
//need to call every time the sql query
string query = "SELECT languagename FROM ctlang WHERE isocode='" + isoCode + "'";
List<string> result = unitOfWork.OwEntities.Database.SqlQuery<string>(query).ToList();
if (result.FirstOrDefault() != null)
{
return result.FirstOrDefault();
}
//if language not available in Database, fallback to German as default language
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("de");
//displayName = Deutsch
return CultureInfo.GetCultureInfo("de").NativeName;
}
}
Bonus question: How can I search for a key in a dictionary using the value ?
It is. There is no problem in returning a key by its value, the only issue would be that there could theoretically be more than one key assigned to that value, since the key is what matters. But in your particular case that should be no problem, since only one isocode represents one language. So there would be no problem to make it like that, with dictionary:
public Dictionary<string, string> languagesAndKeys = new Dictionary<string, string>(); //Create it
Then you can search for keys/values like that:
string myValueByKey = languagesAndKeys["myKey"]; //getting value by key is easy
string myKeyByValue = languagesAndKeys.FirstOrDefault(item => item.Value == "myValue").Key; //getting the key of the FIRST matching value/or returning the default type. You'll need a check to be sure.
Afterwards, you can easily load the data from the sql table into the dictionary. For this goal, you can either use a temporary datatable with dataadapter, which will work well as long as the table is not that big, or you can use a DataReader to loop trough rows in the sql table one by one. I'm gonna be using a temp DataTable:
string cmdText = "SELECT * FROM ctlang"; //As far as I saw your cmd text in the code example, you may still want to take a look tho
string connectionString = ""; //fill the connection string according to your SQL server data
SqlDataAdapter dataAdapter = new SqlDataAdapter(cmdText, connectionString);
DataTable dTable = new DataTable();
dataAdapter.Fill(dTable);
foreach(DataRow row in dTable.Rows)
languagesAndKeys.Add(row[1].ToString(), row[0].ToString());//second column as a key, first column as a value - just like the structure of your table.
This is how I solved the problem. In my Database the Table with the languages is named CTLANG and the columns are LANGUAGENAME and ISOCODE. I wanted to map these to into a Dictionary collection. So that at the end the dictionary looks like this: ["LanguageName","IsoCode"].
private static Dictionary<string, string> languageToIsoCode; //dictionary cache
private void InitializeLanguageCacheDictionary()
{
using (var unitOfWork = dataAccessUnitOfWorkFactory.Create())
{
languageToIsoCode = (from p in unitOfWork.OwEntities.CTLANG
select new {p.LANGUAGENAME, p.ISOCODE}).ToDictionary(p => p.LANGUAGENAME, p => p.ISOCODE);
}
}
Thanks to #D.Petrov I also found a way to search for the key in a dictionary and give its value back.
And this is how I optimized my method to use the dictionary cache.
private string GetLanguageForIsoCode(string isoCode)
{
if (languageToIsoCode == null)//if cache empty initialize it
{
InitializeLanguageCacheDictionary();
}
//searches inside the dictionary, look for value and then return key
//might be bad if there are more than one value asigned to a key
//because value does not habe to be unique
string languageFromIsoCodeFromCache = languageToIsoCode.FirstOrDefault(item => item.Value == isoCode).Key;
if (languageFromIsoCodeFromCache == null)
{
//fallback and use the German language as default
return CultureInfo.GetCultureInfo("de").NativeName;
}
return languageFromIsoCodeFromCache;
}

Combining Data from class into a DataTable using Linq

I have a class like so
class Person(){
int Id;
string Name;
string SomePersonalInfo;
}
and a datatable with columns being Id, Name, and SomePersonalInfo. I also have a collection housing data in a List.
List<Person> = new Id = 1, Name = "So So"
List<Person> = new Id = 1, SomePersonalInfo= "Something"
I am trying to use linq so I dont have multiple for each statements to add Name and SomePersonalInfo in the datatable or making unnessary steps like keep getting the datarow. ANy suggestions
If you already have a strongly typed list, why do you want to have a loosely typed DataTable at all? Also, there is no good way to create a DataTable via LINQ-query without having DataRows.
Therefore i would simply use a loop which is readable and efficient:
foreach(Person p in persons)
tblPerson.Rows.Add(p.Id, p.Name, p.SomePersonalInfo);
Update acc. comment:
no I already have the datatable I am just trying to update the
datatable from the collection
Then you have to find the intersection first, use Enumerable.Join:
var inBoth = from p in persons
join row in tblPersons.AsEnumerable()
on p.Id equals row.Field<int>("Id")
select new { Person = p, Row = row };
foreach(var b in inBoth)
{
b.Row.SetField("Name", b.Person.Name);
b.Row.SetField("SomePersonalInfo", b.Person.SomePersonalInfo);
}

Sort datagridview by created columns. Entity Framework c# Winforms

I´m having a problem, I retrieve all the Loans I have stored in my database like this:
list_loans = db.Loan.Where(x => x.State.id_state != 6).ToList();
db is the Object context.
Then, I assign that list as the DataSource for my DataGridView.
dgv_Loans.Datasource = list_loans;
With that info, I add some columns. Like for example, installments left to pay. I get that value by counting the result of a query.
The user can order the result using some options. Is easy to order the result from the fields that the entity have (using linq), but I dont know how to order the results using this new columns.
I read some posts here and tried this:
dgv_Loans.Sort(dgv_Loans.Columns["installments_left"], ListSortDirection.Ascending);
By doing this, I´m getting the following exception at runtime:
"DataGridView control must be bound to an IBindingList object to be sorted."
Is there anyway to use linq to orderby created columns in a DataGridViewColumn? Or how can I solve this error?
I know there are related posts, but after reading them, I can´t find a solution to this specific problem. Thats why I showed how I implemented to get some advice..
Rather than binding directly to the list retrieved from database, what I generally do is have a view class and have all the calculated properties in that class
public class LoanView : Loan {
public LoanView(Loan loan){
}
public int InsallmentsLeft { get { return ...; } }
}
and then bind the datasource to a list of this, this keeps sorting working.
Concerning about Sort datagridview by created columns using Entity Framework
I guess you need this Presenting the SortableBindingList<T>
Usage:
loanBindingSource.DataSource = new SortableBindingList<Loan>(list_loans.ToList());
dgv_Loans.Datasource = loanBindingSource;
int ID = Convert.ToInt32(cmbDepartments.SelectedValue);
var EmployeeList = from Employee in db.Employee
where Employee.DepartmentID == ID
select new
{
Employee.FirstName,
Employee.LastName
};
dataGridView1.DataSource = EmployeeList.ToList();
You could directly give the data source to dataGridView1.DataSource but you must write ToList() at the end of your query:
int ID = Convert.ToInt32(cmbDepartmanlar.SelectedValue);
dataGridView1.DataSource = (from Employee in db.Employee
where Employee.DepartmentID == ID
select new
{
Employee.FirstName,
Employee.LastName
}).ToList();

How do I load a list via a data set from SQL Server into ListView?

I have what seems to be a simple question but its killing me trying to find out.
I have a form in which I have a ListView. In this ListView I would like to populate it with data from a SQL Server 2008 database table.
public void LoadList()
{
DataTable dtable = budget_MainDataSetReceipt.Tables["Receipt"];
listView1.Items.Clear();
for (int i = 0; i < dtable.Rows.Count; i++)
{
DataRow drow = dtable.Rows[i];
if (drow.RowState != DataRowState.Deleted)
{
ListViewItem lvi = new ListViewItem(drow["ReceiptID"].ToString());
lvi.SubItems.Add(drow["DateCleared"].ToString());
lvi.SubItems.Add(drow["CategoryID"].ToString());
lvi.SubItems.Add(drow["Amount"].ToString());
lvi.SubItems.Add(drow["Store"].ToString());
lvi.SubItems.Add(drow["DateEntered"].ToString());
listView1.Items.Add(lvi);
}
}
}
I keep getting an
Object reference not set to an instance of an object
error, and I can't figure out why. There are about 5 rows of data in my database, so in my mind, there should be 5 rows of data within the list view.
Can anyone tell me what I am missing? I can post more code if that would be helpful.
I have tried calling the LoadList() method in several ways:
Before the method itself
With the InitializeComponent() method
I have tried the following syntax
this.LoadList();
this.Form1.LoadList();`
I have also tried to initialize the DataTables type with the following:
DataTables dt = new DataTables //did not work
My hunch would be: you're assuming for all columns in your DataRow that they're present and not null - that's a bit of a dangerous assumption.
I would change your assignments to use a method that checks for DBNull before returning the string:
public string SafeGetString(DataRow row, string columnName)
{
if(row[columnName] != null && row[columnName] != DBNull.Value)
{
return row[ColumName].ToString();
}
return string.Empty;
}
so your could would look like:
ListViewItem lvi = new ListViewItem(SafeGetString(drow, "ReceiptID"));
lvi.SubItems.Add(SafeGetString(drow, "DateCleared"));
// and so forth
This way, if any of the columns should contain a NULL, you would get back an empty string - instead of running into a NULL.ToString() that causes the error you're seeing.

Categories