DataTable to Json using jquery - asp.net

I'm trying to execute a web service which returns a DataTable with the following piece of code:
$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
//do things
}
});
If the webservice returns a class then it works so it has nothing to do with the input paramters etc. It only fails when the web method returns a datatable (the datatable only has 2 columns and 2 rows for the test I'm doing).
The WebService class is decorated with the [ScriptService] attribute so I thought that ASP.NET would automatically serialize the return value as JSON. It doesn't seem to work with datatable.
The only solution I've found was to return a string (a manually JSON serialized object) but it doesn't seem right to me to do it this way.
I'm using Visual Studio 2008 with .Net 3.5

In the end, I've decided to use the JavaScriptSerializer class to convert the DataTable into a JSON string.
Unfortunately, this class doesn't work with a DataTable so I converted the DataTable into a list of dictionnaries and pass that list to the JavaScriptSerializer class. It takes only a few lines of code and it works fine.
Example in VB.net:
Public Function GetJson(ByVal dt As DataTable) As String
Dim serializer As System.Web.Script.Serialization.JavaScriptSerializer = New System.Web.Script.Serialization.JavaScriptSerializer()
Dim rows As New List(Of Dictionary(Of String, Object))
Dim row As Dictionary(Of String, Object)
For Each dr As DataRow In dt.Rows
row = New Dictionary(Of String, Object)
For Each col As DataColumn In dt.Columns
row.Add(col.ColumnName, dr(col))
Next
rows.Add(row)
Next
Return serializer.Serialize(rows)
End Function

Easiest way is to use the LINQ to DataSet extensions. First need to create a generic list (SearchSerialResults is just a DTO in this case) from the DataTable using LINQ to DataSet.
var resultItems = (from DataRow dr in _returnedData.AsEnumerable() select new SearchSerialResults {
ContractLineItem = (int) dr["fldContractLineItemID"],
SearchItem = (string) dr["Search Item"],
Customer = (string) dr["Customer"],
DeviceFound = (string) dr["Device Found"],
Country = (string) dr["Country"],
City = (string) dr["City"],
ContractNumber = (string) dr["Contract Number"],
QuoteNumber = (string) dr["Quote Number"],
BeginDate = (string) dr["Begin Date"],
EndDate = (string) dr["End Date"]
}).ToList();
_returnedData is the DataTable in this case. Step 2 is to do the conversion. In this case, I am returning a Json object for a jqGrid.
var jsonObject = new {
total = totalPages,
pageSize,
records = totalRecords,
rows = (from SearchSerialResults item in resultItems select new {
id = item.ContractLineItem,
cell = new [] {
item.ContractLineItem.ToString(),
item.SearchItem,
item.DeviceFound,
item.Customer,
item.ContractNumber,
item.QuoteNumber,
item.Country,
item.City,
item.BeginDate,
item.EndDate,
""
}
}).ToArray()
};
return Json(jsonObject) // for MVC

Json.NET has the ability to write DataSets/DataTables to JSON.
http://james.newtonking.com/archive/2008/09/06/dataset-datatable-serialization-with-json-net.aspx

http://schotime.net/blog/index.php/2008/07/27/dataset-datatable-to-json/

It works for very well for me with a WebService
Imports System.Web.Script.Serialization
Dim wsServicio As New ["YourWsInstance"]
Dim dsInstEstado As New DataSet
Dim sSql As String
sSql = " Your SQL Statement"
dsInstEstado = wsServicio.getData("YourWebServiceParameters")
Dim jsonString = DataTableToJSON(dsInstEstado.Tables("CA_INSTITUCION"))
Return Json(jsonString, JsonRequestBehavior.AllowGet)
Function DataTableToJSon(dt As DataTable) As Object
Dim arr(dt.Rows.Count - 1) As Object
Dim column As DataColumn
For i = 0 To dt.Rows.Count - 1
Dim dict As New Dictionary(Of String, Object)
For Each column In dt.Columns
dict.Add(column.ColumnName, dt.Rows(i)(column))
Next
arr(i) = dict
Next
Return arr
End Function

I must admit I'm not hugely surprised - DataTable basically breaks most of the rules of structured data. Why not simply project from the data-table into a typed object? A related question came up earlier... or if you know the schema of the DataTable just do the conversion in C#...
Manually building the JSON might work, but there are a lot of edge-cases to avoid; I'd rather let an existing framework handle it, to be honest.

.Net 3.5 has a JSONSerializer that should be able to handle a datatable. You may want to look at your service code again and try getting it to use that. Also, I put some code together to do it manually in this question.

Like Marc, I too am not surprised that the DataTable breaks your webservice/json exchange. I'd like to endorse Json.NET also.
But if you decide to not go with it, you still don't have to build the json manually. Just make your own lean custom class with all the properties you need and then return an array of that class. You will of course have to write code to "convert" your data table into your new class. I know, it could be a lot of code writing, but it's a lot less error prone then trying to manually make a json string.

I found this C# class very useful:
[Serializable]
public class TableMethod
{
private int m_total; public int total { get { return this.m_total; } set { this.m_total = value; } }
private int m_page; public int page { get { return this.m_page; } set { this.m_page = value; } }
private int m_records; public int records { get { return this.m_records; } set { this.m_records = value; } }
private IList<RowElement> m_rows; public IList<RowElement> rows { get { return this.m_rows; } set { this.m_rows = value; } }
public TableMethod()
{
this.m_records = 20;
this.m_total = 20;
this.m_page = 1;
}
}
[Serializable]
public class RowElement
{
public string id;
public string[] cell;
}

Related

Using Dapper, how do I pass in the values for a sql type as param?

I'm attempting to use dapper and pass into a stored procedure a list of integers which I have defined here using DDL
CREATE TYPE [dbo].[BrandIDSet] AS TABLE ([BrandID] INT NULL);
I've created this stored procedure:
CREATE PROCEDURE dbo.usp_getFilteredCatalogItems
#BrandIDSet [dbo].[BrandIDSet] READONLY
and attempting to pass in in c# code the value for that parameter as
public async Task<PaginatedCatalogItemsVM> GetFilteredCatalogItems(int pageSize, int pageNumber, List<int> brandIDSet)
{
..
string storedProcName = "dbo.usp_getFilteredCatalogItems";
paginatedItemsVM.CatalogItemResults = await connection.QueryAsync<CatalogItemVM>(storedProcName, new { BrandIDSet = brandIDSet }, commandType: CommandType.StoredProcedure);
However, dapper does not seem to be converting the list as expected. With the above code, it results in the following SQL getting executed
exec dbo.usp_getFilteredCatalogItems #BrandIDSet1=1
Which isn't right, since BrandIDSet1 is not the name of the parameter. Arg.
Thus, it results in
SqlException: #BrandIDSet1 is not a parameter for procedure usp_getFilteredCatalogItems.
How do I get the type to convert to the proper SQL?
You can pass DataTable for proper result. You can try the code below;
var dt = new DataTable();
dt.Columns.Add(new DataColumn("BrandID", typeof(int)));
foreach (var id in brandIDSet)
{
var row = dt.NewRow();
row["BrandId"] = id;
dt.Rows.Add(row);
}
string storedProcName = "dbo.usp_getFilteredCatalogItems";
paginatedItemsVM.CatalogItemResults = await connection.QueryAsync<CatalogItemVM>(storedProcName, new { BrandIDSet = dt }, commandType: CommandType.StoredProcedure);

problem with conversion of output from linq query to list<> in asp.net

I have a Software titles class which is defined as follows:
public class SoftwareTitles
{
string softwareTitle;
string invoiceNumber;
public SoftwareTitles(string softwareTitle, string invoiceNumber)
{
this.softwareTitle = softwareTitle;
this.invoiceNumber = invoiceNumber;
}
public string InvoiceNumber
{
get
{
return this.invoiceNumber;
}
}
public string SoftwareTitle
{
get
{
return this.softwareTitle;
}
}
}
and i'm getting software titles and invoice numbers from my linq query which i want to store in a list using the following code:
List<SoftwareTitles> softwareTitlesList = new List<SoftwareTitles>();
var result = (from CustomersRecord custRecords in custRecordContainer select new { InvoiceNumber = custRecords.InvoiceNumber, SoftwareTitle = custRecords.InvoiceNumber }).ToList();
softwareTitlesList = result;
But it is freaking out giving me this error:
Error 1 Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.List<SoftwareTitles>'
Can any one help me ?
Thanks in anticipation
I think the problem is that you are creating an anonymous type:
select new { InvoiceNumber = custRecords.InvoiceNumber, SoftwareTitle = custRecords.InvoiceNumber }
and you are trying to build a list of SoftwareTitles. I am not 100% on the syntax, but try using:
select new SoftwareTitle( custRecords.SoftwareTitle, custRecords.InvoiceNumber)
Your select code
select new {
InvoiceNumber = custRecords.InvoiceNumber,
SoftwareTitle = custRecords.InvoiceNumber
}
is returning an annonymous type. You can't put your annonymous type into a List<SoftwareTitles>.
Two solutions:
1) You can select an annonymous type if you let the compiler determine the type of your list using the var keyword
var myList = from CustomersRecord custRecords
in custRecordContainer
select new {
InvoiceNumber = custRecords.InvoiceNumber,
SoftwareTitle = custRecords.InvoiceNumber
}).ToList();
2) Map to a SoftwareTitle object in your Select
List<SoftwareTitle> myList = from CustomersRecord custRecords
in custRecordContainer
select new SoftwareTitle {
InvoiceNumber = custRecords.InvoiceNumber,
SoftwareTitle = custRecords.InvoiceNumber
}).ToList();
I would guess you probably want to do it the 2nd way. Using a list of annonymous type is only really useful as an intermediate step in a function, as you generally can't pass it as a function paramater somewhere else.

