Sempre que escric un CREATE TABLE on hi ha la definició d'un Index em sorgeix el dubte de quina diferència hi ha entre un Clustered i un Non-Clustered.
Per entendre-ho cal veure que és un index: els indexos són objectes de la BBDD que ajuden en la cerca de dades dins de la BBDD. Conforme les taules es van fent grans, els indexos es fan indispensables per fer-hi cerques. Tenen una estructura en forma d'arbre, on hi ha una arrel i moltes fulles que són el nivell final. Quan es fa una cerca es mira la clau de l'index per tal fer el menor nombre de lectures possibles fins a arribar a les fulles.
De moment hem vist el que és un índex, ara cal veure la diferència, que està en el format de les fulles. En un index de tipus clustered, les dades de la taula es troben a la mateixa fulla, per tan un cop arriba la lectura de la fulla ja es tenen les dades disponibles.
En canvi en un index Non-Clustered el que hi ha a les fulles és un punter a les dades, cal fer un "bookmark lookup" (una lectura extra) per arribar fins les dades.
Òbviament els index Non-clustered tenen un rendiment inferior a un Clustered donat que tenen que fer una lectura més. Des de SQL Server 2005, es permet posar dins d'un index Non-Clustered camps que no formen part de la clau de l'index per tal d'evitar fer aquesta lectura extra i millorar el rendiment. Respecte com s'inclouen camps a un index, les limitacions sobre incloure camps, ... hi ha un article de la Technet que ho explica, amb exemples.
Tampoc es poden definir tots els indexos com Clustered, ja que només es permet un únic index d'aquest tipus en una taula. La raó és que en realitat els registres estan guardats a disc juntament amb l'index, ordenats seguint l'index i clar, no es pot tenir dos ordres físics alhora.
Punts a tenir en compte al fer servir indexos:
Els camps IDENTITY que siguin clau primària (PK) normalment són els que defineixen el index clustered. Això és per evitar el page split o fragmentació de la taula.
Per cerques de camps concrets que es repeteixen sovint, sense que siguin la PK, es millor fer servir un Non-Clustered. Si els resultats ho permeten es pot afegir algun camp resultat a les fulles.
La PK acostuma a ser una bona candidata per fer un Clustered Index, però si la PK no es fa servir gaire (per exemple factures on s'accedeix per la data de la factura) potser es pot plantejar de fer servir un altra. Però això ja varia a cada cas.
Per cerques compostes (es a dir que tinguin varis camps) cal fer servir Non-Clustered.
#22/01/2014 15:33 Programació SQLServer 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
Qui més qui menys ha patit la pèrdua de dades deguda a que un disc dur ha mort. El nivell de la destrossa ja depèn del backup que un faci. Doncs una empresa de copies de seguretat, Backblaze, ha publicat un estudi sobre 25.000 discos, amb marques i models, on mostra les estadístiques d'errors dels discs. Veient les gràfiques Seagate no queda gaire ben parada.
Els propers discs els agafaré Hitachi. De moment faig servir Western Digital.
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
Comèdia que intenta fer riure però que no es creu res del que passa ni el tato. Tracta d'un empleat exemplar i poc valorat al que una dóna li roba les dades i comença a gastar els seus diners. Llavors decideix anar-la a buscar, la troba i la porta cap al seu estat (als USA són gairebé com països diferents). En el viatge de tornada, es cauen molt bé i la delinqüent que té un cor daurat decideix anar a la presó i arreglar els estropicis que ha fet.
No recomano veure-re-la a menys que diguis: "pse, està plovent, posem algo a la TV i no hi fem gaire cas". Eliminada!
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
Aquest és el sisè llibre d'aquesta magnífica serie de novel·les històriques. Es el desenllaç dels personatges que ocupen ja tots els llibres, bé només els personatges que queden vius. Com han passat força anys alguns simplement ja moren de vells.
Spoiler warning: el llibre tracta de com el Roberto de Artois segueix lluitant per recuperar els seus dominis. Per fer-ho falsifica uns documents, els originals els ha destruit la malvada Mahaut. Finalment la Mahaut mor enverinada per la seva minyona, però les falsificacions fan que el Roberto caigui en desgracia, i el rei (Felipe VI, "el encontrado", fill del Carlos de Valois) que ell ha aconseguit posar al tro, el persegueixi per jutjar-lo. El Roberto fuig a Anglaterra on convenç a Eduardo III (fill de la Loba de Francia) per que entri en guerra amb França per reclamar la corona. El llibre acaba amb la mort del Roberto, en una batalla davant d'una ciutat que havia de ser un tràmit, perque en Roberto accepti la rendició i els de la ciutat quedin bé. Potser és un final algo precipitat, però si va ser així tampoc cal fer un final més llarg. El llibre també lliga un fil de la trama, el fill d'en Guccio que en realitat era fill del "Turbulento" i la Clemencia, que s'entera de qui és de veritat als 40 anys, i mor intentant que li facin cas.
El llibre m'ha agradat molt i està al nivell dels anteriors, moltes intrigues i traicions. Estupendu!
Aquesta acostuma a ser una operació senzilla dins d'una aplicació, sigui web o no. Només cal invocar al ASHX amb l'acció corresponent, la clau primària i llestos. De fet és una crida Ajax força curta, i la poso en un botó al formulari web. També es pot invocar des d'un grid fent un botó adhoc. Un cop rebuda la petició no cal fer res més que crida al DEL de la classe derivada de la clsDades. Alguna vegada m'he trobat que cal fer verificacions de que el registre no tingui altres vinculats en altres taules per donar el missatge pertinent, però res més complicat que això. En l'exemple que hi ha a continuació s'esborra el fitxer que hi ha associat i després el registre de la BBDD.