Using an ObjectDataSource with a GridView in a dynamic scenario - c#

I have a search page that is tasked with searching 3.5 million records for individuals based on their name, customer ID, address, etc. The queries range from complex to simple.
Currently, this code relies on a SqlDataSource and a GridView. When a user types a serach term in and presses enter, the TextBoxChanged even runs a Search(term, type) function that changes the query that the SqlDataSource uses, adds the parameters, and rebinds the GridView.
It works well, but I've become obsessed with rewriting the code more efficiently. I want the paging to be done by SQL Server instead of the inefficiencies of a SqlDataSource in DataSet mode.
Enter the ObjectDataSource. Caveat: I have never used one before today.
I have spent the better part of the day putting together this class:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
/// <summary>
/// Summary description for MultiSearchData
/// </summary>
public class MultiSearchData
{
private string _connectionString = string.Empty;
private string _sortColumns = string.Empty;
private string _selectQuery = string.Empty;
private int _lastUpdate;
private int _lastRowCountUpdate;
private int _lastRowCount;
private SqlParameterCollection _sqlParams;
public MultiSearchData()
{
}
private void UpdateDate()
{
_lastUpdate = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
}
private string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
public string SortColumns
{
get { return _sortColumns; }
set { _sortColumns = value; }
}
public SqlParameterCollection SqlParams
{
get { return _sqlParams; }
set { _sqlParams = value; }
}
public string ConnectionString
{
get { return _connectionString; }
set { _connectionString = value; }
}
public string SelectQuery
{
get { return _selectQuery; }
set
{
if (value != _selectQuery)
{
_selectQuery = value;
UpdateDate();
}
}
}
public DataTable GetFullDataTable()
{
return GetDataTable(AssembleSelectSql());
}
public DataTable GetPagedDataTable(int startRow, int pageSize, string sortColumns)
{
if (sortColumns.Length > 0)
_sortColumns = sortColumns;
return GetDataTable(AssemblePagedSelectSql(startRow, pageSize));
}
public int GetRowCount()
{
if (_lastRowCountUpdate == _lastUpdate)
{
return _lastRowCount;
}
else
{
string strCountQuery = _selectQuery.Remove(7, _selectQuery.IndexOf("FROM") - 7);
strCountQuery = strCountQuery.Replace("SELECT FROM", "SELECT COUNT(*) FROM");
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(strCountQuery, conn))
{
if (_sqlParams.Count > 0)
{
foreach (SqlParameter param in _sqlParams)
{
cmd.Parameters.Add(param);
}
}
_lastRowCountUpdate = _lastUpdate;
_lastRowCount = (int)cmd.ExecuteScalar();
return _lastRowCount;
}
}
}
}
public DataTable GetDataTable(string sql)
{
DataTable dt = new DataTable();
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand GetCommand = new SqlCommand(sql, conn))
{
conn.Open();
if (_sqlParams.Count > 0)
{
foreach (SqlParameter param in _sqlParams)
{
GetCommand.Parameters.Add(param);
}
}
using (SqlDataReader dr = GetCommand.ExecuteReader())
{
dt.Load(dr);
conn.Close();
return dt;
}
}
}
}
private string AssembleSelectSql()
{
StringBuilder sql = new StringBuilder();
sql.Append(_selectQuery);
return sql.ToString();
}
private string AssemblePagedSelectSql(int startRow, int pageSize)
{
StringBuilder sql = new StringBuilder();
string originalQuery = ReplaceFirst(_selectQuery, "FROM", ", ROW_NUMBER() OVER (ORDER BY " + _sortColumns + ") AS ResultSetRowNumber FROM");
sql.Append("SELECT * FROM (");
sql.Append(originalQuery);
sql.Append(") AS PagedResults");
sql.AppendFormat(" WHERE ResultSetRowNumber > {0} AND ResultSetRowNumber <= {1}", startRow.ToString(), (startRow + pageSize).ToString());
return sql.ToString();
}
}
I don't know if it's pretty. It works. I give it a query in the ObjectCreating method:
protected void dataMultiSearchData_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
MultiSearchData info;
info = Cache["MultiSearchDataObject"] as MultiSearchData;
if (null == info)
{
info = new MultiSearchData();
}
info.SortColumns = "filteredcontact.fullname";
info.ConnectionString = "Data Source=SERVER;Initial Catalog=TheDatabase;Integrated Security=sspi;Connection Timeout=60";
info.SelectQuery = #"SELECT filteredcontact.contactid,
filteredcontact.new_libertyid,
filteredcontact.fullname,
'' AS line1,
filteredcontact.emailaddress1,
filteredcontact.telephone1,
filteredcontact.birthdateutc AS birthdate,
filteredcontact.gendercodename
FROM filteredcontact
WHERE fullname LIKE 'Griffin%' AND filteredcontact.statecode = 0";
e.ObjectInstance = info;
}
protected void dataMultiSearchData_ObjectDisposing(object sender, ObjectDataSourceDisposingEventArgs e)
{
MultiSearchData info = e.ObjectInstance as MultiSearchData;
MultiSearchData temp = Cache["MultiSearchDataObject"] as MultiSearchData;
if (null == temp)
{
Cache.Insert("MultiSearchDataObject", info);
}
e.Cancel = true;
}
Once the class has the query, it wraps it in paging friendly SQL and we're off to the races. I've implemented caching so that it can skip some expensive queries. Etc.
My problem is, this completely breaks my pretty little Search(term, type) world. Having ot set the query in the ObjectCreating method is completely harshing my vibe.
I've been trying to think of a better way to do this all day, but I keep ending up with a really messy...do it all in ObjectCreating model that just turns my stomach.
How would you do this? How can I keep the efficiency of this new method whilst have the organizational simplicity of my former model?
Am I being too OCD?

