I am using Sqlite with C#. When i run the query in Sqlite browser it returns the correct data but when i run the same query from C# code it does not return data. My C# code is:
foreach (var group in groups)
{
var edgeDataWithGroupCmd = EdgeDatabase._connection.CreateCommand(#"SELECT ad.Id, ad.Name, adType.Id, adType.Name, h.Data, chart.URL
from
tblAdapter ad JOIN
tblAdapterType adType ON ad.AdapterTypeId = adType.Id JOIN
tblHealth h ON ad.HealthId = h.Id JOIN
tblCharts chart ON ad.ChartId = chart.Id
WHERE ad.GroupId = " + group.Id);
List<EdgeData> edgeDatas = edgeDataWithGroupCmd.ExecuteQuery<EdgeData>();
}
When i run this query in C#, query does return a list of "EdgeData" but all the values are set to there default and not the exact data from database. I am using sqlite-net.
EdgeData is a custom class:
public class EdgeData
{
public int AdapterId { get; set; }
public string AdapterName { get; set; }
public int AdapterTypeId { get; set; }
public string AdapterTypeName { get; set; }
public bool IsConnected { get; set; }
public int MaxRefreshRate { get; set; }
public int AchievedRefreshRate { get; set; }
public string ChartLink { get; set; }
}
//get one instance, and use function in logic/business layer to create list
//or rebuild function to return a list<EdgeData>
public EdgeData GetEdgeData(int groupID)
{
connection.open();
EdgeData edgeData;
StringBuilder sb = new StringBuilder();
sb.Append("SELECT ad.Id, ad.Name, adType.Id, adType.Name, h.Data, chart.URL
from
tblAdapter ad JOIN
tblAdapterType adType ON ad.AdapterTypeId = adType.Id JOIN
tblHealth h ON ad.HealthId = h.Id JOIN
tblCharts chart ON ad.ChartId = chart.Id
WHERE ad.GroupId = #GroupId");
SqlDataReader data;
String sql = sb.ToString();
//Use sql injection instead of "+"
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
cmd.Parameters.AddWithValue("#GroupId", groupID);
data = cmd.ExecuteReader();
}
while (data.Read())
{
int AdapterId = (int)data["ad.Id"]
string AdapterName = (string)data["ad.Name"]
int AdapterTypeId = ...
string AdapterTypeName = ...
bool IsConnected = ...
int MaxRefreshRate = ...
int AchievedRefreshRate = ..
string ChartLink = ..
edgeData = new EdgeData(AdapterId, AdapterName, etc...);
}
connection.Close();
return edgeData;
}
Related
I'm trying to create a List of object Work using Dapper to do the mapping.
This is the code:
public class Work
{
public int id { get; set; }
public int id_section { get; set; }
public decimal price { get; set; }
public Model id_model { get; set; }
public Type id_type { get; set; }
}
class Model
{
public int id_model { get; set; }
public string Name { get; set; }
}
class Type
{
public int id_type { get; set; }
public string Name { get; set; }
}
public List<Work> GetListOfWork(int idList)
{
using (DatabaseConnection db = new DatabaseConnection()) //return a connection to MySQL
{
var par = new {Id = idList};
const string query = "SELECT id,id_section,price,id_model,id_type FROM table WHERE id_section = #Id";
return db.con.Query<Work, Model, Type, Work>(query,
(w, m, t) =>
{
w.id_model = m;
w.id_type = t;
return w;
}, par, splitOn: "id_model,id_type").ToList();
}
}
It doesn't give me any error but id_model and id_type in my the returned List are always empty (The object are created but all the fields are empty or null), other fields are mapped correctly.
Any clue ?
You need to add yourself the joins in the query string
Probably it is something like this
var par = new {Id = idList};
const string query = #"SELECT w.id,w.id_section,w.price,
m.id_model, m.Name, t.id_type, t.Name
FROM work w INNER JOIN model m on w.id_model = m.id_model
INNER JOIN type t on w.id_type = t.id_type
WHERE w.id_section = #Id";
return db.con.Query<Work, Model, Type, Work>(query,
(w, m, t) =>
{
w.id_model = m;
w.id_type = t;
return w;
}, par, splitOn: "id_model,id_type").ToList();
I am new to programming so i don't know what i am doing.
I am pulling enum value from different class and set them as getter and setter.
namespace DataLayer.Entities
{
public enum CourseModeOfDelivery
{
Online, ClassRoom, ELearning,
}
public class Course
{
public int ID { get; set; }
public String CourseName { get; set; }
public String Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public CourseModeOfDelivery CourseMode { get; set; }
}
reading this value in courseRepository
public static Course GetCourse(int id)
{
Course a = new Course();
String GetCommand = "Select CourseName, Description, StartDate, EndDate, CourseMode from Course" + "Where ID = #CourseID";
SqlConnection connection = DBManager.GetSqlConnection();
SqlCommand command = new SqlCommand(GetCommand, connection);
command.Parameters.AddWithValue("#StudentID", id);
try
{
var reader = command.ExecuteReader();
//Read the Command Object and then return details
if (reader.HasRows)
{
while (reader.Read())
{
a.ID = Convert.ToInt32(reader["ID"]);
a.CourseName = reader["CourseName"].ToString();
a.Description = reader["Description"].ToString();
a.StartDate = DateTime.Parse(reader["StartDate"].ToString());
a.EndDate = DateTime.Parse(reader["EndDate"].ToString());
var selection = CourseModeOfDelivery.ClassRoom;
switch (selection)
{
case CourseModeOfDelivery.ClassRoom:
a.CourseMode = CourseModeOfDelivery.ClassRoom;
return a.CourseMode;
case CourseModeOfDelivery.ELearning:
a.CourseMode = CourseModeOfDelivery.ELearning;
return a.CourseMode;
case CourseModeOfDelivery.Online:
a.CourseMode = CourseModeOfDelivery.Online;
return a.CourseMode;
}
a.CourseMode =
}
}
else
{
reader.Close();
}
}
The requirement is to use switch but don't know how to pull data in there.
It depends on the type of database field you use.
If it is int then:
a.CourseMode = (CourseModeOfDelivery) reader["CourseMode"];
If it is String then:
a.CourseMode = (CourseModeOfDelivery) Enum.Parse(typeof(CourseModeOfDelivery), reader["CourseMode"].toString());
The following might also help you:
https://msdn.microsoft.com/en-us/library/essfb559(v=vs.110).aspx
Cast int to enum in C#
So I have the following code :
listOfUserLogs.Add(new Log
{
TimeStamp = Convert.ToDateTime(myReader["TimeStamp"]),
CheckpointId = Convert.ToInt32(myReader["CheckpointId"])
});
And when I run the program I get the System.IndexOutOfRangeException {"TimeStamp"} . I don't understand why is this so and how to fix it .
Note: I edited the post so you can see the entire code and let me know what am I missing.
You can see my program here :
namespace Distance
{
class Program
{
static void Main(string[] args)
{
string connectionString = GetConnectionString();
using (SqlConnection sourceConnection =
new SqlConnection(connectionString))
{
sourceConnection.Open();
SqlDataReader myReader = null;
SqlCommand myCommand = new SqlCommand("SELECT User.Id , [Log].[TimeStamp] ,[Checkpoints].[Id] ,[Checkpoints].[Coordinates] FROM dbo.[Users] INNER JOIN dbo.[Log] ON [Log].[UserId] =[Users].[Id] INNER JOIN dbo.[Checkpoints] ON [Checkpoints].[Id] = [Log].[CheckpointId] ", sourceConnection);
//SqlCommand myCommand = new SqlCommand("SELECT User.Id ,User.Name ,Checkpoint.Id ,Checkpoint.Coordinates , Log.TimeStamp FROM dbo.Users, INNER JOIN dbo.Log ON Log.UserId = User.Id, INNER JOIN dbo.Checkpoints ON Checkpoint.Id = Log.CheckpointId ;", sourceConnection);
myReader = myCommand.ExecuteReader();
var listOfUsers = new List<User>(); //get users from db
//long countStart = System.Convert.ToInt32(myCommand.ExecuteScalar());
var listOfCheckpoints = new List<Checkpoint>(); //get checkpoints from db
var listOfUserLogs = new List<Log>();
while (myReader.Read())
{
listOfUsers.Add(new User
{
Id = Convert.ToInt32(myReader["Id"]),
Name = myReader["Name"].ToString(),
Coordinates = myReader["Coordinates"].ToString()
});
listOfCheckpoints.Add(new Checkpoint
{
Id = Convert.ToInt32(myReader["Id"]),
Coordinates = myReader["Coordinates"].ToString()
});
listOfUserLogs.Add(new Log
{
TimeStamp = Convert.ToDateTime(myReader["TimeStamp"]),
CheckpointId = Convert.ToInt32(myReader["CheckpointId"]),
UserId =Convert.ToInt32(myReader["UserId"])
});
}
StringBuilder sb = new StringBuilder();
foreach (var user in listOfUsers)
{
string address = user.Coordinates;
DateTime currentDate = new DateTime(2014, 8, 1);
var dictionary = new Dictionary<string, double>();
while (currentDate <= DateTime.Now)
{
double dayUserDistance = 0.00;
// var listOfUserLogs = new List<Log>(); //Get logs where day == currentDate from db
var previousCoordinate = address;
foreach (var log in listOfUserLogs)
{
Checkpoint checkpoint = listOfCheckpoints.FirstOrDefault(x => x.Id == log.CheckpointId);
dayUserDistance += DistanceCalculator.GetDistance(previousCoordinate, checkpoint.Coordinates);
previousCoordinate = checkpoint.Coordinates;
}
dayUserDistance += DistanceCalculator.GetDistance(previousCoordinate, address);
dictionary.Add(currentDate.ToString("yyyy-MM-dd"), dayUserDistance);
currentDate = currentDate.AddDays(1);
}
sb.Append(user.Name + ";");
foreach (KeyValuePair<string, double> keyValuePair in dictionary)
{
sb.Append(keyValuePair.Value + ";");
}
sb.AppendLine();
}
Console.WriteLine();
Console.ReadLine();
}
}
private static string GetConnectionString()
// To avoid storing the sourceConnection string in your code,
// you can retrieve it from a configuration file.
{
return "Data Source=BESA-PC;" +
" Integrated Security = true;" +
"Initial Catalog=CykelScore2;";
}
}
}
internal class DistanceCalculator
{
public static double GetDistance(string previousCoordinate, string coordinates)
{
string[] PairSequence = previousCoordinate.Split(',');
float sLatitude = float.Parse(PairSequence[0]);
float sLongitude = float.Parse(PairSequence[1]);
string[] PairSequence2 = coordinates.Split(',');
float eLatitude = float.Parse(PairSequence2[0]);
float eLongitude = float.Parse(PairSequence2[1]);
var sCoord = new GeoCoordinate(sLatitude, sLongitude);
var eCoord = new GeoCoordinate(eLatitude, eLongitude);
return sCoord.GetDistanceTo(eCoord);
}
}
internal class Checkpoint
{
public int Id { get; set; }
public string Coordinates { get; set; }
}
internal class Log
{
public DateTime TimeStamp { get; set; }
public int CheckpointId { get; set; }
public int UserId { get; set; }
}
internal class User
{
public int Id { get; set; }
public string Coordinates { get; set; }
public string Name { get; set; }
}
There are a lot of problems in your code above. Basically you are trying to model your classes following the exact model of your tables and this is not always the best path.
For example, I would design your classes in this way
(To avoid naming confusion between the new class Coordinate and the string coordinates I have renamed the latter to Location)
internal class Coordinate
{
public int coordID { get; set; } // This is your CheckpointID
public string Location { get; set; } // This is the string coordinate loaded
public DateTime TimeStamp { get; set; } // This is the TimeStamp of the coordinate
}
internal class User
{
public int Id { get; set; }
public string Name { get; set; }
public List<Coordinate> Coordinates {get;set;}
}
With these changes in place I would modify your query in this way
#"SELECT User.Id AS UserID,
[Log].[TimeStamp],
[Checkpoints].[Id] as CheckPointID,
[Checkpoints].[Coordinates] as Location
FROM dbo.[Users] INNER JOIN dbo.[Log]
ON [Log].[UserId] = [Users].[Id]
INNER JOIN dbo.[Checkpoints]
ON [Checkpoints].[Id] = [Log].[CheckpointId]
ORDER BY User.ID, [Log].[TimeStamp]" <--- This order by is very important
At this point your loop should change with
User usr = null;
int curUserID = -1;
while (myReader.Read())
{
int id = Convert.ToInt32(myReader["UserId"]);
if(curUserID != id)
{
// Enter this block only if the user changes from the previous one
// They are ordered so you are sure to get them in line
usr = new User()
{
Id = id,
Name = reader["Name"].ToString(),
Coordinates = new List<Coordinate>()
};
curUserID = id;
listOfUsers.Add(usr);
}
// Add all the coordinates that belong to the same user
Coordinate cc = new Coordinate()
{
cc.coordID = Convert.ToInt32(reader["CheckPointID"]);
cc.TimeStamp = Convert.ToDateTime(reader["TimeStamp"]);
cc.Location = reader["Location"].ToString();
};
usr.Coordinates.Add(cc);
}
At the end of this loop you could loop on the listOfUser and calculate the distance using the List<Coordinate> that belongs to a specific user after ordering them using the TimeStamp
foreach(User usr in listUser)
{
...
foreach(Coordinate cc in usr.Coordinates.OrderBy(x => x.TimeStamp)
{
......
}
}
Said that, I really recommend you to spend a bit of your time trying to learn the usage of a modern ORM tool (Entity Framework, Dapper) that would remove all the code to load data from DB and let you concentrate your efforts on the logic required by your task.
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();
}
}
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.