Prendendo come punto di partenza l'esempio citato nel primo articolo, Paginazione Lato Server Parte 1 - Oggetti che supportano la paginazione implementeremo in questa sede delle logiche per la ricerca.
Come al solito potete scaricare i sorgenti dell'esempio GPExamples2.zip
Inizieremo con l'inserimento, in entrambre le Stored Procedures, dei seguenti parametri:
[code language="SQL"]
@idProdotto INT,
@nomeProdotto NVarChar(25),
@descProdotto NVarChar(50),
@modelloProdotto NVarChar(25)
[/code]
e delle condizioni WHERE:
[code language="SQL"]
WHERE pmx.CultureID = 'en'
AND ((@idProdotto = 0) OR (p.ProductID = @idProdotto))
AND ((@nomeProdotto= '' ) OR (p.Name LIKE '%' + @nomeProdotto + '%'))
AND ((@descProdotto= '' ) OR (pd.Description LIKE '%' + @descProdotto + '%'))
AND ((@modelloProdotto= '' ) OR (pm.Name LIKE '%' + @modelloProdotto + '%'))
[/code]
Terminata questo processo si deve fare in modo che la nostra architettura comunichi questi parametri alle Stored Procedures.
Bisognerà dunque procedere inizialmente con la creazione di un nuovo tipo ParametriProdotti, in modo da poter passare tra i metodi un oggetto tipizzato, il quale sarà dotato della logica necessaria affichè le proprietà di questo oggetto rispettino la definizione dei paramtri che le Stored Procedures accettano. ParametriProdotti.cs avrà dunque questa definizione:
[code language="C#"]
using System;
using System.Data;
using System.Configuration;
namespace GPExamples.Oggetti.Utilities
{
/// <summary>
/// Summary description for ParametriProdotti
/// </summary>
[Serializable]
public class ParametriProdotti
{
#region Proprietà
private int _idProdotto;
/// <summary>
/// Restituisce o imposta (Gets or sets) l'ID Univoco del Prodotto.
/// </summary>
public int idProdotto
{
get { return _idProdotto; }
set { _idProdotto = value; }
}
private string _nomeProdotto;
/// <summary>
/// Restituisce o imposta (Gets or sets) il nome del Prodotto.
/// </summary>
public string nomeProdotto
{
get { return _nomeProdotto; }
set
{
if (value.Length > 25)
{
_nomeProdotto = value.Substring(0, 24);
}
else
{
_nomeProdotto = value;
}
}
}
private string _descProdotto;
/// <summary>
/// Restituisce o imposta (Gets or sets) la descrizione del Prodotto.
/// </summary>
public string descProdotto
{
get { return _descProdotto; }
set
{
if (value.Length > 50)
{
_descProdotto = value.Substring(0, 49);
}
else
{
_descProdotto = value;
}
}
}
private string _modelloProdotto;
/// <summary>
/// Restituisce o imposta (Gets or sets) il modello del Prodotto.
/// </summary>
public string modelloProdotto
{
get { return _modelloProdotto; }
set
{
if (value.Length > 25)
{
_modelloProdotto = value.Substring(0, 24);
}
else
{
_modelloProdotto = value;
}
}
}
#endregion
#region Costruttori
/// <summary>
/// Costruttore di default per Prodotto
/// </summary>
public ParametriProdotti()
{
//
// TODO: Add constructor logic here
//
}
/// <summary>
/// Overload del costruttore Prodotti
/// </summary>
/// <param name="idProdotto">ID Univoco del Prodotto</param>
/// <param name="nomeProdotto">Nome del Prodotto</param>
/// <param name="descProdotto">Descrizione del Prodotto</param>
/// <param name="modelloProdotto">Modello del Prodotto</param>
public ParametriProdotti(int idProdotto, string nomeProdotto, string descProdotto, string modelloProdotto)
{
this._idProdotto = idProdotto;
this._nomeProdotto = nomeProdotto;
this._descProdotto = descProdotto;
this._modelloProdotto = modelloProdotto;
}
#endregion
}
}
[/code]
In tutti i 'setter' di questo tipo controlleremo che i valori siano coerenti con quelli che la stored procedure si aspetta. Nell'esempio che abbiamo proposto il tipo Prodotto e ParametriProdotti sono molto simili, bisogna tener presente ad ogni modo che in un contesto reale i tipi potrebbero essere sostanzialmente diversi.
Passiamo ora all'analisi del nostro oggeto di accesso ai dati ProdottiDao. Ai metodi esposti aggiungeremo un altro parametro di tipo ParametriProdotti e faremo in modo che i valori contenuti in questo tipo vengano passati alle Stored Procedures. ProdottiDao, una volta ultimate le modifiche, risulterà come segue:
[code language="C#"]
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using GPExamples.Oggetti;
using GPExamples.Oggetti.Utilities;
namespace GPExamples.DB
{
/// <summary>
/// classe per l'accesso ai dati
/// </summary>
public class ProdottiDao
{
public IList<Prodotto> GetProdottiPaginati(int startRowIndex, int numRows, string sortColumn, int sortDirection, ParametriProdotti param)
{
IList<Prodotto> list = new List<Prodotto>();
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = "Data Source=PC-GIANPAOLO\\SQLGIANPAOLO;Initial Catalog=AdventureWorks;Integrated Security=True";
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandText = "GetProdottiPaginati";
SqlParameter[] sqlParams = new SqlParameter
;
sqlParams[0] = new SqlParameter("@StartRowIndex", SqlDbType.Int, 0);
sqlParams[0].Value = startRowIndex;
sqlParams[1] = new SqlParameter("@NumRows", SqlDbType.Int, 0);
sqlParams[1].Value = numRows;
sqlParams[2] = new SqlParameter("@sortDirection", SqlDbType.Int, 0);
sqlParams[2].Value = sortDirection;
sqlParams[3] = new SqlParameter("@sortColumn", SqlDbType.NVarChar, 25);
sqlParams[3].Value = sortColumn;
sqlParams[4] = new SqlParameter("@idProdotto", SqlDbType.Int, 0);
sqlParams[4].Value = param.idProdotto;
sqlParams[5] = new SqlParameter("@nomeProdotto", SqlDbType.NVarChar, 25);
sqlParams[5].Value = param.nomeProdotto;
sqlParams
= new SqlParameter("@descProdotto", SqlDbType.NVarChar, 50);
sqlParams
.Value = param.descProdotto;
sqlParams[7] = new SqlParameter("@modelloProdotto", SqlDbType.NVarChar, 25);
sqlParams[7].Value = param.modelloProdotto;
command.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter sqlParameter in sqlParams)
{
command.Parameters.Add(sqlParameter);
}
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
adapter.SelectCommand = command;
//TODO: Aggiugere controllo in caso di quesry errata o dataset vuoto
DataSet ds = new DataSet();
adapter.Fill(ds);
foreach (DataRow dr in ds.Tables[0].Rows)
{
list.Add(new Prodotto((int)dr["ProductID"], dr["Name"].ToString(), dr["Description"].ToString(), dr["ProductModel"].ToString()));
}
//return ds.Tables[0];
}
}
}
return list;
}
public int GetProdottiPaginatiCount(ParametriProdotti param)
{
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = "Data Source=PC-GIANPAOLO\\SQLGIANPAOLO;Initial Catalog=AdventureWorks;Integrated Security=True";
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
command.CommandText = "GetProdottiPaginatiCount";
SqlParameter[] sqlParams = new SqlParameter[4];
sqlParams[0] = new SqlParameter("@idProdotto", SqlDbType.Int, 0);
sqlParams[0].Value = param.idProdotto;
sqlParams[1] = new SqlParameter("@nomeProdotto", SqlDbType.NVarChar, 25);
sqlParams[1].Value = param.nomeProdotto;
sqlParams[2] = new SqlParameter("@descProdotto", SqlDbType.NVarChar, 50);
sqlParams[2].Value = param.descProdotto;
sqlParams[3] = new SqlParameter("@modelloProdotto", SqlDbType.NVarChar, 25);
sqlParams[3].Value = param.modelloProdotto;
command.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter sqlParameter in sqlParams)
{
command.Parameters.Add(sqlParameter);
}
command.CommandType = CommandType.StoredProcedure;
SqlParameter paramReturnValue = new SqlParameter();
paramReturnValue.ParameterName = "@return_value";
paramReturnValue.SqlDbType = SqlDbType.Int;
paramReturnValue.Direction = ParameterDirection.ReturnValue;
command.Parameters.Add(paramReturnValue);
connection.Open();
command.ExecuteScalar();
return (int)command.Parameters["@return_value"].Value;
}
}
}
}
}
[/code]
Ai metodi esposti da ProdottiFacade ritengo che non si possa aggiugere un parametro di tipo ParametriProdotti, infatti, sebbene non abbia fatto un elevato numero di ricerche in tal senso, l'Oblect Data Source riesce a passare solo tipi 'base' e quindi interi, stringhe etc. Dunque, dal momento che non possiamo passare un oggetto tipizzato, passeremo una stringa che conterrà i valori a noi necessari sotto forma di valori concatenati separati dal punto e virgola.
Infine, per creare il nostro oggetto tipizzato da passare all'oggeto per l'accesso ai dati, useremo un metodo privato.
Ultimate le modifiche ProdottiFacade.cs risultera come segue:
[code language="C#"]
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using GPExamples.DB;
using GPExamples.Oggetti.Utilities;
namespace GPExamples.Oggetti
{
/// <summary>
/// Summary description for ProdottiFacade
/// </summary>
// [DataObject(true)] Questo attributo indica che questa classe è adatta per creare oggetti ObjectDataSource a design time
[DataObject(true)]
public static class ProdottiFacade
{
private static ProdottiDao prodottiDao = new ProdottiDao();
private static int _numOrdini;
static ProdottiFacade()
{
//
// TODO: Add constructor logic here
//
}
/// <summary>
/// Restituisce (Gets) una lista di Prodotti paginati e ordinati secondo i parametri passati
/// </summary>
/// <param name="startRowIndex">il numero iniziale della riga da restituire</param>
/// <param name="maximumRows">il numero totale di righe da restituire</param>
/// <param name="sortColumn">il nome della proprietà della classe Prodotto secondo la quale ordianre</param>
/// <param name="sortDirection">la direzione dell'ordinamento ASC o DESC</param>
/// <returns>una lista di prodotti</returns>
[DataObjectMethod(DataObjectMethodType.Select)]
public static IList<Prodotto> GetProdottiPaginati(int startRowIndex, int numRows, string sortColumn, string sortDirection, string sortExpression)
{
int sortParam = sortDirection == "ASC" ? 1 : -1;
if (!string.IsNullOrEmpty(sortColumn))
{
sortColumn = sortColumn.Split(' ')[0];
}
else
{
sortColumn = "";
}
return prodottiDao.GetProdottiPaginati(startRowIndex, numRows, sortColumn, sortParam, GeneraParametriProdotto(sortBLOCKED EXPRESSION;
}
/// <summary>
/// Restituisce (Gets) una intero che rappresenta il numero totale di prodotti
/// </summary>
/// <param name="startRowIndex">il numero iniziale della riga da restituire</param>
/// <param name="maximumRows">il numero totale di righe da restituire</param>
/// <param name="sortColumn">il nome della proprietà della classe Prodotto secondo la quale ordianre</param>
/// <param name="sortDirection">la direzione dell'ordinamento ASC o DESC</param>
/// <returns>un intero che rappresenta il numero totale di prodotti</returns>
[DataObjectMethod(DataObjectMethodType.Select)]
public static int GetProdottiPaginatiCount(int startRowIndex, int numRows, string sortColumn, string sortDirection, string sortExpression)
{
return prodottiDao.GetProdottiPaginatiCount(GeneraParametriProdotto(sortBLOCKED EXPRESSION;
}
private static ParametriProdotti GeneraParametriProdotto(string sortExpression)
{
ParametriProdotti param = new ParametriProdotti();
//ricavo i singoli valori della sortExpression
if (string.IsNullOrEmpty(sortBLOCKED EXPRESSION
{
param = new ParametriProdotti(0, "", "", "");
}
else
{
string[] parametri = sortExpression.Split(';');
//idProdotto
//nomeProdotto
//descProdotto
//modelloProdotto
try
{
if (string.IsNullOrEmpty(parametri[0]))
{
param.idProdotto = 0;
}
else
{
param.idProdotto = Convert.ToInt32(parametri[0]);
}
}
catch
{
param.idProdotto = 0;
}
try
{
param.nomeProdotto = parametri[1];
}
catch
{
param.nomeProdotto = "";
}
try
{
param.descProdotto = parametri[2];
}
catch
{
param.descProdotto = "";
}
try
{
param.modelloProdotto = parametri[3];
}
catch
{
param.modelloProdotto = "";
}
}
return param;
}
}
}
[/code]
Bene, ci siamo quasi.
Bisogna solo modificare la UI ed il gioco è fatto: all'Ollobject Data Source aggiungeremo un parametro di tipo stringa sortExpression, una tabella che conterrà i nostri campi in cui l'utente potrà inserire i propri criteri di ricerca e infine un button.
Nel code behind della pagina bisogna solo inserire il codice per gestire l'evento click del bottone ricerca, che non farà altro che andare a valorizzare il parametro sortExpression dell'Object Datata Souce con i valori che avrà inserito l'utente:
[code language="C#"]
protected void btnRicerca_Click(object sender, EventArgs e)
{
ricerca();
}
protected void ricerca()
{
gvProdotti.PageIndex = 0;
string idProdotto = this.txtidProdotto.Text.Trim();
string nomeProdotto = this.txtnomeProdotto.Text.Trim();
string descProdotto = this.txtdescProdotto.Text.Trim();
string modelloProdotto = this.txtmodelloProdotto.Text.Trim();
odsProdotti.SelectParameters["sortExpression"].DefaultValue = idProdotto + ";" + nomeProdotto + ";" +
descProdotto + ";" + modelloProdotto;
}
[/code]
L'argomento del prossimo articolo riguarderà la riutilizzazione del codice già scritto per paginare e ricercare usando un oggetto, il DataList, che non supporta nativamente la paginazione.
Paginazione Lato Server Parte 1 - Oggetti che supportano la paginazione
Paginazione Lato Server Parte 3 - Oggetti che non supportano la paginazione con ricerca
Paginazione Lato Server Parte 4 - Using Singleton Collections