How to Deserialize datareader or data table to c# class - c#

i have populated data reader from db table and i have class like
public class CandidateApplication
{
public string EmailID { get; set; }
public string Name { get; set; }
public string PhoneNo { get; set; }
public string CurrentLocation { get; set; }
public string PreferredWorkLocation { get; set; }
public int RoleApplingFor { get; set; }
public string CurrentJobTitle { get; set; }
public int EducationLevel { get; set; }
public decimal SalaryExpected { get; set; }
public string AvailableTime { get; set; }
public int AdvertID { get; set; }
public bool SignForAlert { get; set; }
public string CVInText { get; set; }
public string CVFileName { get; set; }
public bool IsDownloaded { get; set; }
public string specialization { get; set; }
public bool isallocated { get; set; }
public int id { get; set; }
public string AdvertAdditionalInfo { get; set; }
}
i can populate the above class in loop. we can iterate in data reader and populate class but i want to know is there any short cut way to populate class from data reader.
if data deserialization is possible from data reader to class then also tell me if few fields are there in class which are not there in data reader then how to handle the situation.

You don't need to use a Data Reader, You could just Populate the Data into a DataTable, and use the below method to create a List of your CandidateApplication Class.
The Call :-
List<CandidateApplication> CandidateList = GetCandidateInformation();
The Method that generates the list :-
public List<CandidateApplication> GetCandidateInformation()
{
DataTable dt = new DataTable();
using (OleDbConnection con = new OleDbConnection(ConfigurationManager.AppSettings["con"]))
{
using (OleDbCommand cmd = new OleDbCommand("SELECT * FROM [TableName]", con))
{
var adapter = new OleDbDataAdapter();
adapter.SelectCommand = cmd;
con.Open();
adapter.Fill(dt);
var CandApp = (from row in dt.AsEnumerable()
select new CandidateApplication
{
EmailID = row.Field<string>("EmailID"),
Name = row.Field<string>("Name"),
PhoneNo = row.Field<string>("PhoneNo"),
CurrentLocation = row.Field<string>("CurrentLocation"),
PreferredWorkLocation = row.Field<string>("PreferredWorkLocation"),
RoleApplingFor = row.Field<int>("RoleApplingFor"),
CurrentJobTitle = row.Field<string>("CurrentJobTitle"),
EducationLevel = row.Field<int>("EducationLevel "),
SalaryExpected = row.Field<decimal>("SalaryExpected"),
AvailableTime = row.Field<string>("AvailableTime"),
AdvertID = row.Field<int>("AdvertID"),
SignForAlert = row.Field<bool>("SignForAlert"),
CVInText = row.Field<string>("CVInText"),
CVFileName = row.Field<string>("CVFileName"),
IsDownloaded = row.Field<bool>("IsDownloaded"),
Specialization = row.Field<string>("Specialization"),
Isallocated = row.Field<bool>("Isallocated"),
Id = row.Field<int>("Id"),
AdvertAdditionalInfo = row.Field<string>("AdvertAdditionalInfo")
}).ToList();
return CandApp;
}
}
}

Although not an answer to your question, I would suggest you to consider the following workaround, which uses a SqlDataAdapter instead of a data reader:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Xml.Serialization;
class Program
{
static void Main(string[] args)
{
var cs = "YourConnectionString";
var xml = "";
using (var con = new SqlConnection(cs))
using (var c = new SqlCommand("SELECT * FROM CandidateApplication", con))
{
con.Open();
using (var adapter = new SqlDataAdapter(c))
{
var ds = new DataSet("CandidateApplications");
ds.Tables.Add("CandidateApplication");
adapter.Fill(ds, ds.Tables[0].TableName);
xml = ds.GetXml();
}
}
// We need to specify the root element
var rootAttribute = new XmlRootAttribute();
// The class to use as the XML root element (should match the name of
// the DataTable in the DataSet above)
rootAttribute.ElementName = "CandidateApplications";
// Initializes a new instance of the XmlSerializer class that can
// serialize objects of the specified type into XML documents, and
// deserialize an XML document into object of the specified type.
// It also specifies the class to use as the XML root element.
// I chose List<CandidateApplication> as the type because I find it
// easier to work with (but CandidateApplication[] will also work)
var xs = new XmlSerializer(typeof(List<CandidateApplication>), rootAttribute);
// Deserialize the XML document contained by the specified TextReader,
// in our case, a StringReader instance constructed with xml as a parameter.
List<CandidateApplication> results = xs.Deserialize(new StringReader(xml));
}
}
For those properties that are missing in the retrieved data, you could declare a private field with a default value:
string _advertAdditionalInfo = "default";
public string AdvertAdditionalInfo
{
get
{
return _advertAdditionalInfo;
}
set
{
_advertAdditionalInfo = value;
}
}
If you would like to enforce that the retrieved data will not fill in a specific property, use:
[XmlIgnoreAttribute]
public string AdvertAdditionalInfo { get; set; }

