Temporada nyonya i insulsa on les hagi. No m'estranya que hagin cancel·lat la serie. La postura de suficiència del protagonista potser cau simpàtica una temporada, però quatre cansa. Li passa com a House, que el metge bord mola, però 8 temporades...
[Spoilers] Després també cansa el patró, tots els episodis tallats de la mateixa forma: el protagonista es mira l'escena, reparteix tasques impossibles al becari friki en tot, al forense i a la seva jefa que ara sembla secretària. Recull resultats i comença a saltar d'un sospitós al següent, i quan els entrevista els deixa amb la paraula a la boca i es posa a xerra pel mòbil, donant més tasques o recollint resultats. De mentres van esquitxant els episodis amb la preparació del casament del protagonista.
El que no trobo bé és que al final de l'últim capítol li disparin al protagonista i el deixin ferit a terra. Com està cancel·lada no sabrem que passa, suposo que no ho sabien que es cancelaria... Si no ens podem imaginar que es casa i llestos.
Si no es volen dedicar gaires neurones despertes a seguir la serie i només obrir un ull es pot veure, encara que nosaltres hem vist la temporada 1 i em saltat a la 4 sense notar gaires coses... En canvi si es vol una serie amb més substància definitivament no cal mirar aquesta.
Aquesta és fàcil, el DEL és una sentència curta. Es tracta de construir un DELETE amb la clau primària informada (PK). El codi és força curt, encara que hi ha una part algo llarga per les taules que no fan servir identitats com PK i potser tenen una PK composta per alguns camps. Disponible en la clsDades.
publicstring DEL_Builder(Dictionary<string, string> DR)
{
DateTime ValorDateTime;
StringBuilder SB = newStringBuilder();
bool Primer = true;
SB.AppendFormat("DELETE FROM {0} WHERE ", NomTaula);
if (TeIdentitat)
{
SB.AppendFormat("{0}={1}", Identitat, DR[Identitat]);
}
else
{
foreach (clsCamp Camp in CampsResultat)
{
if (Camp.PK)
{
if (!Primer) SB.Append(" AND ");
else Primer = false;
switch (Camp.Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, DR[Camp.NomCamp].Replace("'", "''"));
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DateTime.Parse(DR[Camp.NomCamp]);
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, FDateSQL(ValorDateTime));
break;
caseTipus.dec:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp].Replace(",", "."));
break;
caseTipus.bit:
if (DR[Camp.NomCamp].ToString().ToLower() == "true") SB.AppendFormat("{0}=1, ", Camp.NomCamp);
else SB.AppendFormat("{0}=0", Camp.NomCamp);
break;
default:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp]);
break;
}
}
}
}
return SB.ToString();
}
publicstring DEL_Builder(DataRow DR)
{
DateTime ValorDateTime;
StringBuilder SB = newStringBuilder();
bool Primer = true;
SB.AppendFormat("DELETE FROM {0} WHERE ", NomTaula);
if (TeIdentitat)
{
SB.AppendFormat("{0}={1}", Identitat, DR[Identitat]);
}
else
{
foreach (clsCamp Camp in CampsResultat)
{
if (Camp.PK)
{
if (!Primer) SB.Append(" AND ");
else Primer = false;
switch (Camp.Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, DR[Camp.NomCamp].ToString().Replace("'", "''"));
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DR.Field<DateTime>(Camp.NomCamp);
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, FDateSQL(ValorDateTime));
break;
caseTipus.dec:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp].ToString().Replace(",", "."));
break;
caseTipus.bit:
if (DR[Camp.NomCamp].ToString().ToLower() == "true") SB.AppendFormat("{0}=1, ", Camp.NomCamp);
else SB.AppendFormat("{0}=0", Camp.NomCamp);
break;
default:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp]);
break;
}
}
}
}
return SB.ToString();
}
#28/02/2014 11:21 Programació C# SQLServer Autor: Alex Canalda
Aquestes funcions s'encarreguen de muntar un SQL amb un UPDATE per actualitzar un registre de la taula. Si no hi ha stored procedures es pot recórrer a això. Com sempre en dos sabors, un amb DataRow i un amb Dictionary. Quan es munta el WHERE el les taules que tenen identitat normalment serà fàcil, si no cal suportar que hi hagi una clau primària (PK) composta, per això cal fer un bucle per posar tots els camps i valors de la PK. Disponible a la clsDades
publicstring UPD_Builder(Dictionary<string, string> DR)
{
StringBuilder SB = newStringBuilder();
DateTime ValorDateTime;
bool Primer = true;
SB.AppendFormat("UPDATE {0} SET ", NomTaula);
for (int i = 0; i < CampsResultat.Length; i++)
{
if (!CampsResultat[i].PK)
{
if (!DR.ContainsKey(CampsResultat[i].NomCamp))
{
thrownewException("La fila no té el camp " + CampsResultat[i].NomCamp);
}
if (string.IsNullOrWhiteSpace(DR[CampsResultat[i].NomCamp]))
{
if (!CampsResultat[i].Nulable)
thrownewException("Camp " + CampsResultat[i].NomCamp + " no admet valors NULLs");
else
{
SB.AppendFormat("{0}=NULL, ", CampsResultat[i].NomCamp);
}
}
else
{
switch (CampsResultat[i].Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.AppendFormat("{0}='{1}', ",
CampsResultat[i].NomCamp, DR[CampsResultat[i].NomCamp].Replace("'", "''"));
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DateTime.Parse(DR[CampsResultat[i].NomCamp]);
SB.AppendFormat("{0}='{1}', ",
CampsResultat[i].NomCamp, FDateSQL(ValorDateTime));
break;
caseTipus.dec:
SB.AppendFormat("{0}={1}, ",
CampsResultat[i].NomCamp, DR[CampsResultat[i].NomCamp].Replace(",", "."));
break;
caseTipus.bit:
if (DR[CampsResultat[i].NomCamp].ToLower() == "true")
SB.AppendFormat("{0}=1, ", CampsResultat[i].NomCamp);
else SB.AppendFormat("{0}=0, ", CampsResultat[i].NomCamp);
break;
default:
SB.AppendFormat("{0}={1}, ", CampsResultat[i].NomCamp, DR[CampsResultat[i].NomCamp]);
break;
}
}
}
}
SB.Remove(SB.Length - 2, 2);
//La clau primaria
SB.Append(" WHERE ");
if (TeIdentitat)
{
SB.AppendFormat("{0}={1}", Identitat, DR[Identitat]);
}
else
{
foreach (clsCamp Camp in CampsResultat)
{
if (Camp.PK)
{
if (!Primer) SB.Append(" AND ");
else Primer = false;
switch (Camp.Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, DR[Camp.NomCamp].Replace("'", "''"));
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DateTime.Parse(DR[Camp.NomCamp]);
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, FDateSQL(ValorDateTime));
break;
caseTipus.dec:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp].Replace(",", "."));
break;
caseTipus.bit:
if (DR[Camp.NomCamp].ToString().ToLower() == "true")
SB.AppendFormat("{0}=1, ", Camp.NomCamp);
else SB.AppendFormat("{0}=0", Camp.NomCamp);
break;
default:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp]);
break;
}
}
}
}
return SB.ToString();
}
publicstring UPD_Builder(DataRow DR)
{
StringBuilder SB = newStringBuilder();
DateTime ValorDateTime;
bool Primer = true;
SB.AppendFormat("UPDATE {0} SET ", NomTaula);
for (int i = 0; i < CampsResultat.Length; i++)
{
if (!CampsResultat[i].PK)
{
if (!DR.Table.Columns.Contains(CampsResultat[i].NomCamp))
{
thrownewException("La fila no té el camp " + CampsResultat[i].NomCamp);
}
if (DR.IsNull(CampsResultat[i].NomCamp))
{
if (!CampsResultat[i].Nulable)
thrownewException("Camp " + CampsResultat[i].NomCamp + " no admet valors NULLs");
else
{
SB.AppendFormat("{0}=NULL, ", CampsResultat[i].NomCamp);
}
}
else
{
switch (CampsResultat[i].Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.AppendFormat("{0}='{1}', ", CampsResultat[i].NomCamp,
DR[CampsResultat[i].NomCamp].ToString().Replace("'", "''"));
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DR.Field<DateTime>(CampsResultat[i].NomCamp);
SB.AppendFormat("{0}='{1}', ", CampsResultat[i].NomCamp, FDateSQL(ValorDateTime));
break;
caseTipus.dec:
SB.AppendFormat("{0}={1}, ", CampsResultat[i].NomCamp,
DR[CampsResultat[i].NomCamp].ToString().Replace(",", "."));
break;
caseTipus.bit:
if (DR[CampsResultat[i].NomCamp].ToString().ToLower() == "true")
SB.AppendFormat("{0}=1, ", CampsResultat[i].NomCamp);
else SB.AppendFormat("{0}=0, ", CampsResultat[i].NomCamp);
break;
default:
SB.AppendFormat("{0}={1}, ", CampsResultat[i].NomCamp, DR[CampsResultat[i].NomCamp]);
break;
}
}
}
}
SB.Remove(SB.Length - 2, 2);
//La clau primaria
SB.Append(" WHERE ");
if (TeIdentitat)
{
SB.AppendFormat("{0}={1}", Identitat, DR[Identitat]);
}
else
{
foreach (clsCamp Camp in CampsResultat)
{
if (Camp.PK)
{
if (!Primer) SB.Append(" AND ");
else Primer = false;
switch (Camp.Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, DR[Camp.NomCamp].ToString().Replace("'", "''"));
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DR.Field<DateTime>(Camp.NomCamp);
SB.AppendFormat("{0}='{1}'", Camp.NomCamp, FDateSQL(ValorDateTime));
break;
caseTipus.dec:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp].ToString().Replace(",", "."));
break;
caseTipus.bit:
if (DR[Camp.NomCamp].ToString().ToLower() == "true") SB.AppendFormat("{0}=1, ", Camp.NomCamp);
else SB.AppendFormat("{0}=0", Camp.NomCamp);
break;
default:
SB.AppendFormat("{0}={1}", Camp.NomCamp, DR[Camp.NomCamp]);
break;
}
}
}
}
return SB.ToString();
}
#27/02/2014 15:50 Programació C# SQLServer Autor: Alex Canalda
Per raons que no arribo a entendre a vegades hi ha projectes on el client no vol stored procedures. O potser es requereix fer una migració de dades puntual i no es volen deixar un munt d'stored procedures que ningú farà servir més endevant. Aleshores tota la feina del generador d'storeds no serveix, hi ha que fer servir una altra tècnica.
Per això he mogut uns quants mètodes del generador d'stored a la clsDades, així la clsDades genera SQL sobre la marxa. Es a dir, aprofitant la informació sobre els camps de la taula de la que disposa una clase derivada (nom, longitud i tipus de les columnes) la clsDades ha incorporat unes funcions que generen inserts, updates i deletes sobre la marxa.
Estan disponibles en dos sabors, la que admet DataRow i la que admet Dictionary's, són calcades amb petites modificacions. Començaré pel constructor d'INSerts i deixaré per altres posts els UPD i DEL.
publicstring INS_Builder(Dictionary<string, string> DR, bool IdentityInsert)
{
DateTime ValorDateTime;
StringBuilder SB = newStringBuilder();
SB.AppendFormat("INSERT INTO {0} (", NomTaula);
for (int i = 0; i < CampsResultat.Length; i++)
{
if (!CampsResultat[i].PK || IdentityInsert)
{
SB.Append(CampsResultat[i].NomCamp + ", ");
}
}
//Treiem l'ultima , i espai
SB.Remove(SB.Length - 2, 2);
SB.Append(") VALUES (");
for (int i = 0; i < CampsResultat.Length; i++)
{
if (!CampsResultat[i].PK || IdentityInsert)
{
if (!DR.ContainsKey(CampsResultat[i].NomCamp))
{
thrownewException("La fila no té el camp " + CampsResultat[i].NomCamp);
}
if (string.IsNullOrWhiteSpace(DR[CampsResultat[i].NomCamp]))
{
if (!CampsResultat[i].Nulable)
thrownewException("Camp " + CampsResultat[i].NomCamp + " no admet valors NULLs");
else
{
SB.Append("NULL, ");
}
}
else
{
switch (CampsResultat[i].Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.Append("'" + DR[CampsResultat[i].NomCamp].Replace("'", "''") + "', ");
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DateTime.Parse(DR[CampsResultat[i].NomCamp]);
SB.Append("'" + FDateSQL(ValorDateTime) + "', ");
break;
caseTipus.dec:
SB.Append(DR[CampsResultat[i].NomCamp].Replace(",", ".") + ", ");
break;
caseTipus.bit:
if (DR[CampsResultat[i].NomCamp].ToLower() == "true" ) SB.Append("1, ");
else SB.Append("0, ");
break;
default:
SB.Append(DR[CampsResultat[i].NomCamp] + ", ");
break;
}
}
}
}
SB.Remove(SB.Length - 2, 2);
SB.Append(")");
return SB.ToString();
}
publicstring INS_Builder(DataRow DR, bool IdentityInsert)
{
DateTime ValorDateTime;
Boolean ValorBool;
StringBuilder SB = newStringBuilder();
SB.AppendFormat("INSERT INTO {0} (", NomTaula);
for (int i = 0; i < CampsResultat.Length; i++)
{
if (!CampsResultat[i].PK || IdentityInsert)
{
SB.Append(CampsResultat[i].NomCamp + ", ");
}
}
//Treiem l'ultima , i espai
SB.Remove(SB.Length - 2, 2);
SB.Append(") VALUES (");
for (int i = 0; i < CampsResultat.Length; i++)
{
if (!CampsResultat[i].PK || IdentityInsert)
{
if (!DR.Table.Columns.Contains(CampsResultat[i].NomCamp))
{
thrownewException("La fila no té el camp " + CampsResultat[i].NomCamp);
}
if (DR.IsNull(CampsResultat[i].NomCamp))
{
if (!CampsResultat[i].Nulable)
thrownewException("Camp " + CampsResultat[i].NomCamp + " no admet valors NULLs");
else
{
SB.Append("NULL, ");
}
}
else
{
switch (CampsResultat[i].Tipus)
{
caseTipus.nchar:
caseTipus.nvarchar:
caseTipus.chr:
caseTipus.varchar:
SB.Append("'" + DR[CampsResultat[i].NomCamp].ToString().Replace("'", "''") + "', ");
break;
caseTipus.datetime:
caseTipus.date:
caseTipus.time:
ValorDateTime = DR.Field<DateTime>(CampsResultat[i].NomCamp);
SB.Append("'" + FDateSQL(ValorDateTime) + "', ");
break;
caseTipus.dec:
SB.Append(DR[CampsResultat[i].NomCamp].ToString().Replace(",", ".") + ", ");
break;
caseTipus.bit:
ValorBool = DR.Field<Boolean>(CampsResultat[i].NomCamp);
if (ValorBool) SB.Append("1, ");
else SB.Append("0, ");
break;
default:
SB.Append(DR[CampsResultat[i].NomCamp].ToString() + ", ");
break;
}
}
}
}
SB.Remove(SB.Length - 2, 2);
SB.Append(")");
return SB.ToString();
}
Cal tenir en compte que si després d'un INSERT es vol obtenir el valor d'una columna identitat cal fer servir la funció IDENT_CURRENT. Si es volen agrupar varies sentències SQL s'haurà de fer en un StringBuilder on es vagin acumulant. Les versions de INS/UPD que reben un Dictionary encaixen amb el Deserialitzador que retorna també un Dictionary.
#27/02/2014 11:51 Programació C# SQLServer Autor: Alex Canalda
Segueixo mirant aquesta serie als migdies, i tot just acabo de veure el final d'aquesta temporada. [Spoilers warning] En aquesta temporada el capo del Walt, en Gus, intenta allunyar al pringat del Pinkman del Walt. Si ho aconsegueix i el Pickman pot cuinar la meta el podrà liquidar. També aquesta temporada mostra les relacions del Gus amb un cartel de Mèxic i el Gus els liquida a tots. I quan just es pensa que ja pot liquidar a en Walt, aquest s'adelanta i zas! En Gus mor amb una bomba que ha preparat el Walt. El pla que fa servir per liquidar al Gus és una mica cruel, va mostrant com cada cop més el Walt es va retorçant. De mentres la seva dóna s'involucra en el blanquejat de tants diners, torna amb el Walt i compren un rentacotxes. Però ajuda al seu ex-amant i acaba malament... En algun punt es fa algo lenta, sobretot amb el tema del cunyat del Walt, que ara és ex-agent de la DEA, però bé, es pot passar per alt. Un temporada molt recomanable!
Hi ha dues paraules que són molt fashion i queden molt bé quan es parla de programació:
Mockup: maqueta. En programació es un prototipus molt bàsic. Per fer "mockups" es poden fer servir papers i bolígrafs o un software al respecte, com per exemple el de Balsamiq. El trobo molt útil per dibuixar formularis d'aplicacions.
Scaffolding: andami. En programació un andami es refereix a la infraestructura bàsica necessària per fer funcionar una pantalla, però sense lògica de negoci. Diguem que la clsDades i les stored procedures associades fan això, scaffolding.
#26/02/2014 17:20 Programació Anglès Software Autor: Alex Canalda
He provat diferents formes de posar els SVG dins del HTML. He fet servir dues, la primera fent servir el tag HTML <svg>, i dins del tag posar tot el text SVG que un editor com Inkscape ha generat, fent copiar / enganxar. L'altra es fer servir un tag img i en l'atribut src posar el fitxer svg tal qual surt de l'editor. El resultat visualment és el mateix però, almenys en Firefox, el que fa servir el tag svg permet seleccionar els texts de dins del svg, mentre que el que està com atribut d'un img es comporta com un block, sense poder seleccionar res. Curiós.
#21/02/2014 11:34 Programació HTML/CSS Autor: Alex Canalda
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