Datastructures problem in asp.net/c#

I have a e-commerce web application which allows users to buy software components in my website. I'm retrieving the invoice number and the software component title that was bought by the user from UserTransactionHistory table in sql server. I'm storing them in arraylist with the help of a SoftwareTitles Class
public class SoftwareTitles
{
string softwareTitle;
string invoiceNumber;
public SoftwareTitles(string softwareTitle, string invoiceNumber)
{
this.softwareTitle = softwareTitle;
this.invoiceNumber = invoiceNumber;
}
string InvoiceNumber
{
get
{
return this.invoiceNumber;
}
}
string SoftwareTitle
{
get
{
return this.softwareTitle;
}
}
}
}
And I'm adding this class to arraylist in this manner.
ConnectionToSql con1 = new ConnectionToSql();
string connectionString = con1.ConnectionStringMethod();
SqlConnection sqlConnection = new SqlConnection(connectionString);
SqlCommand cmd2 = new SqlCommand("SelectionOfSoftwareTitles", sqlConnection);
cmd2.CommandType = CommandType.StoredProcedure;
sqlConnection.Open();
SqlDataReader dr2 = cmd2.ExecuteReader();
if (dr2.HasRows)
{
while (dr2.Read())
{
String softwareTitle = (String)dr2[0];
String invoiceNumber = (String)dr2[1];
softwareTitlesArray.Add(new SoftwareTitles(softwareTitle, invoiceNumber));
int i = 0;
}
}
sqlConnection.Close();
dr2.Close();
But when I want to retrieve all the software titles that are associated with a certain Invoice number. I'm not able to do it.
Am i doing it properly ?? Is arraylist appropriate data structure for such operation ?? How should I do it ?
I would personally use a non-generic list object.
To declare:
List<Software> softwareTitles= New List<Software>();
And the object software:
if (dr.HasRows)
{
while (dr.Read())
{
string title = dr["TITLE_COLUMN"];
int invoice = dr["INVOICE_COLUMN"];
Software s = new Software();
s.Title = title;
s.Invoice = invoice;
softwareTitles.add(s);
}
}
and then you can traverse through the list using a simple loop and counter like, softwareTitles(i) or you can even use LINQ to accomplish whatever you want to do.
e.g.
for (i=0; i<softwareTitles.Count;i++)
{
if (softwareTitles[i].Invoice==213)
{
Console.WriteLine(softwareTitles[i].Title);
}
}
Somthing like that. Sorry I am using VB.NET lately, so my C# has become rusty. But it seems correct
Use Generic List Collection to add the Objects and Linq to Query the Records.

