Segueixo ampliant la clsDades amb noves funcionalitats, aquest cop són les funcions que permeten executar sentències SQL que retornen un valor numèric, com per exemple un COUNT o un SUM... Són les funcions ExecScalar, com sempre en tres sabors, només amb la sentència SQL, un que a més a més porta la connexió i el final que també porta la transacció. Com sempre el codi.
/// <summary>/// Executa una sentència SQL que no retorna resultats,/// només la primera columna del primer registre./// Obre i tanca conexió a BBDD (la que retorna GetConnStr)/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <returns>Resultat operació (COUNT)</returns>publicint ExecScalar(string SQL)
{
int Results = 0;
SqlConnection localSqlConn;
bool Local = false;
if (SQLConn != null)
{
localSqlConn = SQLConn;
}
else
{
Local = true;
localSqlConn = newSqlConnection();
localSqlConn.ConnectionString = CadConnBBDD;
localSqlConn.Open();
}
Results = ExecScalar(SQL, localSqlConn);
if (Local) localSqlConn.Close();
return Results;
}
/// <summary>/// Executa una sentència SQL que no retorna resultats,/// només la primera columna del primer registre./// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <returns>Resultat operació (COUNT)</returns>publicint ExecScalar(string SQL, SqlConnection pSqlConn)
{
return ExecScalar(SQL, pSqlConn, SQLTrans);
}
/// <summary>/// Executa una sentència SQL que no retorna resultats,/// només la primera columna del primer registre./// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <param name="SqlTrann">Transacció ja creada sobre la conexió (admet null)</param>/// <returns>Resultat operació (COUNT)</returns>publicint ExecScalar(string SQL, SqlConnection pSqlConn, SqlTransaction pSqlTrann)
{
int Results = 0;
object objResult;
SqlCommand SqlComm = newSqlCommand();
SqlComm.Connection = pSqlConn;
if (pSqlTrann != null) SqlComm.Transaction = pSqlTrann;
SqlComm.CommandText = SQL;
SqlComm.CommandType = CommandType.Text;
objResult = SqlComm.ExecuteScalar();
if (objResult != null && objResult != DBNull.Value)
{
Results = int.Parse(objResult.ToString());
}
return Results;
}
#20/02/2014 17:19 Programació C# Autor: Alex Canalda
La clsDades segueix creixent i cada cop es sembla més a una navalla suïssa de les bases de dades. Els mètodes del primer post d'aquesta sèrie executen un SQL i retornen una taula, els mètodes que poso a continuació són els encarregats d'executar SQLs que no retornen una taula com a resultat. Normalment són els SQL relacionats amb INS/UPD/DEL que retornen el nombre de registres afectats. Aquesta funció està disponibles en tres sabors, depenent del detall que volem, la primera que rep un SQL i la funció ja s'apanya a executar-lo, la segona que rep la connexió sobre la que executar l'SQL i la tercera que també rep la transacció. Com sempre el codi:
/// <summary>/// Executa una sentència SQL que no retorna resultats, /// només els registres afectats. /// Obre i tanca conexió a BBDD (la que retorna GetConnStr)/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <returns>Registres afectats</returns>publicint ExecNonQuery(string SQL)
{
int Results = 0;
SqlConnection localSqlConn;
bool Local = false;
if (SQLConn != null)
{
localSqlConn = SQLConn;
}
else
{
Local = true;
localSqlConn = newSqlConnection();
localSqlConn.ConnectionString = CadConnBBDD;
localSqlConn.Open();
}
Results = ExecNonQuery(SQL, localSqlConn);
if (Local) localSqlConn.Close();
return Results;
}
/// <summary>/// Executa una sentència SQL que no retorna resultats/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <returns>Registres afectats</returns>publicint ExecNonQuery(string SQL, SqlConnection pSqlConn)
{
return ExecNonQuery(SQL, pSqlConn, SQLTrans);
}
/// <summary>/// Executa una sentència SQL que no retorna resultats/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <param name="SqlTrann">Transacció ja creada sobre la conexió (admet null)</param>/// <returns>Registres afectats</returns>publicint ExecNonQuery(string SQL, SqlConnection SqlConn, SqlTransaction SqlTrann)
{
int Results = 0;
SqlCommand SqlComm = newSqlCommand();
SqlComm.Connection = SqlConn;
if (SqlTrann != null) SqlComm.Transaction = SqlTrann;
SqlComm.CommandText = SQL;
SqlComm.CommandType = CommandType.Text;
Results = SqlComm.ExecuteNonQuery();
return Results;
}
#19/02/2014 17:10 Programació C# Autor: Alex Canalda
La clsDades és el punt de l'aplicació web que per una banda dóna servei als ASHX mitjançant les classes derivades i per l'altra interactua amb les Stored Procedures (SP) de la BBDD. Però a vegades una consulta, o una operació no s'ajusta a la forma d'operar de les SP i per això es fa una consulta adhoc directament al codi. Però per evitar muntar tota la infraestructura necessària per accedir i executar la consulta he ampliat la clsDades. També hi ha tasques i problemes que es van donant en la interacció amb la BBDD de forma habitual, també he posat els mètodes corresponents a la clsDades. Diguem que ara té una part "oficial" amb el GET, GETTOP, INS, UPD, DEL, QUERY i una d'utilitats que aniré posant.
Un del punts problemàtics alhora de interactuar amb la BBDD és l'idioma, que si l'idioma del webserver, que l'idioma de la BBDD, etc... La solució és fer servir un format de data que sigui independent de l'idioma, per exemple el ISO 8601. Per això he fet una mini funció que genera una data en aquesta format.
Altres funcions són un xic més complicades, per exemple, executar una SQL i retornar una DataTable. Aquesta està disponible en tres sabors: un que només rep la sentència SQL que s'ha d'executar, una altra SQL i connexió i la tercera SQL, connexió i transacció. Com sempre el codi:
/// <summary>/// Executa una SQL que retorna resultats contra una conexió, no modifica l'estat de la conexió ni de la transaccio/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <param name="SqlTrann">Transacció ja creada sobre la conexió (admet null)</param>/// <returns>Retorna un NEW DataTable</returns>publicDataTable Exec(string SQL, SqlConnection pSqlConn, SqlTransaction pSqlTrann)
{
DataTable TBL = newDataTable();
SqlCommand SqlComm = newSqlCommand();
SqlDataReader SqlReader;
SqlComm.Connection = pSqlConn;
if (pSqlTrann != null) SqlComm.Transaction = pSqlTrann;
SqlComm.CommandText = SQL;
SqlComm.CommandType = CommandType.Text;
SqlReader = SqlComm.ExecuteReader();
TBL.Load(SqlReader);
return TBL;
}
/// <summary>/// Executa una SQL que retorna resultats contra una conexió, no modifica l'estat de la conexió/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <returns>Retorna un NEW DataTable</returns>publicDataTable Exec(string SQL, SqlConnection pSqlConn)
{
return Exec(SQL, pSqlConn, SQLTrans);
}
/// <summary>/// Executa una sentència SQL que retorna resultats, /// si està informada la variable Conn la fa servir de conexio (ha d'estar oberta), si no crea una i la tanca/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <returns>Retorna un NEW DataTable</returns>publicDataTable Exec(string SQL)
{
DataTable TBL;
SqlConnection localSqlConn;
bool Local = false;
if (SQLConn != null)
{
localSqlConn = SQLConn;
}
else
{
Local = true;
localSqlConn = newSqlConnection();
localSqlConn.ConnectionString = CadConnBBDD;
localSqlConn.Open();
}
TBL = Exec(SQL, localSqlConn);
if (Local) localSqlConn.Close();
return TBL;
}
Altres métodes que segueixen ampliant la clsDades les tractare en futurs posts.
#17/02/2014 12:31 Programació C# Autor: Alex Canalda
M'he animat i he posat RSS al blog, ja que també m'ho han demanat. Doncs dit i fet. El cas és que tampoc és gaire difícil fer-ho. Hi ha unes classes de .NET SyndicationFeed, que ho fan fàcil. Cal fer un ASHX i posar el "using System.ServiceModel.Syndication;", després un parell de bucles, es recupera de la BBDD amb la clsDades els posts i voilà. Com sempre el codi.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Syndication;
using System.IO;
using System.Xml;
using System.Data;
using Blog.data;
namespace Blog
{
/// <summary>/// Summary description for RSS/// </summary>publicclass RSS : IHttpHandler
{
private clsConfig Conf = new clsConfig();
privatestring ConnStr = "";
//Cal posar al Blog.aspx://<link rel="alternate" type="application/rss+xml" href="RSS.ashx" title="Blog Feed"/>publicvoid ProcessRequest(HttpContext context)
{
Conf.Initialize();
ConnStr = Conf.GetVal("CadConn");
SyndicationFeed feed = CreateRecentPostsFeed();
var output = new StringWriter();
var writer = new XmlTextWriter(output);
new Rss20FeedFormatter(feed).WriteTo(writer);
context.Response.ContentType = "application/rss+xml";
context.Response.Write(output.ToString());
}
private SyndicationFeed CreateRecentPostsFeed()
{
List<SyndicationItem> syndicationItems = GetPostsRecents();
returnnew SyndicationFeed(syndicationItems)
{
Title = new TextSyndicationContent("Blog"),
Description = new TextSyndicationContent("Una mica de tot i a vegades més"),
ImageUrl = new Uri("http://blog.canalda.net/Img/laughing_man_p.jpg")
};
}
privateList<SyndicationItem> GetPostsRecents()
{
List<SyndicationItem> Posts = newList<SyndicationItem>();
clsPosts PostsTA = new clsPosts(ConnStr);
Dictionary<string, string> Params = newDictionary<string, string>();
DateTime FPubs = DateTime.Now;
DataTable PostsTBL;
FPubs = FPubs.Subtract(TimeSpan.FromDays(7));
Params.Add("DataPubMesGran", PostsTA.DateTimeToSQL(FPubs, "0"));
Params.Add("Actiu", "true");
PostsTBL = PostsTA.GET(Params);
foreach (DataRow Post in PostsTBL.Rows)
{
Posts.Add(GeneraSyndicacioPost(Post));
}
return Posts;
}
private SyndicationItem GeneraSyndicacioPost(DataRow DR)
{
string id = DR["IdPost"].ToString();
string title = String.Format("Post: {1}", id, DR["Titol"]);
string content = DR["PostText"].ToString();
DateTimeOffset DataPub = new DateTimeOffset((DateTime) DR["DataPub"]);
Uri url = new Uri(String.Format("http://blog.canalda.net/Post.aspx?IdPost={0}", id));
returnnew SyndicationItem(title, content, url, id, DataPub);
// { Summary = new TextSyndicationContent(content) };
}
publicbool IsReusable
{
get
{
returnfalse;
}
}
}
}
#17/02/2014 10:51 Programació C# Autor: Alex Canalda
Ja he pogut veure que a alts DPI les aplicacions Winforms es mostren malament.
Buscant solucions pels Internetes diuen que amb un fitxer Manifest la cosa s'arregla, jo ho he provat i res de res. Al final m'he cansat de perdre temps amb el Manifest i l'he enviat a pastar.
Una altra solució és modificar les propietats d'execució de l'aplicació, potser és la més sencilla per aplicacions de les que no es disposa el codi font.
En canvi si es disposa del codi font i no es vol preocupar-se de si l'opció està marcada o no es pot afegir el següent. Totes les aplicacions Winforms tenen un fitxer que es diu Program.cs que és on s'especifica el punt d'entrada al programa, i queda tal que:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace CodeGenerator
{
staticclass Program
{
/// <summary>/// The main entry point for the application./// </summary>
[STAThread]
staticvoid Main()
{
if (Environment.OSVersion.Version.Major >= 6) SetProcessDPIAware();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmStoredGenerator());
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
privatestaticexternbool SetProcessDPIAware();
}
}
Es a dir s'afegeix una crida a SetProcessDPIAware. Curiosament aquest funció està definida a la DLL user32.dll. No cal que ens amoïnem de que l'aplicació que estem fent sigui de 64bits, aquesta DLL és de 64bits, encara que el nom enganyi. Suposo que hi ha tantes aplicacions que la tenen referenciada que no va haver-hi collons de canviar-li el nom a user64.dll i que l'univers es col·lapses.
Amb això s'aconsegueix que les aplicacions es vegin bé, però com s'ha mogut el sistema de coordenades és molt possible que els controls (encara que es vegin nítids) estiguin mal col·locats. Hauré de buscar una solució per això altra.
#13/02/2014 12:17 Programació C# Autor: Alex Canalda
Potser m'avanço a coses que explicaré en la part client, però en una aplicació web, si es vol separar la presentació de les dades hi ha un punt de conflicte que són els combos (desplegables).
Les pàgines que genero només tenen HTML, sense dades, cal doncs un mecanisme per omplir els combos. De moment començaré per explicar la part servidora, un ASHX que es diu comboloader.
Aquest ASHX, s'encarrega de gestionar tots els combos de l'aplicació. Es recolza en la clsDades i derivades on normalment fa servir el mètode GET per anar obtenir els registres, muntar-los en JSON i enviar-los al navegador (al client). La part Javascript del comboloader s'encarrega de desmuntar aquest JSON i posar els valors corresponents als combos. Llavors quan es dissenya una pàgina un combo només és tag <select>, sense valors dins (a vegades es poden posar els valors si són coneguts i no hi ha gaires (ni previsió que hagin més)).
Del navegador es reben un conjunt de combos a carregar (els seus noms venen separats per "-"), els que apareixen en una pàgina i el que es fa es generar-los tots de cop. Per fer això necessitarem objectes que, un cop convertits a JSON donin suport a l'enviament de tots els combos amb els seus valors de cop. Són les classes: Combo i ComboItem.
Són dues classes molt senzilles. Comentar que potser hi ha combos en diferents parts de l'aplicació que tenen els mateixos items però diferents noms, aleshores el que es fa és afegir al "switch" que escull el combo a muntar més opcions. Això fa que moltes vegades ens trobem que el combo que volem muntar en realitat ja està fet per una altra pantalla. Llavors amb una simple línia de codi ja ho tenim enllestit. La construcció dels combos segueix la següent descripció:
Com sempre el codi de cada fase, comencem per "obtenir els combos" i també al final "convertir a JSON i enviar":
/// <summary>/// Genera els items d'un combo/// </summary>/// <param name="TBL">Taula que conte els registres</param>/// <param name="CampID">Camp que va a la part de valor</param>/// <param name="CampDescripcio">Camp que serveix de descripció/// Pot estar format per més d'un camp</param>/// <param name="NumCombo">Número de l'array de combos</param>/// <param name="Separador">Si la descripció t'e més d'un camp, com es separen</param>/// <param name="ValorSiNull">Si hi ha alguna descripció nula</param>publicvoid GeneraItems(DataTable TBL, string CampID,
string CampDescripcio, int NumCombo, string Separador, string ValorSiNull)
{
DataRow DR;
string[] arrCampsDescripio = CampDescripcio.Split('-');
StringBuilder Descripcio = newStringBuilder();
string Camp = "";
if (TBL.Rows.Count > 0)
{
objCombos[NumCombo].Items = new ComboItem[TBL.Rows.Count];
for (int j = 0; j < TBL.Rows.Count; j++)
{
DR = TBL.Rows[j];
Descripcio.Length = 0;
objCombos[NumCombo].Items[j] = new ComboItem();
objCombos[NumCombo].Items[j].Val = DR[CampID].ToString();
for(int i = 0; i < arrCampsDescripio.Length; i++)
{
Camp = arrCampsDescripio[i];
if (i > 0) Descripcio.Append(Separador);
if (!DR.IsNull(Camp)) Descripcio.Append(DR[Camp].ToString().Trim());
else Descripcio.Append(ValorSiNull);
}
objCombos[NumCombo].Items[j].Disp = Descripcio.ToString().Trim();
}
}
}
#21/01/2014 22:54 Programació C# Autor: Alex Canalda
En tota aplicació web, la part servidora té números que hagi d'enviar dades en format JSON.
El mètode que he triat jo és fer servir una llibreria lliure que converteix objectes a JSON, i per la quantitat de projectes que porto amb aquesta llibreria funcionant bé, crec que ja és hora de recomanar-la. És la NewtonSoftJSON.
La particularitat que té i que m'agrada és que el valors NULL no els converteix a JSON, fent que sigui força optim enviar dades en JSON. Altres llibreries els valors NULLs es serialitzen com a: "nom_de_la_propietat":"", en aquesta és configurable. En el codi aquesta llibreria acostuma a aparèixer com un clsJSON.Serialize(objecte_dictionary) en els ASHX.
Al ser una llibreria no posaré el codi per que seria només la línia que he comentat abans, però si cal recordar que s'ha de posar una referència a la DLL corresponent.
#20/01/2014 11:24 Programació C# Autor: Alex Canalda
Dins de la construcció d'un grid, en la part que toca a un ASHX, el que cal saber és la seva configuració i com funcionen la clsColumna i clsCampBBDD. No cal conèixer en detall aquesta funció de la clsHelper, però si ens agrada el codi com a mi, no està de més saber com funciona per dins.
Tots el grids d'una aplicació fan servir aquesta funció, porto uns quants projectes a l'esquena fent-la servir, així que penso que està amortitzada i no he trobat bugs aparents, però si en trobo almenys al corregir-ho serveix per tots.
El primer que fa aquesta funció és recuperar els valors dels paràmetres que indiquen la pàgina, l'ordre, etc... aquest paràmetres els genera el grid automàticament a la part client, en la part servidora només els recuperem.
sidx: camps pel que es fa l'ordenació.
sord: sentit de la ordenació, ascendent o descendent.
rows: número de files a recuperar.
page: pàgina a recuperar.
A més a més d'aquests paràmetres a la URL també es posen els camps i els corresponents valors que es fan servir per filtrar el resultats. Es recuperen també, venen donats a la StringCollection CampsClau.
Durant el procés de recuperar aquests valors també es munta una clau que identifica de forma única un grid, i l'estat en que es troba. Com estat en que es troba s'enten: la pàgina, ordre, número de registres, filtre aplicat etc... Aquesta clau es fa servir per consultar la cache. A la cache de grids s'accedeix mitjaçant aquesta clau, un identificador de grid i la taula sobre la que s'aplica. Les operacions d'INS, UPD i DEL, en cas de tenir cache que la taula es gestioni amb cache, esborren els registres de la taula de cache només d'aquesta taula en concret. Un muntada aquesta clau es mira la taula de cache, si es troba es recupera el JSON i s'envia directament. D'això s'anomena un "cache hit". Si no es troba (un "cache miss") es segueix processant normalment i es crida a la funció QUERY (aquesta al seu temps cridarà a la SP QUERY). La diferència entre operar amb cache o sense és molt gran quan es fan QUERYs contra taules de milions de registres ja que els usuaris acostumen a treballar habitualment amb el mateix subconjunt de dades, es a dir no tots els registres tenen la mateixa importància.
Un cop obtinguts els registres cal empaquetar els camps en els objectes clsGridRow corresponents, donant format a les dades en cas de que estigui informat el camp "Format".
Un cop fet això poc queda per fer. Convertir-ho a JSON. Si la taula es gestiona amb cache, aquest JSON cal guardar-lo, d'aquesta manera el proper cop hi haurà un "cache hit" (de fet només que l'usuari refresqui la pàgina ja farà un hit).
Com sempre el codi:
/// <summary>/// Genera els camps d'un grid/// </summary>/// <param name="TA">TableAdapter del tipus de registres que té que generar</param>/// <param name="Req">Request del que treure informació</param>/// <param name="CampsClau">Si el grid es filtra per algun camp informar-los aqui,/// primer es busquen en el Request, després en ValorsExtraCampsClau</param>/// <param name="ValorsExtraCampsClau">OPCIONAL: Valors de camps Clau que no arriben/// a través del Request, passar null si no es vol</param>/// <param name="ColumnesGrid">Llista de columnes del Grid, ha d'estar
/// en l'ordre que estan a pantalla d'esquerra a dreta</param>/// <param name="GridID">ID del Grid que es genera, només es fa servir quan hi ha cache</param>/// <returns></returns>publicstaticstring LoadGrid(clsDades TA, HttpRequest Req, StringCollection CampsClau,
List<clsColumna> ColumnesGrid, Dictionary<string, string> ValorsExtraCampsClau, string GridID)
{
bool Search = false;
int i = 0;
string Ordenacio = "";
string SentitOrdre = "";
int numRegistres = 0; //Numero de registres per cada paginaint numPag = 0; //Pagina que es volint numTotalRegs = 0;
int numTotalPags = 1; //Numero total de paginesDataTable TBL;
DataRow DR;
Dictionary<string, string> Params = newDictionary<string, string>();
Dictionary<string, string> ParamsCache = newDictionary<string, string>();
clsGridRow GR;
string Valor = "";
decimal ValorDecimal = 0;
DateTime ValorDateTime;
clsCache Cache = new clsCache(TA.CadConnBBDD);
string Result = "";
StringBuilder URL = newStringBuilder();
if (Req["_search"] != null)
bool.TryParse(Req["_search"], out Search);
Ordenacio = Req["sidx"];
SentitOrdre = Req["sord"].ToUpper();
clsGridResponse Data = new clsGridResponse();
numRegistres = int.Parse(Req["rows"]);
numPag = int.Parse(Req["page"]);
foreach (string Clau in CampsClau)
{
if (!string.IsNullOrEmpty(Req[Clau]))
{
Params.Add(Clau, Req[Clau]);
URL.AppendFormat("-{0}-{1}", Clau, Req[Clau]);
}
else
{
if (ValorsExtraCampsClau != null)
{
if (ValorsExtraCampsClau.ContainsKey(Clau))
{
Params.Add(Clau, ValorsExtraCampsClau[Clau]);
URL.AppendFormat("-{0}-{1}", Clau, ValorsExtraCampsClau[Clau]);
}
}
}
}
if (TA.TeCache)
{
URL.AppendFormat("{0}{1}", Ordenacio.ToString(), SentitOrdre.ToString());
URL.AppendFormat("{0}{1}", Req["rows"], Req["page"]);
ParamsCache.Add("Taula", TA.NomTaula);
ParamsCache.Add("URL", URL.ToString());
ParamsCache.Add("GridID", GridID);
TBL = Cache.GET(ParamsCache);
if (TBL.Rows.Count != 0)
{
//Cache hit
Result = TBL.Rows[0]["JSON"].ToString();
return Result;
}
}
//Processat sense cache
Params.Add("PageSize", numRegistres.ToString());
Params.Add("PageNum", numPag.ToString());
Params.Add("SortColumn", Ordenacio + SentitOrdre);
TBL = TA.QUERY(Params, out numTotalRegs);
#if DEBUG
foreach (clsColumna ColumnaGrid in ColumnesGrid)
{
foreach (clsCampBBDD CampGridBBDD in ColumnaGrid.Camps)
{
if (!TBL.Columns.Contains(CampGridBBDD.Nom))
{
thrownewException("ERROR: La columna " + CampGridBBDD.Nom + " no pertany a la taula del LoadGrid");
}
}
}
#endif
numTotalPags = numTotalRegs / numRegistres;
if (numTotalRegs % numRegistres != 0) numTotalPags++;
Data.total = numTotalPags.ToString();
Data.page = Req["page"];
//Generacio de les dades en JSONfor (i = 0; i < TBL.Rows.Count; i++)
{
GR = new clsGridRow();
DR = TBL.Rows[i];
foreach (clsColumna ColumnaGrid in ColumnesGrid)
{
Valor = "";
for (int j = 0; j < ColumnaGrid.Camps.Count; j++)
{
if (j != 0) Valor += ColumnaGrid.Separador;
if (!DR.IsNull(ColumnaGrid.Camps[j].Nom))
{
if (ColumnaGrid.Camps[j].Format != "")
{
switch (ColumnaGrid.Camps[j].Tipus)
{
case"System.DateTime":
ValorDateTime = (DateTime)DR[ColumnaGrid.Camps[j].Nom];
Valor += ValorDateTime.ToString(ColumnaGrid.Camps[j].Format);
break;
case"System.Decimal":
ValorDecimal = (Decimal)DR[ColumnaGrid.Camps[j].Nom];
Valor += ValorDecimal.ToString(ColumnaGrid.Camps[j].Format);
break;
}
}
else Valor += DR[ColumnaGrid.Camps[j].Nom].ToString();
}
else Valor += ColumnaGrid.Camps[j].ValorSiNull;
}
GR.cell.Add(Valor);
}
Data.rows.Add(GR);
}
Data.records = numTotalRegs.ToString();
Result = clsJSON.Serialize(Data);
//Es un MISS cal fer un insert a la cacheif (TA.TeCache)
{
Params.Clear();
Params.Add("Taula", TA.NomTaula);
Params.Add("URL", URL.ToString());
Params.Add("GridID", GridID);
Params.Add("JSON", Result);
Cache.INS(Params);
}
return Result;
}
#17/01/2014 13:47 Programació C# Autor: Alex Canalda
Quan en una aplicació web, en un ASHX, es fa un grid hi ha que fer una crida a la clsHelper LoadGrid amb uns paràmetres en concret. Aquests paràmetres es creen fent servir les classes clsColumna i clsCampBBDD que estàn explicades aquí.
Cal tenir en compte que alhora de definir les columnes aquestes han d'estar en el mateix ordre que al grid. En el codi a continuació es poden veure exemples de columnes compostes per més d'un camp de BBDD, formatades, que passa si el valor és null, etc... Millor veiem l'exemple:
privatevoid LoadGrid()
{
string JSON;
StringCollection CampsClau = newStringCollection();
List<clsColumna> ColumnesGrid = newList<clsColumna>();
clsColumna ColumnaGrid;
string PK_Arxiu = "";
Dictionary<string, string> ValorsExtra = newDictionary<string, string>();
clsCampBBDD CampGridBBDD;
clsDestruccio TA = new clsDestruccio(ConnStr);
//Això és concret d'aquesta aplicació//Però serveix per ilustrar els ValorsExtra//En aquest cas es vol limitar els resultats del grid//segons un valor que no arriba en la petició, està//associat a l'usuari
PK_Arxiu = clsHelper.ObtenirArxiu(IdPersona, ConnStr, null, null);
//Aquests camps arriben per la URL i serveixen//per filtrar el grid.
CampsClau.Add("PK_Destruccio");
CampsClau.Add("FK_TAAD");
CampsClau.Add("Exped_I");
CampsClau.Add("DataInici");
CampsClau.Add("DataFi");
CampsClau.Add("FK_Estat");
CampsClau.Add("FK_Arxiu");
//S'afegeix el valor que també filtra però no//arriba a la URL
ValorsExtra.Add("FK_Arxiu", PK_Arxiu);
// columna 1
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "PK_Destruccio";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 2
ColumnaGrid = new clsColumna();
//Aquesta columna està formada per 2 camps, separats per -
ColumnaGrid.Separador = " - ";
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "codi_TAAD";
ColumnaGrid.Camps.Add(CampGridBBDD);
//Segon camp de la columna 2
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Descripcio";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 3
ColumnaGrid = new clsColumna();
//Aquesta columna està formada per 2 camps, separats per /
ColumnaGrid.Separador = "/";
CampGridBBDD = new clsCampBBDD();
//Si és null es posarà un - enlloc del valor
CampGridBBDD.ValorSiNull = "-";
CampGridBBDD.Nom = "Codi_TC";
ColumnaGrid.Camps.Add(CampGridBBDD);
//Segon camp de la columna 3
CampGridBBDD = new clsCampBBDD();
//Si és null es posarà un - enlloc del valor
CampGridBBDD.ValorSiNull = "-";
CampGridBBDD.Nom = "Especifica_TC";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 4
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Serie_documental";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 5
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Any_obertura";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 6
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Any_tancament";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 7
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Metres";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 8
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "NumExpedients";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 9
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Estat";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 10, datetime sense hora
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "Data_Destruccio";
CampGridBBDD.Tipus = "System.DateTime";
//Aquí es fa servir el format, es pot especificar un//format diferent al que es fa servir en el detall//d'un formulari
CampGridBBDD.Format = "dd/MM/yyyy";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
// columna 11
ColumnaGrid = new clsColumna();
CampGridBBDD = new clsCampBBDD();
CampGridBBDD.Nom = "bCataleg";
ColumnaGrid.Camps.Add(CampGridBBDD);
ColumnesGrid.Add(ColumnaGrid);
JSON = clsHelper.LoadGrid(TA, Req, CampsClau, ColumnesGrid, ValorsExtra, "");
Resp.Write(JSON);
}
#17/01/2014 13:06 Programació C# Autor: Alex Canalda
Els grids són la part més difícil d'una aplicació web. Jo faig servir el papa de tots els grids: el Trirand jqGrid. De fet ja fa força anys que el faig servir (uis! hem faig vell!).
Com aquí em tinc que limitar a tractar la part servidora que està en un ASHX, direm que l'objectiu és: donada una petició cal muntar el JSON corresponent per alimentar al grid.
Muntar aquest JSON té certa gràcia ja que ha de tenir una estructura concreta, per això cal fer unes classes que quan es converteixen a JSON encaixin amb el que necessita el grid. A més a més d'això cal cridar a mètode QUERY de la classe derivada de la clsDades corresponent. Per ja fer triple carambola, hi ha columnes d'un grid que estan compostes de varis camps de la BBDD ajuntats. Començarem doncs per les classes que s'han d'omplir per generar el JSON, són la clsGridResponse i la clsGridRow:
Per descarregar una mica la complexitat de definir un grid amb columnes compostes de varis camps de la BBDD, vaig fer uns altres objectes on informar aquesta configuració, són les clsColumna i la clsCampBBDD.
/// <summary>/// Classe que defineix una columna d'un grid./// També es fa servir durant l'exportació a Excel/// </summary>publicclass clsColumna
{
/// <summary>/// Camps que composen la columna/// </summary>publicList<clsCampBBDD> Camps {get; set;}
/// <summary>/// En el cas d'una columna amb més d'un camp a BBDD/// és el caràcter que les separa, p. ex: '/' o '-'/// </summary>publicstring Separador {get; set;}
/// <summary>/// En el cas de l'exportació a Excel és el nom de la columna,/// quan es fa servir un grid no cal informar-lo perque ja/// està el nom al JS on es defineix el grid./// </summary>publicstring Nom {get; set;}
public clsColumna()
{
Camps = newList<clsCampBBDD>();
Separador = "";
Nom = "";
}
}
/// <summary>/// Camps de la BBDD del que està formada una columna/// </summary>publicclass clsCampBBDD
{
/// <summary>/// Nom del camp de BBDD/// </summary>publicstring Nom { get; set; }
/// <summary>/// En el cas que sigui null el valor es posar aquest valor, p. ex: '-'/// </summary>publicstring ValorSiNull { get; set; }
/// <summary>/// En el cas de ser DateTime o Decimal/// es pot especificar el format, p. ex: 'dd/MM/yyyy' o '#.00'/// </summary>publicstring Format { get; set; }
/// <summary>/// Admet 3 valors: res ("") , 'System.DateTime' o 'System.Decimal'/// </summary>publicstringTipus { get; set; }
public clsCampBBDD()
{
Nom = "";
ValorSiNull = "";
Format = "";
Tipus = "";
}
}
I aquestes classes de suport on es poden posar? Doncs tota aplicació té un calaix de mals endreços on van a parar aquest tipus d'objectes. En el meu cas es diu clsHelper, i mereix un post apart. Aleshores, per fer un grid, el que cal fer es configurar aquestes classes i cridar al Loadgrid genèric de la clsHelper, un exemple d'aquesta configuració.
#16/01/2014 17:43 Programació C# Autor: Alex Canalda