I made a generic function for converting the SELECT result from an OleDbCommand to a list of classes.
Let's say that I have a class that looks like this, which maps to the columns in the database:
internal class EconEstate
{
[Column(Name = "basemasterdata_id")]
public Guid BaseMasterDataId { get; set; }
[Column(Name = "basemasterdata_realestate")]
public Guid? BaseMasterDataRealEstate { get; set; }
[Column(Name = "business_area")]
public string BusinessArea { get; set; }
[Column(Name = "profit_centre")]
public int ProfitCentre { get; set; }
[Column(Name = "rentable_area")]
public decimal RentableArea { get; set; }
}
Then I can get a list of those EconEstate objects using this code:
public void Main()
{
var connectionString = "my connection string";
var objects = ReadObjects<EconEstate>(connectionString, "EMBLA.EconEstates").ToList();
}
private static IEnumerable<T> ReadObjects<T>(string connectionString, string tableName) where T : new()
{
using (var connection = new OleDbConnection(connectionString))
{
connection.Open();
using (var command = new OleDbCommand($"SELECT * FROM {tableName};", connection))
{
var adapter = new OleDbDataAdapter
{
SelectCommand = command
};
var dataTable = new DataTable();
adapter.Fill(dataTable);
foreach (DataRow row in dataTable.Rows)
{
var obj = new T();
foreach (var propertyInfo in typeof(T).GetProperties())
{
var columnAttribute = propertyInfo.GetCustomAttributes().OfType<ColumnAttribute>().First();
var value = row[columnAttribute.Name];
var convertedValue = ConvertValue(value, propertyInfo.PropertyType);
propertyInfo.SetValue(obj, convertedValue);
}
yield return obj;
}
}
}
}
private static object ConvertValue(object value, Type targetType)
{
if (value == null || value.GetType() == typeof(DBNull))
{
return null;
}
if (value.GetType() == targetType)
{
return value;
}
var underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
if (value is string stringValue)
{
if (underlyingTargetType == typeof(int))
{
return int.Parse(stringValue);
}
else if (underlyingTargetType == typeof(decimal))
{
return decimal.Parse(stringValue);
}
}
var valueType = value.GetType();
var constructor = underlyingTargetType.GetConstructor(new[] { valueType });
var instance = constructor.Invoke(new object[] { value });
return instance;
}
As you can see, the code is generic, making it easy to handle different tables and classes.

Related

Dapper - How to create class instance by column value?