Access to QueryString / Session from a static Method?

I use ASP.Net and a static WebMethod / PageMethod to do some async work.
My question is how to access my queryStrings and Session variables here?
I tried "HttpContext.Current" and a lot of information is available here, but not my QueryString nor my Session and I don't know why.
[WebMethod(EnableSession=true)]
public static object Update(string time)
{
string timer;
string lastBidder;
string price;
//Countdown timer
DateTime dt = DateTime.Parse(time);
dt = dt.AddSeconds(-1.0);
timer = dt.ToString("HH:mm:ss");
int auctionID = 6;
if (!int.TryParse(HttpContext.Current.Request.QueryString["id"], out auctionID))
throw new Exception("Seitenaufruf ohne ID");
Business.AuctionHandling ah = new Business.AuctionHandling();
DAL.Auktion auktion = ah.GetSingleAuction(auctionID);
price = auktion.AktuellerPreis.ToString("###0.00");
//this.gvHistory.DataBind();
List<DAL.Biethistorie> his = ah.GetBidHistoryForAuction(auctionID);
if (his.Count > 0)
{
lastBidder = his[0].Benutzer.Benutzername;
//History fett
//gvHistory.Rows[0].Font.Bold = true;
//gvHistory.Rows[0].ForeColor = System.Drawing.ColorTranslator.FromHtml("#3B4D5F");
//lblHöchstesGebot.ForeColor = System.Drawing.Color.Black;
}
else
{
lastBidder = Helper.StringHelper.AuctionDeatil_NoBidder;
//lblHöchstesGebot.ForeColor = System.Drawing.Color.Red;
}
return new
{
valueTimer = timer,
valuePrice = price,
valueLastBidder = lastBidder
};
}
The QueryString is in the Request property.
System.Web.HttpContext.Current.Request.QueryString
But the Session is in there:
System.Web.HttpContext.Current.Session
Out of interest why aren't you just passing the information you need to the web method as you are calling it?
I had a similar problem. I had a number of static methods I was using to help manage my Cache and Session. Luckily, you can pass a reference to the Cache or Session into your moethods like this:
public static void DoSomething(System.Web.SessionState sessn)
And then access your session by using the sessn object.
THIS IS LATE REPLY BUT WILL HELP OTHERS AND MARK IT AS ANSWER ..well u have to post your code on how you are calling that Update method. coz i am doing the same and im getting my querystring and the trick for that is you have to pass that in alongwith your get or post call like following
$.ajax({
type: "POST",
url: "" + getDirectoryPath() + getCurrentPageName() + "/SavePatientEpisodes?ApplicationInstanceID=" + querystring,
data: JSON.stringify({ PatientOne: patientOneData, PatientTwo: patientTwoData, PatientOneID: $("#tbPatient1").val(), PatientTwoID: $("#tbPatient2").val() }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
// Replace the div's content with the page method's return.
}
});
AND ACCESS IT as BELOW
_
Public Shared Function SavePatientEpisodes(ByVal PatientOne As List(Of Episode), ByVal PatientTwo As List(Of Episode), ByVal PatientOneID As String, ByVal PatientTwoID As String) As String
Dim dd As String = HttpContext.Current.Request.QueryString("ApplicationInstanceID")
Dim lang As Integer = toInt(HttpContext.Current.Session("UserID"))
return ""
End Function

