We created a Web API which takes a Array of ID as input parameters and queries the Oracle Database, returns the result in JSON format. If the data returned is very large it throws an OutOfMemoryException issue. So planning to add an additional query parameter, which set of columns to return data.
Instead of loading loading the entire result set in to memory currently we are before serializing it out to the HttpResponseMessage. Our query is complex and huge, which selects 45 columns.I am just giving only few of the columns here as an example in the below code,
using PSData.Models;
namespace PSData.Controllers
public class PDataController : ApiController
{
public HttpResponseMessage Getdetails([FromUri] string[] id)
{
List<OracleParameter> prms = new List<OracleParameter>();
string connStr = ConfigurationManager.ConnectionStrings["PDataConnection"].ConnectionString;
using (OracleConnection dbconn = new OracleConnection(connStr))
{
DataSet userDataset = new DataSet();
var strQuery = #"SELECT STCD_PRIO_CATEGORY_DESCR.DESCR AS CATEGORY,
STCD_PRIO_CATEGORY_DESCR.SESSION_NUM AS SESSION_NUMBER,
Trunc(STCD_PRIO_CATEGORY_DESCR.START_DATE) AS SESSION_START_DATE,
STCD_PRIO_CATEGORY_DESCR.START_DATE AS SESSION_START_TIME ,
Trunc(STCD_PRIO_CATEGORY_DESCR.END_DATE) AS SESSION_END_DATE,
STCD_PRIO_CATEGORY_DESCR.END_DATE AS SESSION_END_TIME,
Round((TO_DATE (TO_CHAR (STCD_PRIO_CATEGORY_DESCR.END_DATE, 'dd/mm/yyyy hh24:mi'),'dd/mm/yyyy hh24:mi') - TO_DATE (TO_CHAR (STCD_PRIO_CATEGORY_DESCR.START_DATE, 'dd/mm/yyyy hh24:mi'),'dd/mm/yyyy hh24:mi'))*1440)AS SESSION_DURATION_MINUTES
from STCD_PRIO_CATEGORY
where STCD_PRIO_CATEGORY_DESCR.STD_REF IN(";
var sb = new StringBuilder(strQuery);
for (int x = 0; x < inconditions.Length; x++)
{
sb.Append(":p" + x + ",");
var p = new OracleParameter(":p" + x, OracleDbType.NVarchar2);
p.Value = inconditions[x];
prms.Add(p);
}
if (sb.Length > 0) sb.Length--;
strQuery = sb.Append(")").ToString();
var returnObject = new { data = new OracleDataTableJsonResponse(connStr, strQuery, prms.ToArray()) };
var response = Request.CreateResponse(HttpStatusCode.OK, returnObject, MediaTypeHeaderValue.Parse("application/json"));
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=PSData.json", out contentDisposition))
{
response.Content.Headers.ContentDisposition = contentDisposition;
}
return response;
}
Now the api call is like
http://localhost:430/api/PData?id=JW21&id=BL02&id=AB02
I understand that there are these keywords top and skip in ODATA using IQueryable so the API call is
http://localhost:9658/api/values?$top=2&$skip=2
I am not sure how to implement the above in my approach, I created the Model class like below
namespace PSData.Models
{
public class StudyDataModel
{
[Key]
public string CATEGORY { get; set; }
public int SESSION_NUMBER { get; set; }
public DateTime SESSION_START_DATE { get; set; }
public DateTime SESSION_START_TIME { get; set; }
public Nullable<DateTime> SESSION_END_DATE { get; set; }
public Nullable<DateTime> SESSION_END_TIME { get; set; }
public string DOSE_ACTIVITY { get; set; }
public Nullable<decimal> SESSION_DURATION_MINUTES { get; set; }
}
}
And
namespace PSData.Models
{
public class StudyDataContext:DbContext
{
public DbSet<StudyDataModel> details { get; set; }
}
}
But I am not sure how to implement them in my actual controller. Any help with this greatly appreciated.
Related
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;
}
I am Newbie to ASP.NET, created the MVC Framework Web API which accepts the array of ID's as input parameter and queries the Oracle DB, this should return the result in the JSON format.Our query is like
SELECT STCD_PRIO_CATEGORY_DESCR.DESCR AS CATEGORY,
STCD_PRIO_CATEGORY_DESCR.SESSION_NUM AS SESSION_NUMBER,
Trunc(STCD_PRIO_CATEGORY_DESCR.START_DATE) AS SESSION_START_DATE
from STCD_PRIO_CATEGORY
where STCD_PRIO_CATEGORY_DESCR.STD_REF IN(X,Y,Z)
where X,Y,Z are the values we will be passing as input parameters
I created the API controller as
public class PDataController : ApiController
{
public HttpResponseMessage Getdetails([FromUri] string[] id)
{
List<OracleParameter> prms = new List<OracleParameter>();
string connStr = ConfigurationManager.ConnectionStrings["PDataConnection"].ConnectionString;
using (OracleConnection dbconn = new OracleConnection(connStr))
{
var inconditions = id.Distinct().ToArray();
var srtcon = string.Join(",", inconditions);
DataSet userDataset = new DataSet();
var strQuery = #"SELECT STCD_PRIO_CATEGORY_DESCR.DESCR AS CATEGORY,
STCD_PRIO_CATEGORY_DESCR.SESSION_NUM AS SESSION_NUMBER,
Trunc(STCD_PRIO_CATEGORY_DESCR.START_DATE) AS SESSION_START_DATE
from STCD_PRIO_CATEGORY
where STCD_PRIO_CATEGORY_DESCR.STD_REF IN(";
StringBuilder sb = new StringBuilder(strQuery);
for(int x = 0; x < inconditions.Length; x++)
{
sb.Append(":p" + x + ",");
OracleParameter p = new OracleParameter(":p" + x,OracleDbType.NVarchar2);
p.Value = inconditions[x];
prms.Add(p);
}
if(sb.Length > 0) sb.Length--;
strQuery = sb.ToString() + ")";
using (OracleCommand selectCommand = new OracleCommand(strQuery, dbconn))
{
selectCommand.Parameters.AddRange(prms.ToArray());
using (OracleDataAdapter adapter = new OracleDataAdapter(selectCommand))
{
DataTable selectResults = new DataTable();
adapter.Fill(selectResults);
var returnObject = new { data = selectResults };
var response = Request.CreateResponse(HttpStatusCode.OK, returnObject, MediaTypeHeaderValue.Parse("application/json"));
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=ProvantisStudyData.json", out contentDisposition))
{
response.Content.Headers.ContentDisposition = contentDisposition;
}
return response;
}}}}}}
It works perfectly and returns the result as
{"data":[{"CATEGORY":"Internal Study","SESSION_NUMBER":7,"SESSION_START_DATE":"2015-02-13T00:00:00"}]}
But would like to implement the entity framework here by using the Model and the DBContext.I created the model class and the DataContext Class as follows
namespace PSData.Models
{ public class StudyDataModel
{ [Key]
public string CATEGORY { get; set; }
public int SESSION_NUMBER { get; set; }
public DateTime SESSION_START_DATE { get; set; }
}}
And
namespace PSData.Models
{
public class StudyDataContext:DbContext
{
public DbSet<StudyDataModel> details { get; set; }
}}
I am not sure how to implement them in the controller. When I tried to create the Controller using Web API 2 Controller with actions,using Entity Framework selected both the Model Class and the DB Context Class it creates controller with
private StudyDataContext db = new StudyDataContext();
// GET: api/StdData
public IQueryable<StudyDataModel> Getdetails()
I am not sure how to proceed as the return type is the HttpResponseMessage in my other Controller where I am returning the JSON message. Any help is greatly appreciayed
You do not need to explicitly convert it to json format. The content negotiation module and media formatter will take care of converting the data to the needed format (XML/JSON) based on the request. By default it returns JSON.
Assuming you have a DTO class like this
public class CategoryDto
{
public string Category { get; set; }
public int SessionNumber { get; set; }
public DateTime SessionStartDate { get; set; }
}
and in your action method, you can use Request.CreateResponse method.
public HttpResponseMessage Get()
{
var db = new StudyDataContext();
var data = db.details.Select(x => new CategoryDto {
Category = x.Category,
SessionStartDate = x.SessionStartDate,
SessionNumber = x.SessionNumber }
).ToList();
return Request.CreateResponse(HttpStatusCode.OK, data);
}
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.
I am having trouble with the representation of a date in JSON. I am using Service Stack as a web service to get the data from. My code on the server side is as follows:
public object Execute(GetNoPatientList request)
{
NoPatientList _noPatientList = new NoPatientList();
List<string> _noMatchPatientList = new List<string>();
List<NoPatientList> _newList = new List<NoPatientList>();
try
{
using (SqlConnection cn = new SqlConnection(Database.WaldenWebConnection))
{
cn.Open();
using (SqlCommand cm = cn.CreateCommand())
{
cm.CommandText = "select [DateTimeStamp] as DateCreated,[ID],[PatientMRN],[FirstName],[MiddleName]"
+ " ,[LastName],convert(varchar,[DOB],101) as DOB,[Sex],[Note],[Source] as Interface"
+ " from PatientNoMatch"
+ " where FoundMatch = 'F'"
+ " and Show = 'T'"
+ " order by DateTimeStamp desc";
SqlDataReader dr = cm.ExecuteReader();
while (dr.Read())
{
NoPatientList _noPatientList1 = new NoPatientList();
_noPatientList1.PatientMRN = dr["PatientMRN"].ToString();
_noPatientList1.FirstName = dr["FirstName"].ToString();
_noPatientList1.MiddleName = dr["MiddleName"].ToString();
_noPatientList1.LastName = dr["LastName"].ToString();
_noPatientList1.DOB = dr["DOB"].ToString();
_noPatientList1.Sex = dr["Sex"].ToString();
_noPatientList1.Note = dr["Note"].ToString();
_noPatientList1.DateCreated = dr.GetDateTime(0);
_noPatientList1.Interface = dr["Interface"].ToString();
_newList.Add(_noPatientList1);
}
return _newList;
}
}
}
catch
{
return _newList;
}
}
The type is represented as follows:
[DataContract]
public class NoPatientList
{
[DataMember]
public string ID { get; set; }
[DataMember]
public string PatientMRN { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string MiddleName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string Sex { get; set; }
[DataMember]
public string DOB { get; set; }
[DataMember]
public string Note { get; set; }
[DataMember]
public DateTime DateCreated { get; set; }
[DataMember]
public string Interface { get; set; }
}
The web service is being consumed by a Silverlight application from the following call:
/InterfaceUtility/servicestack/json/syncreply/
The Silverlight application is processing the code into a grid using the following code
private void GetNoPatientMatchData()
{
try
{
gridViewNoMatch.ItemsSource = null;
}
catch { }
_client = new WebClient();
_client.OpenReadCompleted += (a, f) =>
{
if (!f.Cancelled && f.Error == null)
{
_listOfNoPatientsMatches = new List<NoPatientList>();
MemoryStream _memoryStream = new MemoryStream();
f.Result.CopyTo(_memoryStream);
_memoryStream.Position = 0;
StreamReader _streamReader = new StreamReader(_memoryStream);
string _memoryStreamToText = _streamReader.ReadToEnd();
List<NoPatientList> _deserializedNoPatientList = (List<NoPatientList>)Newtonsoft.Json.JsonConvert.DeserializeObject(_memoryStreamToText, typeof(List<NoPatientList>));
gridViewNoMatch.ItemsSource = _deserializedNoPatientList;
}
else
{
MessageBox.Show(f.Error.Message,
"Error", MessageBoxButton.OK);
}
};
_client.OpenReadAsync(new Uri(_serviceUri + "getnopatientlist"));
The issue is that the times on DateTime field appear to always 6 hours off.
Any ideas as to what is going on?
This is probably a time zone issue. Check that:
Your webservice is returning you dates/times in UTC format.
Your code is parsing these dates/times as UTC dates and times.