How to instantiate class by specific column value with Dapper?
Let's say we have table 'items' with columns: ID, Type, Name...
And, when Type is equal to "Burger", class Burger should be instantiated, when Type is "Sandwich", class Sandwich should be instantiated.
First approach that comes to my mind is to execute query for every type, for example:
var sandwiches = conn.Query<Sandwich>("SELECT * FROM items WHERE Type = 'Sandwich'");
var burgers = conn.Query<Burger>("SELECT * FROM items WHERE Type = 'Burger'");
But this approach is expensive because we will have as many queries as number of item types.
Can we do the same job with single query? (Select all rows and create instances based on column value)
A class could have a generic method that could do what you want:
public class Query
{
public IEnumerable<T> GenericQuery<T>(string query)
{
using (SqlConnection cnn = new SqlConnection("Data Source=(LOCAL);Initial Catalog=LinqPadTest;Integrated Security=True;"))
{
cnn.Open();
return cnn.Query<T>(query);
}
}
}
Now you can call it in this way
void Main()
{
var q = new Query();
var sandwiches = q.GenericQuery<Sandwich>("SELECT * FROM items WHERE Type = 'Sandwich'");
var burgers = q.GenericQuery<Burger>("SELECT * FROM items WHERE Type = 'Burger'");
}
use method's generic argument to get type
public class ItemsRepository
{
private const string Query = "SELECT * FROM items WHERE Type = #Type";
public async Task<IEnumerable<T>> GetItemsBasedOnTypeAsync<T>()
{
await using var connection = new SqlConnection("Data Source=(LOCAL);Initial Catalog=LinqPadTest;Integrated Security=True;");
await connection.OpenAsync();
var parameters = new { Type = nameof(T) };
return connection.Query<T>(Query, parameters);
}
}
now you can call method for any items type
var itemsRepository = new ItemsRepository();
var sandwiches = await itemsRepository.GetItemsBasedOnTypeAsync<Sandwich>();
var burgers = await itemsRepository.GetItemsBasedOnTypeAsync<Burger>();
V2(select all items):
use can do something like this, or cast to base interface if exists.
public class Item
{
public int Id { get; set; }
public string Type { get; set; }
public int Property1 { get; set; }
public int Property2 { get; set; }
}
public class Sandwich
{
public Sandwich(Item item)
{
Id = item.Id;
Property1 = item.Property1;
Property2 = item.Property2;
}
public int Id { get; set; }
public int Property1 { get; set; }
public int Property2 { get; set; }
}
public class Burger
{
public Burger(Item item)
{
Id = item.Id;
Property1 = item.Property1;
Property2 = item.Property2;
}
public int Id { get; set; }
public int Property1 { get; set; }
public int Property2 { get; set; }
}
public class ItemsRepository
{
private const string Query = "SELECT * FROM items";
public async Task<IEnumerable<object>> GetItemsBasedOnTypeAsync()
{
await using var connection = new SqlConnection("Data Source=(LOCAL);Initial Catalog=LinqPadTest;Integrated Security=True;");
await connection.OpenAsync();
var items = connection.Query<Item>(Query);
var objects = items.Select(Create)
.ToList();
return objects;
}
private object? Create(Item item) =>
Assembly
.GetExecutingAssembly()
.CreateInstance(item.Type,true,BindingFlags.Public,null,
new object[]{item},
CultureInfo.DefaultThreadCurrentCulture, null);
}

C# generic insert method to insert object into Sqlite3 table?