ASP.NET: URI handling

I'm writing a method which, let's say, given 1 and hello should return http://something.com/?something=1&hello=en.
I could hack this together pretty easily, but what abstraction functionality does ASP.NET 3.5 provide for building URIs? I'd like something like:
URI uri = new URI("~/Hello.aspx"); // E.g. ResolveUrl is used here
uri.QueryString.Set("something", "1");
uri.QueryString.Set("hello", "en");
return uri.ToString(); // /Hello.aspx?something=1&hello=en
I found the Uri class which sounds highly relevant, but I can't find anything which does the above really. Any ideas?
(For what it's worth, the order of the parameters doesn't matter to me.)
Edited to correct massively incorrect code
Based on this answer to a similar question you could easily do something like:
UriBuilder ub = new UriBuilder();
// You might want to take more care here, and set the host, scheme and port too
ub.Path = ResolveUrl("~/hello.aspx"); // Assumes we're on a page or control.
// Using var gets around internal nature of HttpValueCollection
var coll = HttpUtility.ParseQueryString(string.Empty);
coll["something"] = "1";
coll["hello"] = "en";
ub.Query = coll.ToString();
return ub.ToString();
// This returned the following on the VS development server:
// http://localhost/Hello.aspx?something=1&hello=en
This will also urlencode the collection, so:
coll["Something"] = "1";
coll["hello"] = "en&that";
Will output:
Something=1&hello=en%26that
As far I know nothing here. So everybody has its own implementation.
Example from LinqToTwitter.
internal static string BuildQueryString(IEnumerable<KeyValuePair<string, string>> parameters)
{
if (parameters == null)
{
throw new ArgumentNullException("parameters");
}
StringBuilder builder = new StringBuilder();
foreach (var pair in parameters.Where(p => !string.IsNullOrEmpty(p.Value)))
{
if (builder.Length > 0)
{
builder.Append("&");
}
builder.Append(Uri.EscapeDataString(pair.Key));
builder.Append("=");
builder.Append(Uri.EscapeDataString(pair.Value));
}
return builder.ToString();
}
UPDATE:
You can also create extension method:
public static UriBuilder AddArgument(this UriBuilder builder, string key, string value)
{
#region Contract
Contract.Requires(builder != null);
Contract.Requires(key != null);
Contract.Requires(value != null);
#endregion
var query = builder.Query;
if (query.Length > 0)
{
query = query.Substring(1) + "&";
}
query += Uri.EscapeDataString(key) + "="
+ Uri.EscapeDataString(value);
builder.Query = query;
return builder;
}
And usage:
var b = new UriBuilder();
b.AddArgument("test", "test");
Please note that everything here is untested.
Just combined answers=>
public static class UriBuilderExtensions
{
public static void AddQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key)) throw new ArgumentNullException
("Key '{0}' already exists!".FormatWith(key));
x.Add(key, value);
b.Query = x.ToString();
}
public static void EditQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x[key] = value;
else throw new ArgumentNullException
("Key '{0}' does not exists!".FormatWith(key));
b.Query = x.ToString();
}
public static void AddOrEditQueryArgument(this UriBuilder b, string key, string value)
{
key = Uri.EscapeDataString(key);
value = Uri.EscapeDataString(value);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x[key] = value;
else
x.Add(key, value);
b.Query = x.ToString();
}
public static void DeleteQueryArgument(this UriBuilder b, string key)
{
key = Uri.EscapeDataString(key);
var x = HttpUtility.ParseQueryString(b.Query);
if (x.AllKeys.Contains(key))
x.Remove(key);
b.Query = x.ToString();
}
}
Half baked code. But should work well enough.
There's also the UriBuilder class
This is something that might appeal to you- recently at work I was looking at a way to "type" commonly used URL query string variables and so developed this interface:
'Represent a named parameter that is passed from page-to-page via a range of methods- query strings, HTTP contexts, cookies, session, etc.
Public Interface INamedParam
'A key that uniquely identfies this parameter in any HTTP value collection (query string, context, session, etc.)
ReadOnly Property Key() As String
'The default value of the paramter.
ReadOnly Property DefaultValue() As Object
End Interface
You can then implement this interface to describe a query string parameter, such an implementation for your "Hello" param might look like this:
Public Class HelloParam
Implements INamedParam
Public ReadOnly Property DefaultValue() As Object Implements INamedParam.DefaultValue
Get
Return "0"
End Get
End Property
Public ReadOnly Property Key() As String Implements INamedParam.Key
Get
Return "hello"
End Get
End Property
End Class
I developed a small (and very, very basic) class to help build URLs using these strongly typed parameters:
Public Class ParametrizedHttpUrlBuilder
Private _RelativePath As String
Private _QueryString As String
Sub New(ByVal relativePath As String)
_RelativePath = relativePath
_QueryString = ""
End Sub
Public Sub AddQueryParameterValue(ByVal param As INamedParam, ByVal value As Object)
Dim sb As New Text.StringBuilder(30)
If _QueryString.Length > 0 Then
sb.Append("&")
End If
sb.AppendFormat("{0}={1}", param.Key, value.ToString())
_QueryString &= sb.ToString()
End Sub
Public Property RelativePath() As String
Get
Return _RelativePath
End Get
Set(ByVal value As String)
If value Is Nothing Then
_RelativePath = ""
End If
_RelativePath = value
End Set
End Property
Public ReadOnly Property Query() As String
Get
Return _QueryString
End Get
End Property
Public ReadOnly Property PathAndQuery() As String
Get
Return _RelativePath & "?" & _QueryString
End Get
End Property
End Class
Here's my version (needs .NET4 or a ToArray() call on the Select)
var items = new Dictionary<string,string> { { "Name", "Will" }, { "Age", "99" }};
String query = String.Join("&", items.Select(i => String.Concat(i.Key, "=", i.Value)));
I thought the use of Dictionary might mean the items can get reordered, but that doesn't actually seem to be happening in experiments here - not sure what that's about.

Resources