I determined that it can't be done. Furthermore, after benchmarking this class I found it performed no better than a SqlDataSource but was much more difficult to maintain.
Thus I abandoned this project. I hope someone finds this code useful at some point though.

Related

How to use Generic with SqlDataReader

I'm trying to come up with a way just to load a table from SQL Server into a class, without having to tell it anything. Basically, just create the class and have it know what to load, based on that. Here's what I have so far.
My question is, is there some way to keep from having to hard code the types, to call reader.readString, reader. readInt32, etc.. based on the FieldType?
private Int32? readInt32(SqlDataReader reader, string columnName)
{
Int32? result = null;
if (!reader.IsDBNull(reader.GetOrdinal(columnName)))
{
result = reader.GetInt32(reader.GetOrdinal(columnName));
};
return result;
}
public List<T> readTable(string table, string wherecls, string connStr)
{
List<T> result = new List<T>();
using (SqlConnection connection = new SqlConnection(connStr))
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select * from " + table;
if (wherecls.Length > 0) command.CommandText += " where " + wherecls;
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Object i = Activator.CreateInstance(typeof(T));
System.Reflection.FieldInfo[] fieldInfoList = typeof(T).GetFields();
foreach (System.Reflection.FieldInfo f in fieldInfoList)
{
if (f.FieldType == typeof(string)) f.SetValue(i, readString(reader, f.Name));
if (f.FieldType == typeof(Int32)) f.SetValue(i, readInt32(reader, f.Name));
if (f.FieldType == typeof(Int16)) f.SetValue(i, readInt16(reader, f.Name));
if (f.FieldType == typeof(byte)) f.SetValue(i, readByte(reader, f.Name));
if (f.FieldType == typeof(short)) f.SetValue(i, readShort(reader, f.Name));
}
result.Add((T)i);
}
}
}
}
return result;
}
Thank you,
Dan Chase
What you describe is a lot of work... and is exactly what tools like "dapper" already do. So my suggestion here: use dapper:
// Dapper adds a Query<T>(this DbConnection, ...) extension method
var data = connection.Query<T>(sql, args).AsList();
I would, however, say that string wherecls sends shivers down my spine - that sounds like a SQL injection nightmare. But... that's up to you.
Try this.
Make sure the type has a public default constructor--one that takes no arguments--and that the column names in the SQL string exactly match the name of the type's public properties.
namespace MyNamespace {
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Reflection;
public static class MyExtensions {
public static IEnumerable<T> Query<T>(this SqlConnection cn, string sql) {
Type TypeT = typeof(T);
ConstructorInfo ctor = TypeT.GetConstructor(Type.EmptyTypes);
if (ctor == null) {
throw new InvalidOperationException($"Type {TypeT.Name} does not have a default constructor.");
}
using (SqlCommand cmd = new SqlCommand(sql, cn)) {
using (SqlDataReader reader = cmd.ExecuteReader()) {
while (reader.Read()) {
T newInst = (T)ctor.Invoke(null);
for (int i = 0; i < reader.FieldCount; i++) {
string propName = reader.GetName(i);
PropertyInfo propInfo = TypeT.GetProperty(propName);
if (propInfo != null) {
object value = reader.GetValue(i);
if (value == DBNull.Value) {
propInfo.SetValue(newInst, null);
} else {
propInfo.SetValue(newInst, value);
}
}
}
yield return newInst;
}
}
}
}
}
}
Maybe my solution is a bit better. I populate type T using extension with handling null values and populating properties in order I like.
Example:
public async Task<ObservableCollection<T>> Search_data<T>()
{
var data = new ObservableCollection<T>();
try
{
using (OracleConnection con = new OracleConnection(connn_string))
{
con.Open();
OracleCommand cmd = new OracleCommand("MySchema.SomeTable", con)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("result", OracleDbType.RefCursor, ParameterDirection.Output);
using (OracleDataReader rdr = cmd_iskanje_apo.ExecuteReader())
{
while (await rdr.ReadAsync())
{
var item = Activator.CreateInstance<T>();
item.SetValue("NAME", rdr.IsDBNull(0) ? null : rdr.GetString(0));
item.SetValue("SURNAME", rdr.IsDBNull(1) ? null : rdr.GetString(1));
item.SetValue("ADDRESS", rdr.IsDBNull(2) ? null : rdr.GetString(2));
data.Add(item);
};
}
}
return data;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
Extension:
public static void SetValue<T>(this T _source, string _property_name, object _value)
{
_source.GetType().GetProperty(_property_name).SetValue(_source, _value);
}

Passing values between Windows form and a database

I faced a problem while trying to build a Windows form solution for a college assignment, and hope somebody can point out my mistake.
The solution is about a mobile shop. I have two classes, Apple and Android forms. I need to read the data in the database table, categorize the entries to either Android or Apple phones, and then display all phones in a list when the form loads.
I can successfully categorize phones, but when trying to read the entries, I always end up with the same entry twice in my list on the form, while the second entry doesn't appear at all.
I know I made a big stupid mistake while doing the connection but I can't find it!.
Here is my code:
public abstract class MobilePhone {
private Int32 phoneID;
private string operatingSystem;
private string make;
private string model;
public enum Condition { Poor, Fair, Good, Mint };
private Condition condition;
private decimal originalPrice;
private DateTime datePurchase;
private string description;
private clsDataConnection dbConnection;
//constructor
public MobilePhone(string make, string model, decimal originalPrice, DateTime datePurchase, Condition condition, string description) {
this.make = make;
this.model = model;
this.originalPrice = originalPrice;
this.datePurchase = datePurchase;
this.condition = condition;
this.description = description;
}
Not complete, but that's what is relevant:
public class ApplePhone : MobilePhone {
decimal ApproxValue;
public ApplePhone(string make, string model, decimal originalPrice, DateTime datePurchase, Condition condition, string description)
: base(make, model, originalPrice, datePurchase, condition, description) {
}
The Android class is the same but with different other functions.
class Shop {
clsDataConnection dbConnection;
const int NotAdded = -1; // invalid primary key
private string name;
private decimal ApproxValue;
private Int32 phoneID;
private string operatingSystem;
private string make;
private string model;
private MobilePhone.Condition condition;
private decimal originalPrice;
private DateTime datePurchase;
private string description;
Int32 Index;
private List<MobilePhone> phonesForSale;
//constructor
public Shop(string name) {
this.name = name;
}
MobilePhone phone;
public void SelectAll() {
dbConnection = new clsDataConnection();
dbConnection.Execute("SellectAllPhones");
}
public void FilterByOperatingSystem(string operatingSystem) {
dbConnection = new clsDataConnection();
dbConnection.AddParameter("#OperatingSystem", operatingSystem);
dbConnection.Execute("FilterByOperatingSystem");
}
public Int32 Count {
get {
//return the count of records
return dbConnection.Count;
}
}
public string DescribeCurrentPhone(int Index) {
Int32 phoneID;
string make;
string model;
MobilePhone.Condition condition;
decimal originalPrice;
DateTime datePurchase;
string description;
phoneID = Convert.ToInt32(phonesForSale[Index].PhoneID);
make = Convert.ToString(phonesForSale[Index].Make);
model = Convert.ToString(phonesForSale[Index].Model);
condition = phonesForSale[Index].GetCondition;
originalPrice = Convert.ToDecimal(phonesForSale[Index].OriginalPrice);
datePurchase = Convert.ToDateTime(phonesForSale[Index].DatePurchased);
description = Convert.ToString(phonesForSale[Index].Description);
//set up a new object of class list item
string listItemText = make + " " + "|" + " " + model + " " + "|" + " " + condition + " " + "|" + " " + "£" + Math.Round(originalPrice, 2) + " " + "|" + " " + datePurchase.ToShortDateString() + " " + "|" + " " + description;
return listItemText;
}
public List<MobilePhone> Allphones {
get {
phonesForSale = new List<MobilePhone>();
int count = Count;
Index = 0;
while (Index < count) {
phoneID = Convert.ToInt32(dbConnection.DataTable.Rows[Index]["PhoneId"]);
operatingSystem = Convert.ToString(dbConnection.DataTable.Rows[Index]["OperatingSystem"]);
make = Convert.ToString(dbConnection.DataTable.Rows[Index]["Make"]);
model = Convert.ToString(dbConnection.DataTable.Rows[Index]["Model"]);
string conditionString = Convert.ToString(dbConnection.DataTable.Rows[Index]["Condition"]);
originalPrice = Convert.ToInt32(dbConnection.DataTable.Rows[Index]["OriginalPrice"]);
datePurchase = Convert.ToDateTime(dbConnection.DataTable.Rows[Index]["DatePurchased"]);
description = Convert.ToString(dbConnection.DataTable.Rows[Index]["Description"]);
// Set Condition
if (conditionString == "Poor") {
condition = MobilePhone.Condition.Poor;
} else if (conditionString == "Fair") {
condition = MobilePhone.Condition.Fair;
} else if (conditionString == "Good") {
condition = MobilePhone.Condition.Good;
} else if (conditionString == "Mint") {
condition = MobilePhone.Condition.Mint;
}
//check Operating System
if (operatingSystem == "IOS") {
phone = new ApplePhone(make, model, originalPrice, datePurchase, condition, description);
//ApproxValue = ApplePhone.CalculateApproximateValue();
} else if (operatingSystem == "Android") {
phone = new AndroidPhone(make, model, originalPrice, datePurchase, condition, description);
//ApproxValue = AndroidPhone.CalculateApproximateValue();
}
Index++;
phonesForSale.Add(phone);
}
return phonesForSale;
}
}
And the form code is:
public partial class FormMain : Form {
public FormMain() {
InitializeComponent();
Shop shop = new Shop("");
}
private void FormMain_Load(object sender, EventArgs e) {
DisplayItems("");
}
protected int DisplayItems(string operatingSystem) {
Shop MyShop = new Shop("");
Int32 RecordCount;
Int32 Index = 0;
Int32 PID;
if (operatingSystem != "") {
MyShop.FilterByOperatingSystem(operatingSystem);
} else {
MyShop.SelectAll();
}
RecordCount = MyShop.Count;
ArrayList MyPhones = new ArrayList();
while (Index < RecordCount) {
// I Suspect this line is the problem but don't know how to fix it
PID = MyShop.Allphones[Index].PhoneID
string listItemText = MyShop.DescribeCurrentPhone(PID);
//add the new item to the list
MyPhones.Add(listItemText);
//increment the index
Index++;
}
listBox1.DataSource = MyPhones;
return RecordCount;
}
I am not used to connecting to databases, so any advice will be of help!
An example of an alternative to the DB connection you have made is below
List<MyPhone> myIPhoneList = new List<Myphone>();
List<MyPhone> myAndroidList = new List<Myphone>();
SqlConnection myDBConnection = new SqlConnection("MyConnectionString"); //DB Connection
SqlCommand dbCommand = new SqlCommand("SelectAllPhones"); //Stored Procedure
SqlDataReader recordReader = dbCommand.ExecuteReader(); //Execute
//Read records return in to phone objects
while (recordReader.Read()) {
var phoneField1 = recordReader["PhoneField1FromDatabase"];
var phoneField2 = recordReader["PhoneField2FromDatabase"];
//etc...
var myPhone = new MyPhone();
myPhone.Name = phoneField1;
//etc...
if (myPhone.OS == "iPhone")
myIPhoneList.Add(myPhone);
if (myPhone.OS = "Android")
myAndroidList.Add(myPhone);
}
Just a twist to Wheels answer really,
I'd personally put a filter on the stored-proc.
SqlCommand dbCommand = new SqlCommand("SelectAllPhones"); //Stored Procedure
becomes something like:
using (SqlConnection conn = new SqlConnection())
{
using (SqlCommand cmd = new SqlCommand("SelectAllPhones", conn))
{
cmd.Parameters.Add(new SqlParameter() { ParameterName = "#OS", SqlDbType = SqlDbType.VarChar, Direction = ParameterDirection.Input, Value = phoneOS });
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
// load your data...
}
}
}
Only because there is very little point dragging both sets of phone data (android/iphone) for each class. You may as well only pull back the data you require.
Of course the Stored-Proc will need an update to cater for the parameter.
something like:
AND PhoneOS = #OS
needs appending to your SQL condition.
clsDataConnection dbConnection; is unknown to me - is this a third party library or a class you've wrote and not included?
public Int32 Count
{
get
{
//return the count of records
return dbConnection.Count;
}
}
dbConnection.Count seems very non-standard. Doesn't read as if you're trying to get the number of rows, more the number of connections - which is invalid here.
dbConnection.DataTables[0].Rows.Count; would be a better way of determining the rows using your existing code, as currently it reads as if your counting the number of database connections which isn't what your after - and would be redundant if using either mine or Wheels as you wont need to know beforehand how many rows your about to process.

Memory leak with DataAdapter.Fill to DataSet

I have a simple program that grab data from the database and stored it in a DataSet through DataAdapter.Fill().
The problem I am facing is that the program's memory size keep increasing. Using process explorer, I monitor the virtual size of the program. The processes are run in a standalne build(not in unity editor).
2mins after the process is launch, the program sits at 824,444K virtual size, but leaving the program running for 30 mins, the program's virtual size increased to 1,722,340K.
(can't upload screenshot) https://drive.google.com/open?id=0B0DwzunTEqfKcDhHcXRmV2twUEE
The program only consist of 2 simple script: A helper class SQLController which manage the loading from database to dataset and a monobehavior script DataProducer that poll the database at regular interval for "update" using SQLController object.
SQLController:
using UnityEngine;
using System.Collections;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using UnityEngine.UI;
using System.Threading;
public class SQLController {
string connectionString;
string dataSource, catalog, uid, pwd;
DataSet dat_set;
public SQLController(string dataSource, string catalog, string uid, string pwd)
{
this.dataSource = dataSource;
this.catalog = catalog;
this.uid = uid;
this.pwd = pwd;
}
/// <summary>
/// Open a connectio to the database and query it for data with the statement input
/// </summary>
/// <param name="statement"> The query statement to be used to query the server</param>
/// <returns></returns>
public DataSet Load(string statement, string name)
{
connectionString = string.Format("data source={0};initial catalog={1};uid={2};pwd={3}", dataSource, catalog, uid, pwd);
using (SqlConnection dbcon = new SqlConnection(connectionString))
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(statement, dbcon))
{
dat_set = new System.Data.DataSet();
dbcon.Open();
dataAdapter.Fill(dat_set, name);
}
return dat_set;
}
}
DataProducer
using UnityEngine;
using System.Collections;
using System.Threading;
using System.Data;
using UnityEngine.UI;
using SimpleJSON;
public class DataProducer : MonoBehaviour {
public GameObject textObj;
private string[] _configData;
private string _outputText;
private SQLController _sqlController;
private bool _toggle = true;
private bool _updating = false;
private DataSet _dataSetCache;
private Thread _dataGrabThread;
// Use this for initialization
void Start () {
_configData = new string[5];
if (LoadFromConfigFile())
{
StartCoroutine(LoadFromDB());
}
}
// Update is called once per frame
void Update () {
textObj.GetComponent<Text>().text = _outputText;
}
public void OnDisable()
{
// stop any running thread
if (null != _dataGrabThread && _dataGrabThread.IsAlive)
{
_dataGrabThread.Abort();
}
}
IEnumerator LoadFromDB()
{
while (true)
{
if (_updating)
{
Debug.Log("Data System Poll DataBase ignored");
}
else
{
_updating = true;
_dataGrabThread = new Thread(Load);
_dataGrabThread.IsBackground = true;
_dataGrabThread.Start();
}
yield return new WaitForSeconds(10f);
}
}
void Load()
{
string statement;
if (_toggle)
{
_toggle = !_toggle;
statement = "SELECT TOP 100000 [AIIDX],[LASTATTACKDATE],[LASTRECEIVEDDATE],[DEVICEID],[INSTANCES],[ATTACKTYPE],[SEVERITY],[STATUS] FROM AI (NOLOCK)";
}
else
{
_toggle = !_toggle;
statement = "SELECT TOP 100000 [AIIDX],[LASTATTACKDATE],[LASTRECEIVEDDATE],[DEVICEID],[SEVERITY],[STATUS] FROM AI (NOLOCK)";
}
_sqlController = new SQLController(_configData[0], _configData[1], _configData[2], _configData[3]);
_outputText = "Loading";
_dataSetCache = _sqlController.Load(statement, "TestObject");
PrintDataSet();
_updating = false;
}
/// <summary>
/// Convert datatable into string and print it out through a text object
/// </summary>
void PrintDataSet()
{
if (null == _dataSetCache)
{
return;
}
DataTable dt = _dataSetCache.Tables["TestObject"];
if (null == dt)
{
return;
}
System.Text.StringBuilder builder = new System.Text.StringBuilder();
for (int i = 0; i < 20; ++i)
{
builder.AppendFormat("{0,-5}", (i + 1) + ".");
//comp.text += string.Format("{0,-5}", (i + 1) + ".");
DataRow dr = dt.Rows[i];
for (int j = 0; j < dt.Columns.Count; ++j)
{
builder.AppendFormat("{0, -30}", dr[dt.Columns[j]].ToString());
}
builder.Append("\n");
}
_outputText = builder.ToString();
builder = null;
}
bool LoadFromConfigFile()
{
string line;
using (System.IO.StreamReader file = new System.IO.StreamReader("./config.txt"))
{
if (file == null)
{
return false;
}
int index = 0;
while ((line = file.ReadLine()) != null)
{
if (index > _configData.Length)
{
Debug.LogError("Invalid Config file");
return false;
}
_configData[index++] = line;
}
//if the config file does not consist of 5 data
if (index < _configData.Length)
{
Debug.LogError("Invalid Config file");
return false;
}
return true;
}
}
}
I am not very sure what exactly causes the memory leak but when I changed the loading from threaded
_dataGrabThread = new Thread(Load);
to running in the main thread,
Load()
the process virtual size, though still increment, but at a slower rate. From 828,312K at 2 min to 1,083,908K at 40mins.

I Want to make function in different class(outside my form) for my first-Next-Previous-Last buttons for my All forms with same buttons

I Want to make function in different class(outside my form) for my first-Next-Previous-Last buttons for my All forms with same buttons .
How should I do it ? no Idea to access textboxes in other class.
My Code is here for Next button !!
<pre lang="c#">
private void CmdNext_Click(object sender, EventArgs e) {
ConStr Constring = new ConStr();
String CS = Constring.ConString("DBCS");
using (SqlConnection con = new SqlConnection(CS))
{
SqlDataAdapter DA = new SqlDataAdapter("Select * from FeesHead order by ID", con);
DataSet DS = new DataSet();
DA.Fill(DS, "FeesHead");
if ( i < DS.Tables["FeesHead"].Rows.Count-1)
{
i++;
this.TxtID.Text = DS.Tables["FeesHead"].Rows[i]["ID"].ToString();
this.TxtFees.Text = DS.Tables["FeesHead"].Rows[i]["Fees"].ToString();
this.TxtFeesHead.Text = DS.Tables["FeesHead"].Rows[i]["FeesHead"].ToString();
this.TxtRemarks.Text = DS.Tables["FeesHead"].Rows[i]["Remarks"].ToString();
if (i == DS.Tables["FeesHead"].Rows.Count - 1)
{
MessageBox.Show("This is Last Record");
}<pre lang="c#">
I am Trying Class library for above procedure My code is here !! I need some corrections !! I feed difficulty to access my form text box here so I left blank this space !!
//
`public void move_prev(String Con_String, String Table)
{
{
using (SqlConnection con = new SqlConnection(Con_String))
{
SqlDataAdapter DA = new SqlDataAdapter("Select * from " + Table +"", con);
DataSet DS = new DataSet();
DA.Fill(DS, Table);
for (int j=0; j<= DS.Tables[Table].Columns.Count;j++ )
{
int i = 0;
String name_of_column = DS.Tables[Table].Columns[j].ColumnName;
if (i == DS.Tables[Table].Rows.Count - 1 || i != 0)
{
i--;
= DS.Tables[Table].Rows[i][name_of_column].ToString();
if (i == 0)
{
MessageBox.Show("This is First Record");
}
}
}
}}
}
}
}
`//
You should try to have business logic in a separate class from the UI logic. I'm sure you've encountered the terms Model View Controller (MVC), Model View Presenter (MVP) and Model View, ViewModel (MVVM), all of which are approaches to achieving this.
In the context of this question, a simplified version of this can be achieved by encapsulating the 'first-prev-next-last' last logic in a business logic class and attaching an instance of it to the form class, (where attachment might be done via a factory class or method.) The business logic class exposes methods and properties that the UI form class can access.
public interface INavigation
{
// Property mapping field names to values
public Dictionary<string,string> CurrentRecordFields;
public void FirstRecord(void);
// ... plus all the other navigation methods.
}
The use of an interface here has several advantages: you can create a mock class for unit testing purposes, or you might have different implementations of the interface (for different tables in the database for example) any of which can re-use the same UI.
public class Navigation : INavigation
{
// Properties from INavigator
public Dictionary<string,string> CurrentRecordFields {get; set;}
// private fields
private string conString;
private string tableName;
// Constructor requires that the connection string and
// required table name be passed as arguments.
public Navigation(String Con_String, String Table)
{
conString = Con_String;
tableName = Table;
CurrentRecordFields = new Dictionary<string,string>();
}
// Note - this should really do some kind of error checking
// and exception handling.
public void FirstRecord(void)
{
using (SqlConnection con = new SqlConnection(conString))
{
SqlDataAdapter DA = new SqlDataAdapter("Select * from " + tableName +"", con);
DataSet DS = new DataSet();
DA.Fill(DS, tableName);
// Populate the Dictionary for the first row.
CurrentRecordFields.Empty();
foreach(var column in DS.Tables[tableName].Columns)
{
string columnName = column.ColumnName;
CurrentRecordFields.Add(columnName,
DS.Tables[tableName].Rows[0][columnName].ToString());
}
}
}
// ... and the rest of the navigation methods: prev, next, last, etc.
}
public partial class NavigationForm
{
private INavigation Navigator {get; set;}
public NavigationForm(INavigation navigator) :
base()
{
this.Navigator = navigator;
}
private void CmdFirst_Click(object sender, EventArgs e)
{
// Use the business logic class to do the DB work.
// See note above regarding error checking and exception handling.
Navigator.FirstRecord();
// Update the UI from the business logic properties.
this.TxtID.Text = Navigator.CurrentRecordFields["ID"];
this.TxtFees.Text = Navigator.CurrentRecordFields["Fees"];
this.TxtFeesHead.Text = Navigator.CurrentRecordFields["FeesHead"];
this.TxtRemarks.Text = Navigator.CurrentRecordFields["Remarks"];
}
// Etc., for the other form methods.
}
Then, when you create the form, you also create an instance of the Navigation business logic class and pass it to the form class constructor.
// In the main form or application
private void OnNavigateThroughRecords()
{
// Create the navigator for the required table.
INavigation navigator = new Navigation(this.ConectionString, this.SelectedTableName);
Form navigationForm = new NavigationForm(navigator);
}
This isn't a complete example, but it should be enough to get you started. For more information and plentiful examples, read the many rich resources on the various three-tiered architectures (MVC, MVP and MVVM being amongst the most well known).
EDIT
Changed the properties of the Navigator so that an unspecified collection of fields can be presented by the use of the Dictionary class. This is populated by the Navigator class methods with each entry containing the name of the column as the Key and the value for the record as the Value.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Configuration;
using System.Windows.Forms;
using System.Data;
namespace APMC
{
class Action_Btn
{
public void Navigation_Btns(String Con_Str, String Tab_Name, Control form, String Btn_Name,int ID)
{
int i = ID;
List<Control> TB_List = new List<Control>();
foreach (Control childcontrol in form.Controls)
{
if (childcontrol is GroupBox)
{
foreach (Control cc in childcontrol.Controls)
{
if (cc is TextBox)
{
TB_List.Add(cc);
}
}
}
}
using (SqlConnection con = new SqlConnection(Con_Str))
{
SqlDataAdapter da = new SqlDataAdapter("Select * from " + Tab_Name + "", con);
DataSet ds = new DataSet();
da.Fill(ds, Tab_Name);
if (Btn_Name == "CmdFirst")
{
if (ds.Tables[Tab_Name].Rows.Count > 0)
{
i = 0;
foreach (Control Txt_Name in TB_List)
{
String field_name = Txt_Name.Name.Substring(3);
Txt_Name.Text = ds.Tables[Tab_Name].Rows[i][field_name].ToString();
}
}
if (i == 0 )
{
MessageBox.Show("This is First Record");
}
}
if (Btn_Name == "CmdNext")
{
if (i < ds.Tables[Tab_Name].Rows.Count )
{
foreach (Control Txt_Name in TB_List)
{
String field_name = Txt_Name.Name.Substring(3);
Txt_Name.Text = ds.Tables[Tab_Name].Rows[i][field_name].ToString();
}
}
if (i == ds.Tables[Tab_Name].Rows.Count)
{
MessageBox.Show("This is Last Record");
}
}
if (Btn_Name == "CmdPrev")
{
if (i == ds.Tables[Tab_Name].Rows.Count || i != 1)
{
i = i - 2;
foreach (Control Txt_Name in TB_List)
{
String field_name = Txt_Name.Name.Substring(3);
Txt_Name.Text = ds.Tables[Tab_Name].Rows[i][field_name].ToString();
}
}
if (i == 0)
{
MessageBox.Show("This is First Record");
}
}
if (Btn_Name == "CmdLast")
{
if (ds.Tables[Tab_Name].Rows.Count - 1 >= i)
{
i = ds.Tables[Tab_Name].Rows.Count - 1;
foreach (Control Txt_Name in TB_List)
{
String field_name = Txt_Name.Name.Substring(3);
Txt_Name.Text = ds.Tables[Tab_Name].Rows[i][field_name].ToString();
}
}
if (i == ds.Tables[Tab_Name].Rows.Count)
{
MessageBox.Show("This is Last Record");
}
}
}
}
}
}

ASP.NET AJAX: How to get a JSON response obtained using also Session?

I'm new to ASP.NET but I've to deal with it quickly and powerfully (I know I'm asking the sky but I have to ;) ).
I have a page that uses google maps to show many markers. So I would like to ask to the server asynchronously the coordinates. I consider the best way to do this is to ask them json-serialized via AJAX and keeping track via ASP session the portions of the map already sent to the client and sending only those new.
I tryied in many ways but I found always issues that broke the magic.
My JS code is:
function map2Json() {
return JSON.stringify({ "categories": $("#typeSelector input:checked").map(function () {
return $(this).val();
}).get(),
"bounds": boundsDecorator(map.getBounds())
})
};
function getMarkers() {
$.ajax({
type: "POST",
url: "GetMarkers",
data: map2Json(),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: OnSuccess,
failure: OnFailure
});
}
While my ASP.NET C# code (WebService.cs) is:
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string GetMarkers(List<string> categories, Bounds bounds)
{
System.Diagnostics.Debug.WriteLine(categories.ToString());
System.Diagnostics.Debug.WriteLine(bounds.SouthWest.lat);
return JsonConvert.SerializeObject(getMarkers(categories, bounds));
}
private string getRectangle(Bounds bounds)
{
return string.Format("polygon(({0} {1}, {2} {1}, {2} {3}, {0} {3}, {0} {1}))", bounds.NorthEast.lng.ToString(CultureInfo.InvariantCulture), bounds.NorthEast.lat.ToString(CultureInfo.InvariantCulture), bounds.SouthWest.lng.ToString(CultureInfo.InvariantCulture), bounds.SouthWest.lat.ToString(CultureInfo.InvariantCulture));
}
private string STpolygon(string polygon)
{
return string.Format("geography :: STGeomFromText('{0}', 4326)", polygon);
}
private string typologiesListString(List<string> typologies)
{
return string.Format("({0})", string.Join(",", typologies));
}
private string formatMapNew(List<MapViewed> maps)
{
return string.Join(" OR ", maps.Select(x => string.Format("{0}.STIntersects(GeoLocation) = 1 and Tipologia = {1}", x.lastPatch.ToString(), x.typology)));
}
private DataSet getCoordinatesInPolygon(List<MapViewed> maps)
{
SqlCommand Command_coord = new SqlCommand();
Command_coord.CommandType = CommandType.Text;
SqlConnection conn = new SqlConnection("Server=localhost\\SQLEXPRESS;Database=geotagging;Trusted_Connection=Yes;Integrated Security=SSPI;");
Command_coord.Connection = conn;
string query = string.Format(#"
select [Ragione sociale], GeoLocation, Tipologia, Dettagli
from GEOTAG
where {0}", formatMapNew(maps));
Command_coord.CommandText = query;
DataSet coordinatesDS = new DataSet();
SqlDataAdapter coordinatesDA = new SqlDataAdapter();
coordinatesDA.SelectCommand = Command_coord;
try
{
coordinatesDA.Fill(coordinatesDS);
}
catch (System.Data.SqlClient.SqlException e)
{
System.Diagnostics.Debug.WriteLine("Error query: \n{0}\n Message = {1}", query, e.Message);
}
return coordinatesDS;
}
private bool IsEmpty(DataSet dataSet)
{
foreach (DataTable table in dataSet.Tables)
if (table.Rows.Count != 0) return false;
return true;
}
private List<Marker> getDBMarkers(DataSet coordinatesDS)
{
List<Marker> markers = new List<Marker>();
SqlGeography point;
Marker marker;
if (!IsEmpty(coordinatesDS))
{
foreach (DataRow row in coordinatesDS.Tables[0].Rows)
{
point = (SqlGeography)row[1];
marker = new Marker((string)row[0], new Point(point.Lat.Value, point.Long.Value), (string)row[2], (string)row[3]);
markers.Add(marker);
}
}
return markers;
}
private List<Marker> getMarkers(List<string> typologies, Bounds bounds)
{
return getDBMarkers(getCoordinatesInPolygon(updatedMaps(bounds, typologies)));
}
private List<MapViewed> updatedMaps(Bounds bounds, List<string> typologies)
{
List<MapViewed> maps = new List<MapViewed>();
MapViewed map;
foreach (string typology in typologies)
{
if (Session["mapViewed-" + typology] == null) Session["mapViewed-" + typology] = new MapViewed(typology);
map = (MapViewed)Session["mapViewed-" + typology];
map.mergeAndGetDiff(getRectangle(bounds));
maps.Add(map);
}
return maps;
}
Please don't focus too much on the ugliness of my code. I tryied also to use almost the same code in the code behind of my page but each method I use should be static so I can't use Session variables.
Question
What would you do?
Notes
I didn't put the code to insert markers on the map because there is no need.
Solution
Thank you all for the advices. Following your suggests I put this code in the code behind and it works well:
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Globalization;
using Microsoft.SqlServer.Types;
using System.Data.SqlTypes;
using System.Text;
public partial class _Default : System.Web.UI.Page
{
static SqlConnection conn = new SqlConnection("Server=localhost\\SQLEXPRESS;Database=geotagging;Trusted_Connection=Yes;Integrated Security=SSPI;");
protected void Page_Load(object sender, EventArgs e)
{
string[] typologies = new string[] { "Ricambi", "Concessionario", "Motocicli", "Officina" };
foreach (string typology in typologies)
{
HttpContext.Current.Session["mapViewed-"+typology] = new MapViewed(typology);
}
}
private static string getRectangle(Bounds bounds)
{
return string.Format("polygon(({0} {1}, {2} {1}, {2} {3}, {0} {3}, {0} {1}))", bounds.NorthEast.lng.ToString(CultureInfo.InvariantCulture), bounds.NorthEast.lat.ToString(CultureInfo.InvariantCulture), bounds.SouthWest.lng.ToString(CultureInfo.InvariantCulture), bounds.SouthWest.lat.ToString(CultureInfo.InvariantCulture));
}
private static string STpolygon(string polygon)
{
return string.Format("geography :: STGeomFromText('{0}', 4326)", polygon);
}
private string typologiesListString(List<string> typologies)
{
return string.Format("({0})", string.Join(",", typologies));
}
private static string formatMapNew(List<MapViewed> maps)
{
return string.Join(" OR ", maps.Select(x => string.Format("{0}.STIntersects(GeoLocation) = 1 and Tipologia = '{1}'", STpolygon(x.lastPatch.ToString()), x.typology)));
}
private static DataSet getCoordinatesInPolygon(List<MapViewed> maps)
{
SqlCommand Command_coord = new SqlCommand();
Command_coord.CommandType = CommandType.Text;
Command_coord.Connection = conn;
string query = string.Format(#"
select [Ragione sociale], GeoLocation, Tipologia, Dettagli
from GEOTAG
where {0}", formatMapNew(maps));
Command_coord.CommandText = query;
DataSet coordinatesDS = new DataSet();
SqlDataAdapter coordinatesDA = new SqlDataAdapter();
coordinatesDA.SelectCommand = Command_coord;
try
{
coordinatesDA.Fill(coordinatesDS);
}
catch (System.Data.SqlClient.SqlException e)
{
System.Diagnostics.Debug.WriteLine("Error query: \n{0}\n Message = {1}", query, e.Message);
}
return coordinatesDS;
}
private static bool IsEmpty(DataSet dataSet)
{
foreach (DataTable table in dataSet.Tables)
if (table.Rows.Count != 0) return false;
return true;
}
private static List<Marker> getDBMarkers(DataSet coordinatesDS)
{
List<Marker> markers = new List<Marker>();
SqlGeography point;
Marker marker;
if (!IsEmpty(coordinatesDS))
{
foreach (DataRow row in coordinatesDS.Tables[0].Rows)
{
point = (SqlGeography)row[1];
marker = new Marker((string)row[0], new Point(point.Lat.Value, point.Long.Value), (string)row[2], (string)row[3]);
markers.Add(marker);
}
}
return markers;
}
private static List<Marker> getMarkers(List<string> typologies, Bounds bounds)
{
return getDBMarkers(getCoordinatesInPolygon(updatedMaps(bounds, typologies)));
}
private static List<MapViewed> updatedMaps(Bounds bounds, List<string> typologies)
{
List<MapViewed> maps = new List<MapViewed>();
MapViewed map;
foreach (string typology in typologies)
{
map = (MapViewed)HttpContext.Current.Session["mapViewed-" + typology];
map.mergeAndGetDiff(getRectangle(bounds));
maps.Add(map);
}
return maps;
}
[System.Web.Services.WebMethod(EnableSession = true)]
public static List<Marker> GetMarkers(List<string> categories, Bounds bounds)
{
return getMarkers(categories, bounds);
}
}
You can use session in static methods like this
//Store value in session
HttpContext.Current.Session["mysession"]=value;
//Get value from session
var sessionvaue=HttpContext.Current.Session["mysession"];
use the static HttpContext.Current property to access the Session variables:
HttpContext.Current.Session

Categories

Resources