I am getting a system.outofmemory exception in my code:
While r1.Read()
menu1id = r1("id")
db.AddInParameter(command2, "#menu1id", DbType.Int32, menu1id)
r2 = db.ExecuteReader(command2)
command2.Parameters.Clear()
menu1heading = r1("Heading")
If r1("url") IsNot Nothing And r1("moduleid") = 0 Then
url = r1("url").ToString
If InStr(url, "file:///") > 0 Then
Dim builder As New StringBuilder
builder.Append("<a href=")
builder.Append(r1("url"))
builder.Append(" target=")
builder.Append(r1("urltarget"))
builder.Append(">")
builder.Append(menu1heading)
builder.Append("</a>")
level1.Add(builder.ToString)
Else
Dim builder As New StringBuilder
builder.Append("<a href=http://")
builder.Append(r1("url"))
builder.Append(" target=")
builder.Append(r1("urltarget"))
builder.Append(">")
builder.Append(menu1heading)
builder.Append("</a>")
level1.Add(builder.ToString)
End If
Else
Dim builder As New StringBuilder
builder.Append("<a href=~/Default.aspx?id=")
builder.Append(r1("id"))
builder.Append(">")
builder.Append(menu1heading)
builder.Append("</a>")
level1.Add(builder.ToString)
End If
While r2.Read
menu2id = r2("id")
db.AddInParameter(command3, "#menu2id", DbType.Int32, menu2id)
r3 = db.ExecuteReader(command3)
command3.Parameters.Clear()
menu2heading = r2("Heading")
If r2("url") IsNot Nothing And r2("moduleid") = 0 Then
Dim builder As New StringBuilder
builder.Append("<a href=http://")
builder.Append(r2("url"))
builder.Append(" target=")
builder.Append(r2("urltarget"))
builder.Append(menu2heading)
builder.Append("</a>")
level2.Add(builder.ToString)
Else
Dim builder As New StringBuilder
builder.Append("<a href=~/Default.aspx?id=")
builder.Append(r2("id"))
builder.Append(">")
builder.Append(menu2heading)
builder.Append("</a>")
level2.Add(builder.ToString)
End If
While r3.Read
menu3heading = r3("Heading")
menu3id = r3("id")
If r3("url") IsNot Nothing And r3("moduleid") = 0 Then
Dim builder As New StringBuilder
builder.Append("<a href=http://")
builder.Append(r3("url"))
builder.Append(" target=")
builder.Append(r3("urltarget"))
builder.Append(">")
builder.Append(menu3heading)
builder.Append("</a>")
level3.Add(builder.ToString)
Else
Dim builder As New StringBuilder
builder.Append("<a href=~/Default.aspx?id=")
builder.Append(r3("id"))
builder.Append(">")
builder.Append(menu3heading)
builder.Append("</a>")
level3.Add(builder.ToString)
End If
End While
r3.Close()
End While
r2.Close()
End While
r1.Close()
End While
r0.Close()
Please can you tell me how I go about diagnosing and fixing this exception? thanks a lot.
You would be a lot better off changing your logic to a single sql query that returns all of your menu items in one go and then iterating this dataset to build your menu.
You could just try
SELECT id, DepartmentID, GroupingID, Heading, OrderID,
Publish, moduleid, url, urltarget
FROM Grouping
WHERE (DepartmentID = 0 AND Publish <> 0)
ORDER BY OrderID
This returns all of the data that the above queries return, including the GroupingID which determines the tree structure. You should be able to load up the results into a collection of objects and then query them using LINQ to build your menu.
Copy your data into the following class annd then use LINQ on a list of them:
public class DataClass
{
public string Id { get; set; }
public string DepartmentID { get; set; }
public string GroupingID { get; set; }
public string Heading { get; set; }
public string OrderID { get; set; }
public string Publish { get; set; }
public string Moduleid { get; set; }
public string Url { get; set; }
public string Urltarget { get; set; }
public List<DataClass> Children { get; set; }
public DataClass(string id, string departmentID, string groupingID, string heading, string orderID, string publish, string moduleid, string url, string urltarget)
{
Id = id;
DepartmentID = departmentID;
GroupingID = groupingID;
Heading = heading;
OrderID = orderID;
Publish = publish;
Moduleid = moduleid;
Url = url;
Urltarget = urltarget;
}
}
The method uses a lot of string concatenations, each of them creating lots of new temporary string objects which use up your heap space. This can be avoided by using a StringBuilder to create the concatenated string. The StringBuilder works not only more memory-efficient when concatenating lots of strings, it will also be much faster:
Dim builder As New StringBuilder()
builder.Append("<a href=")
builder.Append("r0("url"))
builder.Append("target=")
builder.Append(r0("urltarget"))
builder.Append(menu0heading)
builder.Append("</a>")
Dim str as String
str = builder.ToString()
Note: As pointed out by David Neale in the comments, in this context it would be better to use a TagBuilder (or an XmlWriter) to create the HTML/XML document tree.
Related
I am trying to connect to SQL Server from the Web API and return a result set as JSON. But my code shown here is not working as expected. I am trying to return the entire query response as a JSON:
[HttpGet]
public HttpResponseMessage Getdetails(string ROOM)
{
string commandText = "SELECT * from [TDB].[dbo].[results_vw] where ROOM = #ROOM_Data";
string connStr = ConfigurationManager.ConnectionStrings["TDBConnection"].ConnectionString;
var jsonResult = new StringBuilder();
using (SqlConnection connection = new SqlConnection(connStr))
{
SqlCommand command = new SqlCommand(commandText, connection);
command.Parameters.Add("#ROOM_Data", SqlDbType.VarChar);
command.Parameters["#ROOM_Data"].Value = ROOM;
connection.Open();
var reader = command.ExecuteReader();
if (!reader.HasRows)
{
jsonResult.Append("[]");
}
else
{
while (reader.Read())
{
jsonResult.Append(reader.GetValue(0).ToString());
}
}
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
response.Content = new StringContent(jsonResult.ToString());
connection.Close();
return response;
}
}
This code returns this result:
333838362692368203368203368203362692368203359544362692368203362692368203362692368203368203
Where I am expecting the JSON as
{"data":
[
{"R_ID":"368203","ROOM":"K2"},
{"R_ID":"368203","ROOM":"K2"}
]}
Now I created a model class called DatabaseResult to store the response but I am not sure how I can store the result in to the model class in the controller
public class DatabaseResult
{
public int r_id { get; set; }
public string room { get; set; }
}
The current result is because you are only return the the value from the first column of each row and adding it to the string builder.
Create a new instance of the model and populate it using the values from the reader for each row.
[HttpGet]
public IHttpActionResult Getdetails(string ROOM) {
string commandText = "SELECT * from [TDB].[dbo].[results_vw] where ROOM = #ROOM_Data";
string connStr = ConfigurationManager.ConnectionStrings["TDBConnection"].ConnectionString;
var jsonResult = new StringBuilder();
using (SqlConnection connection = new SqlConnection(connStr)) {
using (SqlCommand command = new SqlCommand(commandText, connection)) {
command.Parameters.Add("#ROOM_Data", SqlDbType.VarChar);
command.Parameters["#ROOM_Data"].Value = ROOM;
connection.Open();
List<DatabaseResult> records = new List<DatabaseResult>();
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
var row = new DatabaseResult {
r_id = (int)reader["r_id"],
room = (string)reader["room"],
//...other properties.
};
records.Add(row);
}
return Ok(records);
}
}
}
}
The above uses the column names as the indexer to get the values from the reader.
public class GetAreaFromCity : System.Web.Services.WebService
{
[WebMethod]
public GetAreaByCityId ClassForGetCIty(int City_Id)
{
string CS =ConfigurationManager.ConnectionStrings["FOODINNConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("spGetCityById",con);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter parameter = new SqlParameter("#ID", City_Id);
//To assiate this parameter object with cmd object
cmd.Parameters.Add(parameter);
GetAreaByCityId GETAreaByCityId =new GetAreaByCityId();
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
//as WeakReference read data wewant ToString retrive Column value & then polute this property City_Id values
while (reader.Read()){
GETAreaByCityId.City_Id = Convert.ToInt32(reader["City_Id"]);
GETAreaByCityId.Area_Id = Convert.ToInt32(reader["Area_Id"]);
}
return GETAreaByCityId;
//ToString return sql
}
}
}
that's my codes for service page
public class GetAreaByCityId
{
public int Ca_Id {get;set; }
public int City_Id { get; set; }
public int Area_Id { get; set; }
}
that's the class for getting the Area by city
Create Proc [dbo].[spGetCityById]
#ID int
as
Begin
Select Area_Id from
CITIES_AREA where City_Id = #ID
End
GO
and above the database procedure which is data can be retrieve
System.IndexOutOfRangeException: City_Id
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName)
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
at System.Data.SqlClient.SqlDataReader.get_Item(String name)
at WebApplication1.GetAreaFromCity.ClassForGetCIty(Int32 City_Id) in c:\Users\Mudassir\Documents\Visual Studio 2013\Projects\WebApplication1\WebAppli
the above error i dont know whats the problem
Your stored procedure is returning only Area_Id. Your code in the "while loop" while (reader.Read()){ is attempting to read data from two columns:
City_Id
Area_Id
You could add the column City_Id to the result set for your stored procedure query, BUT you already have that value because you are passing it to the stored procedure as a parameter.
Easiest fix is probably to just change this line:
GETAreaByCityId.City_Id = Convert.ToInt32(reader["City_Id"]);
to this:
GETAreaByCityId.City_Id = City_Id;
I am creating a web service via asp.net. I want to pull as a list the Address's of the Fuel Stops as shown below unfortunately what I am getting is the string name of address's as shown below from the xml...
Unfortunately when I do run this I get a xml file filled with
-<FuelStop>
<Physical_Address_Street>Physical_Address_Street</Physical_Address_Street>
<Physical_Address_Local>Physical_Address_Local</Physical_Address_Local>
<Physical_Address_State>Physical_Address_State</Physical_Address_State>
<Physical_Address_Zip>Physical_Address_Zip</Physical_Address_Zip>
<Phone_Number>Phone_Number</Phone_Number>
</FuelStop>
Versus getting the address info that is populated in the database.
Below is my code.
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.ComponentModel
Imports System.Data
Imports System.Data.SqlClient
<System.Web.Services.WebService(Namespace:="http://watersports.com 8010/", Description:="Holds Fuel Stop and Shelter information", Name:="ShelterandFuelService")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ToolboxItem(False)> _
Public Class Service1
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function GetAddresses(ByVal skip As Integer, ByVal take As Integer) As FuelStop()
Dim sqlCon As New SqlConnection
Dim resultList = New List(Of FuelStop)()
Try
sqlCon.ConnectionString = "Data Source=google.watersports.com;Initial Catalog=myDb;Persist Security Info=True;Connect Timeout=30;User ID=****;Password=******"
Dim command As New SqlCommand("SELECT #Physical_Address_Street, #Physical_Address_Local, #Physical_Address_State, #Physical_Address_Zip, #Phone_Number FROM Gas_Stations WHERE Location_Type = 1")
command.Parameters.Add("#Physical_Address_Street", SqlDbType.VarChar, 50).Value = "Physical_Address_Street"
command.Parameters.Add("#Physical_Address_Local", SqlDbType.VarChar, 50).Value = "Physical_Address_Local"
command.Parameters.Add("#Physical_Address_State", SqlDbType.VarChar, 50).Value = "Physical_Address_State"
command.Parameters.Add("#Physical_Address_Zip", SqlDbType.VarChar, 50).Value = "Physical_Address_Zip"
command.Parameters.Add("#Phone_Number", SqlDbType.VarChar, 50).Value = "Phone_Number"
command.Connection = sqlCon
sqlCon.Open()
'command.ExecuteNonQuery()
Using reader = command.ExecuteReader()
While reader.Read()
Dim fuelStop = New FuelStop()
fuelStop.Physical_Address_Street = reader.GetString(0)
fuelStop.Physical_Address_Local = reader.GetString(1)
fuelStop.Physical_Address_State = reader.GetString(2)
fuelStop.Physical_Address_Zip = reader.GetString(3)
fuelStop.Phone_Number = reader.GetString(4)
resultList.Add(fuelStop)
End While
End Using
Catch ex As Exception
sqlCon.Close()
Finally
sqlCon.Close()
End Try
Return resultList.Skip(skip).Take(take).ToArray()
Based on the Sql Command below I will return possibly a few hundred address's how do I incorporparate this into my vb.net logic to return the list of address's?
Dim command As New SqlCommand("SELECT Physical_Address_Street, Physical_Address_Local, Physical_Address_State, Physical_Address_Zip, Phone_Number FROM Gas_Stations WHERE Location_Type = 1")
And here is my Fuel stop class
Public Class FuelStop
Property Physical_Address_Street As String
Property Physical_Address_Local As String
Property Physical_Address_State As String
Property Physical_Address_Zip As String
Property Phone_Number As String
End Class
The below code should give you what you are looking for. I am not sure if WebMethods can return custom objects like the Address object below. If it can not do that then you could convert the return type to String() and turn each Address object into a json object.
If I was doing this from scratch, I would just return a single JSON array that contained a list of Address objects.
Also, I have included in this method signature a skip and take parameter, as you mentioned in your comment, the web service sql query is unbounded so it could return hundreds of addresses. By using the skip and take you can limit how many are returned at once and can page through them from the client. To reduce the size of the sql query you could pass the skip and take parameters into the sql query to reduce the number of rows returned, which would improve performance as the number of addresses grows.
There are additional safety checks you can include in the below code to prevent errors but this can get you moving.
<WebMethod> _
Public Function GetAddresses(skip As Integer, take As Integer) As Address()
Dim resultList = New List(Of Address)()
Using sqlCon As New SqlConnection()
sqlCon.ConnectionString = "Data Source=google.watersports.com;Initial Catalog=myDb;Persist Security Info=True;Connect Timeout=30;User ID=****;Password=******"
Dim command As New SqlCommand("SELECT Physical_Address_Street, Physical_Address_Local, Physical_Address_State, Physical_Address_Zip, Phone_Number FROM Gas_Stations WHERE Location_Type = 1")
sqlCon.Open()
Using reader = command.ExecuteReader()
While reader.Read()
Dim addr = New Address()
addr.Physical_Address_Street = reader.GetString(0)
addr.Physical_Address_Local = reader.GetString(1)
addr.Physical_Address_State = reader.GetString(2)
addr.Physical_Address_Zip = reader.GetString(3)
addr.Phone_Number = reader.GetString(4)
resultList.Add(addr)
End While
End Using
End Using
Return resultList.Skip(skip).Take(take).ToArray()
End Function
Below is a temp class object that is used above.
public class Address
{
public string Physical_Address_Street { get; set; }
public string Physical_Address_Local { get; set; }
public string Physical_Address_State { get; set; }
public string Physical_Address_Zip { get; set; }
public string Phone_Number { get; set; }
}
I have DataReader that holds the results from a stored procedure caal. The results consist of two fields ...
UserID
UserName
Normally I bind these results to an ASP.NET dropdownlist control ...
ddlUserList.DataSource = rdr // rdr is the DataReader
ddlUserList.DataTextField = "UserName"
ddlUserList.DataValueField = "UserID"
ddlUserList.DataBind()
However I am now trying to accomplish the same thing using jQuery AJAX. What I am stuck on is how to manually convert the dataset held in the DataReader to JSON. How are multiples values separated? Does this look correct?
{{"UserID":1, "UserName":"Bob"}, {"UserID":2, "UserName":"Sally"},{"UserID":3, "UserName":"Fred"}}
I realize there are libraries out there such as JSON.NET to handle the serialization but I am in the learning stage now and want to make sure I understand everything from the bottom up.
Was wondering if you have tried using System.Web.Script.Serialization.JavaScriptSerializer library?
You can look at Rick Stahl's blog on this:
http://www.west-wind.com/weblog/posts/737584.aspx
Or you could also do something like create a method that will pull out data from the datareader and place it in a list of objects. (See code below). These list of object will be serialized using the JavaScriptSerializer library.
Hope this helps!
public class User
{
public int UserId { get; set; }
public string UserName { get; set;}
}
public class DataLayer
{
public string GetUsers(string connString)
{
string result = null;
List<User> users = null;
// get data using SqlReader
using(var conn = new SqlConnection(connString))
{
using(var cmd = new SqlCommand{ Connection = conn, CommandText = "SELECT * FROM Users", CommandType = CommandType.Text })
{
conn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if(!reader.HasRows)
return null;
//convert data reader to a list of user objects
users = (List<User>)ConvertToList<User>(ref reader);
conn.Close();
}
}
//convert list of objects in list to json objects
var jsonSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
result = jsonSerializer.Serialize(users);
return result;
}
public static IList<T> ConvertToList<T>(ref SqlDataReader reader)
{
IList<T> result = null;
if (reader.IsClosed)
return result;
result = new List<T>();
T item = default(T);
while (reader.Read())
{
//create item instance
item = (T)Activator.CreateInstance<T>();
//get class property members
var propertyItems = item.GetType().GetProperties();
//populate class property members with data from data reader
for (int ctr = 0; ctr < reader.FieldCount; ctr++)
{
if(reader.GetName(ctr) == propertyItems[ctr].Name)
propertyItems[ctr].SetValue(item, UtilsHelper.GetValue<string>(reader[ctr]), null);
}
//add item to list
result.Add(item);
}
reader.Close();
reader.Dispose();
return result;
}
}
I've decided to start another thread based on the responses I got in this thread:
Asp.Net: Returning a Reader from a Class
I was returning a reader, but members have suggested I'd be better off returning a Dataset instead and also try to seperate the data access tier from the presentation tier.
This is what I have so far:
//my class methods
public DataSet GetSuppliers()
{
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("con_spSuppliersList", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#blogid", HttpContext.Current.Request.QueryString["p"]);
return FillDataSet(cmd, "SuppliersList");
}
//my FillDataSet method
private DataSet FillDataSet(SqlCommand cmd, string tableName)
{
SqlConnection conn = new SqlConnection(connectionString);
cmd.Connection = conn;
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
try
{
conn.Open();
adapter.Fill(ds, tableName);
}
finally
{
conn.Close();
}
return ds;
}
// on my ascx page I call the method like so:
protected void Page_Load(object sender, EventArgs e)
{
//instantiate our class
MyClass DB = new MyClass();
// grab the table of data
DataTable dt = DB.GetSuppliers().Tables["SuppliersList"];
//loop through the results
foreach (DataRow row in dt.Rows)
{
this.supplierslist.InnerHtml += Server.HtmlEncode(row["Address"].ToString()) + "<br/>";
this.supplierslist.InnerHtml += "<b>Tel: </b>" + Server.HtmlEncode(row["Telephone"].ToString()) + "<p/>";
}
}
}
Would anyone like to suggest improvements?
Is my loop 'data tier' or 'presentation tier', should the loop be inside the class and I just return a formatted string instaed of a dataset?
Thanks for all the great advice
I also would use a Typed DataSet or create your own class that holds the properties so you are not dealing with strings like row["Address"] you would say object.Address and get compile time checking.
DataSets have a lot of built in functionality that is nice but also caries with it a lot of overhead that might not be needed in something simple. Creating a simple class with properties and passing that out of your data access layer is probably the simplest way to implement what you want.
Something like this on the DAL (Data Access Layer) side:
//Also pass in the blogID dont have the DAL get the value from the UI layer..
//make the UI layer pass it in.
public IList<Supplier> GetSuppliers(string connectionString, int blogID)
{
IList<Supplier> suppliers = new List<Supplier>();
//wrap with the using statements
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("con_spSuppliersList", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#blogid", blogID);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
suppliers.Add(new Supplier
{
Address = reader.GetString(0),
Telephone = reader.GetString(1)
});
}
}
}
return suppliers;
}
}
public class Supplier
{
//I would have Address an object....but you have as string
//public Address Address { get; set; }
public string Address { get; set; }
public string Telephone { get; set; }
}
//Example if you went with Address class...
public class Address
{
//Whatever you want in the address
public string StreetName { get; set; }
public string Country { get; set; }
public string Region { get; set; }
public string City { get; set; }
}
One thing you should get in the habit of doing is calling Dispose() on your SqlConnection. The best pattern to do this is to use the using statement, which will automatically dispose of it for you. It looks like this:
private DataSet FillDataSet(SqlCommand cmd, string tableName)
{
using(SqlConnection conn = new SqlConnection(connectionString))
{
cmd.Connection = conn;
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
try
{
conn.Open();
adapter.Fill(ds, tableName);
}
finally
{
conn.Close();
}
return ds;
}
}
What you have in the foreach loop in Page_Load, is presentation logic (layout), and IMO this should not be in the code-behind of your page, but in the markup.
I'd suggest that instead of using a foreach loop to construct the HTML output, you should use a databound control (such as a asp:Repeater, DataList or GridView). Then you can bind the repeater to your dataset or datatable and have all the markup where it belongs (in the ASCX file). See this page for an example.
As a general note: you can find lots of tutorials on www.asp.net, e.g. about data access: http://www.asp.net/%28S%28pdfrohu0ajmwt445fanvj2r3%29%29/learn/data-access/