I am trying to make a generic method where I can insert any object into a sqlite3 database.
User class:
public class Users : IClassModel<Users>
{
public int Id { get; set; }
public string UserName { get; set; }
public string UserAddress { get; set; }
public string OtherUserDetails { get; set; }
public decimal AmountOfFine { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
Interface class:
public interface IClassModel<T>
{
public int Id { get; set; }
}
QueryBuilder class:
public class queryBuilder : IDisposable
{
private SqliteConnection _connection;
public queryBuilder(string connectionString)
{
_connection = new SqliteConnection(connectionString);
_connection.Open();
}
public void Dispose()
{
_connection.Close();
}
public void Create<T>(T obj) where T : IClassModel<T>
{
// insert into tableName values()
Type myType = obj.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
ArrayList valueArray = new ArrayList();
ArrayList nameArray = new ArrayList();
var questionString = "";
var nameString = "";
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(obj, null);
object propName = prop.Name;
valueArray.Add(propValue);
nameArray.Add(propName);
questionString += "?, ";
nameString += $"{propName}, " ;
}
var newNameString = nameString.Trim();
var newerNameString = newNameString.TrimEnd(',');
var newQuestionString = questionString.Trim();
var newerQuestionString = newQuestionString.TrimEnd(',');
SqliteCommand insertSQL = new SqliteCommand($"INSERT INTO {typeof(T).Name} ({newerNameString}) VALUES ({newerQuestionString})", _connection);
foreach (var item in valueArray)
{
insertSQL.Parameters.Add(item);
}
insertSQL.ExecuteNonQuery();
//Console.WriteLine("Successfully added the thing.");
}
}
Driver:
using Microsoft.Data.Sqlite;
using QueryBuilder.Models;
using System.Reflection;
using (var query = new queryBuilder(#"Data Source=C:\path\to\database"))
{
// con
var user = new Users();
user.UserName = "username";
user.UserAddress = "some_address";
user.OtherUserDetails = "details";
user.AmountOfFine = 90;
user.Email = "something#email.com";
user.PhoneNumber = "5555555555";
query.Create<Users>(user);
}
I know my code is bit messy, but the idea is to somehow create an object and then be able to insert it into the already made table, no matter what object it is. I keep getting invalid cast exceptions.
I need to be able to iterate through the values and properties and add them to the sqlite insert command but it doesn't seem to be working. Any help is appreciated.

C# class library, access object properties

I have a function which suppose to receive an object as parameter and use its properties for further use,but when i try to access object properties it mark them as red (can't resolve symbol...)
i tried to add few references but it didn't help.
public bool InsertStudent(object student)
{
var db = DAL.Services.SqlServicesPool.HackService;
using (var connection = db.StartConnection())
{
var sqlParam = new SqlParameter[]
{
new SqlParameter("#Name", student.Name),
new SqlParameter("#Lname", student.Lname),
new SqlParameter("#Phone", student.Phone),
new SqlParameter("#Email", student.Email),
new SqlParameter("#Img", student.Img),
new SqlParameter("#CityId", student.CityId)
};
var result = db.Exec(connection, "spInsertNewStudent", sqlParam);
db.StopConnection(connection);
return result;
};
}
The type of student is object, or more formally System.Object. This type hasn't any property called Name or Lname etc. That's the problem. You should change the type to the class used to create the student object. For instance if you have created the Student object like below:
var student = new Student
{
Name = "foo"
// .....
}
You should change the signature of your method like below:
public bool InsertStudent(Student student)
Parameter 'student' has an 'Object' type. This is base type for all classes and structures. You should cast 'student' to your class type
public bool InsertStudent(object obj)
{
var student = (Student)obj;
....
}
Or change 'student' parameter type
public bool InsertStudent(Student student)
{
....
}
Use Student class insted of object class
public bool InsertStudent(Student student)
{
var db = DAL.Services.SqlServicesPool.HackService;
using (var connection = db.StartConnection())
{
var sqlParam = new SqlParameter[]
{
new SqlParameter("#Name", student.Name),
new SqlParameter("#Lname", student.Lname),
new SqlParameter("#Phone", student.Phone),
new SqlParameter("#Email", student.Email),
new SqlParameter("#Img", student.Img),
new SqlParameter("#CityId", student.CityId)
};
var result = db.Exec(connection, "spInsertNewStudent", sqlParam);
db.StopConnection(connection);
return result;
};
}
public class Student
{
public string Name { get; set; }
public string Lname { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public byte []Img { get; set; }
public string CityId { get; set; }
}
or
public bool InsertStudent((Student)object student){ ... }
As Christos commented, you need to use a Student object rather.
public class Student
{
public string Name { get; set; }
public string Lname { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Image { get; set; } //Wasn't sure what type you use here
public int CityId { get; set; }
}
Then pass through a Student object as your parameter.
public bool InsertStudent(Student student)
{
var db = DAL.Services.SqlServicesPool.HackService;
using (var connection = db.StartConnection())
{
var sqlParam = new SqlParameter[]
{
new SqlParameter("#Name", student.Name),
new SqlParameter("#Lname", student.Lname),
new SqlParameter("#Phone", student.Phone),
new SqlParameter("#Email", student.Email),
new SqlParameter("#Img", student.Img),
new SqlParameter("#CityId", student.CityId)
};
var result = db.Exec(connection, "spInsertNewStudent", sqlParam);
db.StopConnection(connection);
return result;
};
}

Unable to implicitly convert type cross solution

Hi everyone I am new to programming, I am currently trying to create a web service to retrieve the customer information in my database but currently I am unable to solve this error, someone please help me
CustAccounts.svc.cs
public class CustAcc : ICustAccounts
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public List<CustAcc> GetCustAccJSON()
{
List<CustomerAccountDAL> abc = new List<CustomerAccountDAL>();
CustomerAccountDAL caDAL = new CustomerAccountDAL();
List<CustAcc> allAcc = new List<CustAcc>();
allAcc = caDAL.retrieveCustAccount();
//caDAL.retrieveCustAccount();
return allAcc;
}
}
CustomerAccountDAL.cs
public List<CustAcc> retrieveCustAccount()
{
List<CustAcc> acc = new List<CustAcc>();
using (SqlConnection myConnection = new SqlConnection(con))
{
string jString = "Select * from CustDB";
SqlCommand jCmd = new SqlCommand(jString, myConnection);
myConnection.Open();
using (SqlDataReader rr = jCmd.ExecuteReader())
{
while (rr.Read())
{
CustAcc accounts = new CustAcc();
custEmail = rr["custEmail"].ToString();
custPassword = rr["custPassword"].ToString();
acc.Add(accounts);
}
myConnection.Close();
}
}
return acc;
}
CustAcc.cs
public class CustAcc
{
public string custFullName { get; set; }
public string custPreferredName { get; set; }
public string custPassword { get; set; }
public string custEmail { get; set; }
public string custPhoneNumber { get; set; }
public string message { get; set; }
public CustAcc(string custName, string custFullName, string custPreferredName, string custPassword, string custEmail, string custPhoneNumber)
{
this.custFullName = custFullName;
this.custPreferredName = custPreferredName;
this.custPassword = custPassword;
this.custEmail = custEmail;
this.custPhoneNumber = custPhoneNumber;
}
public CustAcc()
{
this.custEmail = custEmail;
this.custPassword = custPassword;
}
}
Move your Model classes to their own assembly called Model and reference the assembly in both the WS and website assemblies.
You will also need to setup the Service reference in your website to look for the Service types in the Model assembly (it's an option in the Add Service Reference wizard).

Converting Dataset to IList [duplicate]

This question already has answers here:
Convert DataSet to List
(11 answers)
Closed 8 years ago.
I am using SqlHelper to execute the stored procedure in the DB.
In a namespace called constants i defined something like this
public class ShowInstitutes
{
public string InstituteName { get; set; }
public string InstituteCity { get; set; }
public int InstituteId { get; set; }
}
In the DAL layer I am trying to execute stored proc and get results in IList format
public IList<ShowInstitutes> ShowInstitutes(int instituteId)
{
return SqlHelper.ExecuteDataset(dBConnection, "usp_SPName");
}
I am getting the following error:
Cannot implicitly convert type 'System.Data.DataSet' to 'System.Collections.Generic.IList<>
You can converting your Dataset result to IList like this.
public IList<ShowInstitutes> ShowInstitutes(int instituteId)
{
var dataTable = SqlHelper.ExecuteDataset(dBConnection, "usp_SPName");
var SourceLists = new List<ShowInstitutes>();
for (var index = 0; index < dataTable.Rows.Count; index++)
{
SourceLists.Add(new ShowInstitutes
{
InstituteName = Convert.ToString(dataTable.Rows[index]["Columnname"], CultureInfo.InvariantCulture),
InstituteCity = Convert.ToString(dataTable.Rows[index]["Columnname"], CultureInfo.InvariantCulture),
InstituteId = Convert.ToInt32(dataTable.Rows[index]["Columnname"], CultureInfo.InvariantCulture)
});
}
return SourceLists;
}
public IList<ShowInstitutes> ShowInstitutes(int instituteId)
{
var d = SqlHelper.ExecuteDataset(dBConnection, "usp_SPName");
var myData = d.Tables[0].AsEnumerable().Select(data => new ShowInstitutes{
InstituteName = data.Field<string>("InstituteName "),
InstituteCity = data.Field<string >("InstituteCity "),
InstituteId = data.Field<int>("InstituteId ")
});
var list = myData.ToList();
return list;
}
public class info
{
public string counter
{
get; set;
}
public string age
{
get;
set;
}
public string id
{
get;
set;
}
public string marks
{
get;
set;
}
public string name
{
get;
set;
}
public List<info> getdata()
{
string c = "Data Source=bla ;bla ;bla";
SqlConnection con = new SqlConnection(c);
DataSet ds = SqlHelper.ExecuteDataset(con, CommandType.Text, "SELECT * from table1");
var list = (ds.Tables[0].AsEnumerable().Select(
df =>
new info
{
age = df[0].ToString(),
counter = df[1].ToString(),
id = df[3].ToString(),
name = df[4].ToString(),
marks = df[2].ToString()
})).ToList();
return list;
}
}
class Program
{
static void Main(string[] args)
{
info a =new info();
List<info> list1= a.getdata();
foreach (var info in list1)
{
Console.WriteLine(info.name+" "+info.age+" "+info.marks);
}
Console.Read();
}
}

Categories