mercoledì 1 dicembre 2010

Ase 12 vs Ase 15 ( 12.5.x )

Posto di avere una semplice applicazione che utilizza i ODBC per connettersi a SyBase e posto quindi di avere come versione Ase la 12 (senza accedere tramite i connettori per .Net forniti da SyBase), ci si accorge subito o quasi che rispetto alle chiamate ODBC c'e' qualche differenza.
Se poi l'oggetto è attuare una migrazione fra 12 > 15 ci si imbatte in molte differenze...

L'esmpio è una chimata ad una Stored Procedure che accetta n parametri..

OdbcConnection odbcCnn = new OdbcConnection();

odbcCnn.ConnectionString = _connectionstring;

odbcCnn.Open();

OdbcCommand odbcCommand = new OdbcCommand();

odbcCommand.Connection = odbcCnn;

odbcCommand.CommandType = CommandType.StoredProcedure;

// Questa funziona Ma non è la sintassi ODBC
// funziona con il 12 ..
// non funziona con il 15 (12.5.x)
odbcCommand.CommandText = "{call sp_TestParams(?,?)}";

// Questa non funziona Ma è la sintassi ODBC
// funziona con il 15 ..
// non funziona con il 12
odbcCommand.CommandText = "exec sp_TestParams(@,@)";

odbcCommand.Parameters.Add(p1);
odbcCommand.Parameters.Add(p2);

odbcCommand.ExecuteNonQuery();

odbcCnn.Close();

In questo secondo esempio si applica alla possibile attività anche una transazione...

OdbcTransaction odbcTransact = null;

odbcCnn.ConnectionString = _connectionstring;

odbcCnn.Open();

odbcTransact = odbcCnn.BeginTransaction(IsolationLevel.Serializable);

//
// questo command è necessario per
// Ase 15 (12.5.x)
// MA non necessario per la 12
// potrebbe dare errori a run Time
//
OdbcCommand odbcCommandChained = new OdbcCommand();
odbcCommandChained.Connection = odbcCnn;
odbcCommandChained.Transaction = odbcTransact;
odbcCommandChained.CommandType = CommandType.StoredProcedure;
odbcCommandChained.CommandText = "SET CHAINED OFF";
odbcCommandChained.ExecuteNonQuery();


OdbcCommand odbcCommand = new OdbcCommand();
odbcCommand.Connection = odbcCnn;
odbcCommand.Transaction = odbcTransact;
odbcCommand.CommandType = CommandType.StoredProcedure;

// Questa funziona Ma non è la sintassi ODBC
// funziona con il 12 ..
// non funziona con il 15 (12.5.x)
odbcCommand.CommandText = "{call sp_TestParams(?,?)}";

// Questa non funziona Ma è la sintassi ODBC
// funziona con il 15 ..
// non funziona con il 12
odbcCommand.CommandText = "exec sp_TestParams(@,@)";

odbcCommand.Parameters.Add(p1);
odbcCommand.Parameters.Add(p2);

odbcCommand.ExecuteNonQuery();

odbcTransact.Commit();

odbcCnn.Close();

Le differenze che notato non sono proprio un "piccolo impatto" anzi il costo per modificare un applicazione valutando eventualmente il passaggio dalla 12 > 15.

C'è da dire che non è detto che sia necessario invocare la SET CHAINED OFF, ma nel caso lo si dovesse affrontare questa situazione risulta o può risultare necessario impostarla a ON alla fine delle nostre attività.

E' possibile che vi siano molte altre differenze che ancora non ho trovato, ma penso (mi auguro) che visto il possibile allineamento alla sintassi ODBC, renda più semplice la conversione.

venerdì 26 novembre 2010

From string to stream...

Alcune situazioni sembrano davvero fatte per rendere le cose complesse..

Vi porto quest'esmpio atto ad un processo di validazione di un testo XML.
Questo è contenuto in una string che è passata a questo metodo come
parametro.

In un primo momento mi è sembrato corretto affrontare la soluzione così.

XmlReaderSettings xmlSettings = new XmlReaderSettings();
xmlSettings.Schemas.Add("http://cSharp.org", Path.Combine(_xmlRequestPath, ValidatorFileName));
xmlSettings.ValidationType = ValidationType.Schema;

XmlReader xR = XmlReader.Create(dataDocument, xmlSettings);

Tuttavia questa soluzione propone un errore che sembra davvero incomprensibile ..
"CARATTERI NON VALIDI NEL PERCORSO"...
Già perchè utilizzando il metodo XmlReader.Create(string,XmlSettings) ci si che per il
primo parametro non ci sia il nudo e crudo XML .. ma il percorso

Acc.. ma io non ho il percorso ho una stringa...

A questo punto dopo aver letto le varie firme di XmlReader.Create scopro che si può
utilizzare uno stream ... e quindi non rimane altro che convertire una
stringa in Stream.

Efficace e di concetto c'e' il memory stream certo magari occupa un po di ram,
ma è sempre meglio che ipotizzare di salvarsi il file in una cartella temporanea,
accedervi e cancellarlo...

XmlReaderSettings xmlSettings = new XmlReaderSettings();
xmlSettings.Schemas.Add("http://cSharp.org", Path.Combine(_xmlRequestPath, ValidatorFileName));
xmlSettings.ValidationType = ValidationType.Schema;

MemoryStream msx = new MemoryStream(Encoding.UTF8.GetBytes(dataDocument));

XmlReader xR = XmlReader.Create(msx, xmlSettings);

E' chiaro che msx dovrà essere messo a null non appena non servirà più ...

mercoledì 24 novembre 2010

XmlSerializer ,list ,CollectionBase, Array

Meno male che ho 5 min per postare quest'esempio.. il cui prò ha poco senso, ma potrebbe chiarire qualche cosa su come si comporta la serializzazione in xml...

Come sempre il post sarà lunghetto, armatevi di pazienza e buona lettura.

partiamo da una cosa semplice un Entity più o meno banale.

public class Entity
{
#region Private
private int _UserID;
private string _UserName;
private string _UserSurname;
private DateTime _UserBirth;
private string _UserAddress;
#endregion


#region Public
public int UserID
{
get { return _UserID; }
set { _UserID = value; }
}

public string UserName
{
get { return _UserName; }
set { _UserName = value; }
}

public string UserSurname
{
get { return _UserSurname; }
set { _UserSurname = value; }
}

public DateTime UserBirth
{
get { return _UserBirth; }
set { _UserBirth = value; }
}

public string UserAddress
{
get { return _UserAddress; }
set { _UserAddress = value; }
}
#endregion
}

Nulla di davvero eccezionale...

Quindi passiamo alle possibili collezioni ...

public class Entities:List
{
}

public class collectionEntities : System.Collections.CollectionBase
{
public void Add(Entity et)
{
this.List.Add(et);
}
}


public class GenericEntities
{

public class Item
{
private Item next;

private T data;


public Item Next
{
get { return next; }
set { next = value; }
}


public T Data
{
get { return data; }
set { data = value; }
}


public Item(T t)
{
next = null;
Data = t;
}
}

private Item Element;

public GenericEntities()
{
Element = null;
}

public void Add(T t)
{
Item i = new Item(t);
i.Next = Element;
Element = i;
}

public IEnumerator GetEnumerator()
{
Item current = Element;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
}

e quindi i serializer ...

public static class SerializerTool
{
public static string Serialize(Entities eList)
{
XmlSerializer xmls = new XmlSerializer(typeof(Entities));


StringWriter sw = new StringWriter();

xmls.Serialize(sw, eList);


return sw.ToString();
}

public static string Serialize(Entity[] eList)
{
XmlSerializer xmls = new XmlSerializer(typeof(Entity[]));


StringWriter sw = new StringWriter();

xmls.Serialize(sw, eList);


return sw.ToString();
}

public static string Serialize(collectionEntities eList)
{
XmlSerializer xmls = new XmlSerializer(typeof(collectionEntities));


StringWriter sw = new StringWriter();

xmls.Serialize(sw, eList);


return sw.ToString();
}

public static string Serialize(GenericEntities eList)
{
XmlSerializer xmls = new XmlSerializer(typeof(GenericEntities));


StringWriter sw = new StringWriter();

xmls.Serialize(sw, eList);


return sw.ToString();
}
}

bene questo è tutto quello che ci serve o quasi per il nostro esempio..

manca solo il main

class Program
{
static void Main(string[] args)
{

#region Entity Creation
Entity e1 = new Entity();

e1.UserName = "Mario";
e1.UserSurname = "Rossi";
e1.UserBirth = DateTime.Parse("01/01/1994");
e1.UserAddress = "via roma n1 ";


Entity e2 = new Entity();

e2.UserName = "Davide";
e2.UserSurname = "Bianchi";
e2.UserBirth = DateTime.Parse("05/05/1996");
e2.UserAddress = "via milano n2 ";

Entity e3 = new Entity();

e3.UserName = "Giorgio";
e3.UserSurname = "Verdi";
e3.UserBirth = DateTime.Parse("05/05/1996");
e3.UserAddress = "via torino n3";
#endregion


// LIST T
Entities el = new Entities();

el.Add(e1);
el.Add(e2);
el.Add(e3);

Console.WriteLine("List T");
Console.WriteLine(SerializerTool.Serialize(el));

Console.ReadLine();

// Array
Entity[] eA = new Entity[3];

eA[0] = e1;
eA[1] = e2;
eA[2] = e3;

Console.WriteLine("ARRAY");
Console.WriteLine(SerializerTool.Serialize(eA));

Console.ReadLine();

// collectionBase
collectionEntities eC = new collectionEntities();

eC.Add(e1);
eC.Add(e2);
eC.Add(e3);

Console.WriteLine("Collection");
Console.WriteLine(SerializerTool.Serialize(eA));

Console.ReadLine();


GenericEntities gE = new GenericEntities();

gE.Add(e1);
gE.Add(e2);
gE.Add(e3);

Console.WriteLine("Generics");
Console.WriteLine(SerializerTool.Serialize(eA));

Console.ReadLine();

}
}

Io che sono più o meno testardo provando questo codice non i sono accorto di grandi differenze,
tanto che l'xml risultante è uguale per tutti ...
Che cosa cambia quindi ? I tempi ? I pesi ? L'occupazione di memoria ?

Per List e Arry non ho scritto righe di codice per Collection ho dovuto implementare il metodo Add ( ma è specializzato solo per le Entity ) per Generics ho dovuto implementare
tutto o quasi ma posso riutilizzare il codice completamente per altri scopi...

Quindi a parità di risultati possiamo ancora una volta scegliere differenti modalità di implementazione.

mercoledì 10 novembre 2010

Replace case Insensitve ?

Ahi.. la string.Replace() accetta due parametri ma è accettabile ?? No direi proprio di no.

Effettivamente mi sono prodigato più volte nell'affrontare una Replace IgnoreCase, e in molti (troppi casi) ci si trova a dover affrontare utenti che non leggono il manuale (come tutti noi del resto) è grave, davvero grave quando è chi installa un applicazione e la configura a non leggere
le specifiche.

Quindi ci si trova a leggere elementi da un file di configurazione che hanno valori non consoni
a quel che ci si aspetta.

Capita quindi che utilizzando una Replace in un Template qualcosa non vada...

Tuttavia nel namespace System.Text c'e' RegularExpression (ammetto un mondo nuovo e bellissimo) ... Dove potete trovare questo gioiello.

Regex.Replace (Testo di partenza, Testo Da trovare, Testo Che sostituisce, RegexOptions)

E proprio questo RegexOptions (Enumerativo) contiene anche IgnoreCase.

Qualcuno dice un po lento ( ma se si tratta di poche occorrenze fa il suo sporco lavoro per bene)

Volendo nello splendido mondo di Regular Expression si potrebbe anche pensare a qualcosa
di più efficace... ma credo che possa già essere efficace così.

List.Sort( )

Decisamente ostico, ma di sicura utilità...
Imbattersi in un sort su elementi complessi non è sempre una cosa semplice, anzi il più delle volte siamo noi a dover implementare una logica di Sort.

Gia ma come fare ???

Eccovi un esempio completo e funzionante di come creare un sort

Create una console Application in C# e copiate interamente il codice.

using System;
using System.Collections.Generic;
using System.Net;
using System.Text;

namespace ListT_Sort
{
class Program
{
static void Main(string[] args)
{
UseHost uh = new UseHost();

uh.CreateHostList();

uh.SortBeforePrint();

Console.ReadLine();
}
}

public class Host
{
private string _HostName;
private IPAddress _HostIP;

public string HostName
{
get{return _HostName;}
set{_HostName = value;}
}

public IPAddress HostIP
{
get{return _HostIP;}
set{_HostIP = value;}
}

public Host( string Name, IPAddress IP)
{
_HostName = Name;
_HostIP = IP;
}


}

public class UseHost
{
private List Hosts = new List();

public void CreateHostList()
{
Hosts.Add(new Host("OfficePC", IPAddress.Parse("175.10.33.1")));
Hosts.Add(new Host("Localhost",IPAddress.Parse("127.0.0.1")));
Hosts.Add(new Host("HomePC",IPAddress.Parse("192.168.3.1")));
Hosts.Add(new Host("PortablePC",IPAddress.Parse("194.30.5.28")));
Hosts.Add(new Host("AbtServer", IPAddress.Parse("196.221.5.28")));
}

public void SortBeforePrint()
{
Hosts.Sort(CompareHosts);

for (int i =0; i< Hosts.Count; i++)
{
Console.WriteLine (Hosts[i].HostName);
}
}

public int CompareHosts(Host hp1, Host hp2)
{
if ((hp1 == null) && (hp2 == null))
{
return 0;
}

if ((hp1 == null) && (hp2 != null))
{
return -1;
}

if ((hp1 != null) && (hp2 == null))
{
return 1;
}

if (hp1.HostName == hp2.HostName)
{
return 0;
}

return String.CompareOrdinal(hp1.HostName, hp2.HostName);
}
}
}


Spiegazione pezzo per pezzo..

Questo è il tipo che noi abbiamo implementato o che abbiamo bisogno di implementare.
Si tratta di un tipo semplice tuttavia non implementa altre interfaccie ma solo due
campi.
Tuttavia già questa struttura è una buona base su cui studiare il sort.

public class Host
{
private string _HostName;
private IPAddress _HostIP;

public string HostName
{
get{return _HostName;}
set{_HostName = value;}
}

public IPAddress HostIP
{
get{return _HostIP;}
set{_HostIP = value;}
}

public Host( string Name, IPAddress IP)
{
_HostName = Name;
_HostIP = IP;
}
}

Ho implementato il costruttore solo per fare più in fretta...

Lista degli Host che dobbiamo Ordinare... messa giù un po a caso...

Hosts.Add(new Host("OfficePC", IPAddress.Parse("175.10.33.1")));
Hosts.Add(new Host("Localhost",IPAddress.Parse("127.0.0.1")));
Hosts.Add(new Host("HomePC",IPAddress.Parse("192.168.3.1")));
Hosts.Add(new Host("PortablePC",IPAddress.Parse("194.30.5.28")));
Hosts.Add(new Host("AbtServer", IPAddress.Parse("196.221.5.28")));


Chiamata al metodo di Sort ...
Hosts.Sort(CompareHosts);


Comparazione fra elementi...

if ((hp1 == null) && (hp2 == null))
{
return 0;
}

Gli elementi sono uguali ( e nulli )

if ((hp1 == null) && (hp2 != null))
{
return -1;
}

il secondo elemento è per forza maggiore il primo è null!!

if ((hp1 != null) && (hp2 == null))
{
return 1;
}

Il primo elemento è il maggiore ( vedi sopra)
if (hp1.HostName == hp2.HostName)
{
return 0;
}


Bene dato che il mio elemento di comparazione sono
i campi nome .. li confronto..
return String.CompareOrdinal(hp1.HostName, hp2.HostName);

Smart and Easy...

martedì 2 novembre 2010

DataTable.Select( Filter )

Giusto perchè sono sempre il primo a dimenticarsi le cose semplici... ecco una delle mille cosette che servono ma che ci si dimentica di utilizzare.

Quanto ci cosa una query in memoria ??? e costa di meno di una Query su Db ???

Come sempre tutto dipende da che cosa si deve fare anche se sono più che convinto che in molti casi caricarsi un centinaio di record in memoria non sia poi così oneroso (ad oggi).

In alcuni casi torna comodo, però saper effettuare una query anche in memoria..

in questo esempio ho creato una dataTable con 4 colonne, e ben poche righe, in questo modo
è più semplice seguire gli esempi.


DataTable dt = new DataTable("filteTest");
dt.Columns.Add("clnNome");
dt.Columns.Add("clnCognome");
dt.Columns.Add("clnNazionalita");
dt.Columns.Add("clnNote");


dt.Rows.Add(new object[] { "Valentino", "Rossi","i", "Motociclista" });
dt.Rows.Add(new object[] { "Lorenzo", "Jorge", "s", "Motociclista" });
dt.Rows.Add(new object[] { "Daniel", "Pedrosa","s", "Motociclista" });
dt.Rows.Add(new object[] { "Rolling", "Stoner","a", "Stunt man" });
dt.Rows.Add(new object[] { "Andrea", "Dovizioso","i", "Motociclista" });


DataRow[] drsMotociclisti = dt.Select("clnNote = 'Motociclista'");
Console.WriteLine( drsMotociclisti.Length.ToString() ); // scrive 4

DataRow[] drsItaliani = dt.Select("clnNazionalita = 'i'");
Console.WriteLine(drsItaliani.Length.ToString()); // scrive 2

DataRow[] drsMisti = dt.Select("clnNazionalita = 's' OR clnNazionalita ='i' ");
Console.WriteLine(drsMisti.Length.ToString()); // scrive 4

DataRow[] drsMisti = dt.Select("clnNazionalita = 'i' AND clnNote = 'Motociclista ");
Console.WriteLine(drsMisti.Length.ToString()); // scrive 2


ps. Non è una nota polemica, ma non ho proprio trovato esempi in cui far CADERE Stoner, tuttavia sono convinto che farlo sia già bravo senza il mio aiuto.

venerdì 29 ottobre 2010

Sillabe (Array di sillabe italiane)

Come sempre ... devo inventarmi qualcosa per risolvere un problema...

Scherzavo .. eh eh eh ! questa volta il problema non esiste ma sono più che sicuro che questo post possa essere uno dei miei post più importanti.

Già sono più che sicuro che molti di noi si siano più volte imbattuti in questa domanda.. MA QUALCUNO AVRA' MAI PENSATO A CREARE UN ARRAY DELLE SILLABE ITALIANE ??

Credo (anche se non lo spero) di essere il primo ... ed ecco il quanto

public const string[] Sillabe { "à","a","ab","ac","àc","ad","af","ag","ai","al","am","an","ap","ar","àr","as","at",
"au","az","az","ba","bà","bac","bal","bam","ban","bar","bàr","bat","be","bè","bel",
"bèl","béz","bi","bil","bo","bò","boc","bor","bra","bràn","bri","brò","brùt","ca",
"cà","cal","càl","cam","can","car","cat","ce","cel","cèl","cem","cèn","cet","ces",
"cer","cét","che","ché","chi","chì","chia","chià","chiac","chie","chiò","chio","cì",
"ci","cia","ciè","ciè","cièn","cìl","cil","cin","cio","cio","ciòl","clé","co","cò",
"coc","col","còl","com","con","cón","cop","cor","còr","crà","cra","crè","crè","cre",
"crì","cri","cro","crò","cru","cu","cù","cul","da","dà","dar","dàt","de","dèl","den",
"di","dì","dig","dio","diu","do","dò","dol","dól","don","dót","du","dù","dur","e",
"ei","eu","fa","fà","fab","fal","fan","fè","fèt","fi","fie","fiu","fo","for","fra",
"fra","frà","frap","fré","frin","frìt","frón","fron","fu","ga","gà","gal","gat",
"gaz","ge","get","ghe","gi","giàl","gio","giù","giu","giùn","gli","glie","glie",
"glie","gliò","gliò","go","gò","gon","gor","gra","grìl","gròp","gù","gua","guan",
"gue","guèl","guì","gui","i","ì","ia","ia","ià","ià","iai","ie","iei","in","ìn",
"io","io","iu","iuo","ka","kov","la","là","làn","las","làsc","làt","laz","le","lè",
"lèn","let","lét","lét","lèt","léz","léz","lì","lì","lia","liar","liè","lìn","lin",
"lis","lo","lò","lon","lon","lón","lòp","lòp","lór","lot","lòt","lot","lu","lù","lù",
"luò","ma","mà","mac","mal","man","màr","mar","mat","me","mé","mè","mè","mèl","mén",
"men","met","mi","mì","mic","mit","mo","mò","mol","mon","mor","mór","mul","mùl","na",
"nà","nal","nar","né","nè","ne","nel","nèl","ner","nér","nèr","net","ni","nì","nìs",
"niz","no","nò","noc","nòc","non","nòt","nu","nù","nua","nuo","nuò","o","ò","oc","oi",
"on","os","ot","ou","pa","pà","pàl","pal","pan","pàn","pàp","par","par","pàt","pe",
"pé","pèl","pen","per","pèr","per","pèt","péz","pi","pì","pie","pìl","pin","piu",
"pla","plàn","ple","pli","pli","plu","pluì","pò","po","pó","pò","pòl","pór","pòr",
"prà","pra","pre","pren","prez","pric","pro","pu","pùl","pùm","quag","quas","quàt",
"quin","ra","rà","rac","ral","ran","ràn","rat","rat","ré","re","rec","rèl","rem",
"ren","rèn","rét","rì","ri","rial","riàn","ric","rio","riz","ro","rò","róm","ron",
"rù","ru","rul","rùl","sa","sà","sac","sàc","sàg","sal","sàl","san","sàn","sba",
"sbal","sbi","sca","scà","scar","scel","sci","sci","sco","scon","scor","scrac","scré",
"scu","scu","sdi","se","sè","se","sen","sen","ses","set","sfar","si","sì","sià",
"sic","sìc","sièr","sìl","sim","slà","slab","slàr","slès","slit","smàl","smu",
"so","sò","só","sol","sòl","són","sor","sòr","sot","spèt","spi","spre","sra",
"sta","stà","stan","stè","ste","stéc","sti","stì","stic","stìn","sto","sto","stò",
"stra","strà","stràc","stres","strì","stri","stril","striz","strò","stroz","stu",
"stuò","su","sù","suà","sur","svio","ta","tà","tal","tàn","tap","tàr","tàr","te",
"tè","tè","tel","tèl","ten","tèn","tèn","ter","ti","tì","tià","tiè","tiè","tis","tìs",
"to","tó","tò","ton","tór","tor","tra","trét","tri","tro","trol","tron","trù","tu",
"tù","tù","tùc","u","ua","uai","uc","ue","uei","ui","un","uo","uoi","va","ve","vel",
"ven","vèn","ver","vi","vì","vil","vo","vò","vol","vòr","vór","yò","zà","za","zam",
"zé","ze","zec","zèl","zèl","zi","zì","zial","zio","zió","zio","zò","zòp","zop","zu"
};

Probabilmente qualche doppione c'e' è possibile che non siano tutte, ma sono più che convinto
che sia un ottimo (se non essenziale) punto di partenza...

A che cosa può servire ??
A) Generatore di parole casuali
B) Generatore di frasi casuali
C) Generatore di Password (volendo si .. )

E molto altro credo .. la fantasia rimane a voi...

martedì 12 ottobre 2010

From XML to C# Class...

Si già e poi ?? Verrebbe da dire, ma questa funzionalità esiste dal VS 2005 solo che non tutti lo sanno.

Esattamente in che cosa consiste ? Esiste un tools "XSD" che consente varie opzioni, quella in analisi consente
a) la generazione di un file xsd, partendo da un xml
b) la generazione di un file .cs (classe o classi) che rappresentano l'xsd.

Onestamente ho fatto solo una prova e sono più o meno convinto del risultato ottenuto, anche se sono partito da un XML.

Fate questa prova:
-createvi un file xml che possegga l'intestazione un nodo (root) e una serie di figli..
-qualcosa di simile a
[animali]
[animale]
[tipo] qudrupede [/tipo]
[verso] bau [/verso]
[famglia] canidi [/famglia]
[nome] Doggy [/nome]
[/animale]
[animale]
[tipo] qudrupede [/tipo]
[verso] miao [/verso]
[famglia] felinidi [/famglia]
[nome] kitty [/nome]
[/animale]
[/animali]
-salvatevi il file come [nome].xml
-da prompt di visual studio lanciate il comando XSD [nome].xml
-il risultato è un xsd che descrive il vostro xml
-sempre da prompt lanciate il comando XSD [nome].xsd /Classes
-E il risultato è il file [nome].cs che contiene le classi rappresentanti il vostro XML

Provare per credere....

giovedì 7 ottobre 2010

IsNumeric ( regular Expression )...

Avevo già affrontato questo simpatico problema... come comprendere se un valore in una stringa, è o non è un numero.

Al tempo mi ero risolto il problema (non molto elegantemente) con un try catch dove nel try avevo bene pensato di forzare il valore letto in un int in caso di Exception... non stavo trattando un numero.

Bene l'idea era valida, e funzionante ma non del tutto efficace..

Ad oggi ecco due nuove Idee... testate e garantite basate sulle regular expression.

public bool isNumeric (string value)
{
Regex verifier = new Regex( @"^(\+|-)?\d+(.|,)*\d+$");

return verifier.IsMatch(value);
}

public bool isNumeric (string value)
{
Regex verifier = new Regex(@"[0-9](.|,)*[0-9]$");

return verifier.IsMatch(value);
}

Nel primo caso si considera anche la possibilità di avere un + o un - come intestazione del numero, il secondo no.

Entrambi funzionano, ma è necessario includere la System.Text.RegularExpressions...

martedì 5 ottobre 2010

Assmbly Build Version...

Mi è capitato di voler sapere la data di build di un assembly (non la data di creazione o la data in cui è stato copiato.. per quello basta leggerle da file sistem...) e non è stato semplice comprendere dove e come reperire l'informazione.

Tuttavia, il linker (in fase di build) si occupa anche di questo... E' necessario fare una premessa, questo codice in parte trovato on line, in parte modificato da me, ha lo scopo nudo e crudo di recuperare una porzione di byte che rappresentano proprio o quasi la data di buil di un assembly.

E' doveroso ricordare che quest'informazione proviene in parte o in toto dall'assembly Version che se compilato con logica (1.0.*) funziona... (nel mio caso funziona anche con (1.0.0.0).

private string GetAssemblyBuildDate()
{
//
// percorso dell'assembly
//
string filePath = System.Reflection.Assembly.GetCallingAssembly().Location;
//
// posizione di Offset dell'header..
//
const int c_PeHeaderOffset = 60;
//
// posizione in cui il linker deposita la data durante la sua fase.
//
const int c_LinkerTimestampOffset = 8;
//
// buffer
//
byte[] b = new byte[2048];
//
// stream di byte.
//
System.IO.Stream sAssembly = null;

try
{
//
// operazione ad assembly aperto...
//
sAssembly = new System.IO.FileStream(
filePath,
System.IO.FileMode.Open,
System.IO.FileAccess.Read
);
//
// legge dal byte 0 al 2048'esimo
//
sAssembly.Read(b, 0, 2048);
}
finally
{
//
// se non è riuscito nel tentativo di aprire il buffer..
//
if (sAssembly != null)
{
sAssembly.Close();
}
}

//
// legge dal buffer i primi 60 char/byte
//
int i = System.BitConverter.ToInt32(b, c_PeHeaderOffset);
//
// crea il limite (superiore byte che rappresentano la data di build in secondi...) partendo da ciò che letto
//
int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);

//
// si crea la data di comodo a cui sommare il limite
//
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);

//
// li aggiunge alla data di comodo
//
dt = dt.AddSeconds(secondsSince1970);
//
// "aggiunge" il gmt
//
dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);

return dt.ToShortDateString() + " " + dt.ToLongTimeString();
}

Come sempre se recuperate questo codice, potrebbe essere necessario verificare l'indentazione dei commenti ...

giovedì 9 settembre 2010

C# COM Visible

Risolviamo questo problema una volta per tutte.

C'e' tanta letteratura nel web sull'argomento, ma è davvero complesso ottenere una soluzione veritiera. C'e' chi afferma che serva solo un flag nell'assembly info, c'e' chi afferma che è necessario
lo strong Name, c'e' chi afferma che servono danze e balli propriziatori o congiunzioni astrali...

C'e' chi come me prova e ottiene la solzuione senza dover ricorrere a strani oracoli.

-Step by step C#Com Visible
1) create una nuova soluzione di tipo Class Library
2) create una classe
3) aggiungete il namespace System.Runtime.Interop
4) aggiungete alla classe i vostri Metodi / Proprietà
5) aggiungete ai metodi che devono essere COM visible gli Attributi
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
6) aggiungete (se non lo fate per abitudine i commenti ///summary)
7) create un INTERFACE che mappa tutti i Metodi e tutte le proprietà COM visible della classe
8) aggiungete alla classe l'interfaccia.

-Codice d'esempio
(ho rimosso il maggiore e il minore nei summary)

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace ComVisibleTest
{

public interface iComTest
{
string HelloWord();
int GetInt();
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class ComTest:iComTest
{
/// summary
/// Non fa nulla ma lo fa bene
/// /summary
/// returns/returns
[ComVisible(true)]
public string HelloWord()
{
return "ciao mondo";
}

/// summary
/// Restituisce il mio numero preferito.
/// /summary
/// returns/returns
[ComVisible(true)]
public int GetInt()
{
return 7;
}
}
}

-Come e cosa fare negli Attributi dell'Assembly
Nelle proprietà del progetto è necessario esplicitare che si tratta di un ComVisible.
Quindi dalle proprietà del progetto/assembly information settare:
MAKE ASSEMBLY COM VISIBLE = true.

Nelle proprietà del progetto sotto build può essere necessario indicare di ri registrare
ad ogni build l'assembly
Quindi dalle proprietà di build settare:
REGISTER FOR COM INTEROP = true.

-Volte davvero fare un salto di qualità ??
Nelle azioni di build in POST BUILD
Regasm [nome della libreria].dll /tlb:[nome della libreria.]tlb
es
Regasm ComVisibleTest.dll /tlb:ComVisibleTest.tlb


-Qualche info in più...
a) L'interfaccia, serve anzi è necessaria, perchè rappresenta l'entryPoint, o header della nostra dll. Come questa venga tratta, o come questa venga resa a livello di TLB, giuro non lo so. Ma senza l'interfaccia VB6 ( mio caso di test per questo prototipo ) vede la classe ma non i metodi, aggiungendo l'interfaccia VEDO TUTTO.

b) in VB.Net tutto questo non serve, anzi basta solo indicare che la nostra libreria è comvisible.

c) E' possibile che in caso la soluzione sia composta da più libreria, si devono realizzare tante
interfaccie per quante classi dovranno essere rese come COMvisible.


-Note Tecniche sull'ambiente:
Visual Studio 2008 tipo di build "Release X86"
Visual basic 6 sp 6
Windows 7 64 bit

martedì 24 agosto 2010

FlowLayoutPanel C#

Avete mai provato a usare questo splendido controllo ?? Io mi sto divertendo proprio ora ad usare uno strumento javesco in c#...
Non so effettivamente chi abbia fatto il primo passo con questo tortuoso strumento .. tuttavia lo trovo discretamente pratico, nel caso si debba ricorrere ad array di controlli... e gestirne il riposizionamento dopo la rimozione di uno o più elementi.

Nel mio caso sto cercando di realizzare un Tab control un po più ricercato e customizzabile di quello proposto nel VS 2008 che devo dire fa pure il suo porco mestiere... ma lascia poco spazio alla fantasia.

Ora imbattendomi in questo controllo speravo di trovare fra le properties qualcosa di utile.. stile grip / grid size.. ma nulla.

Scopro e mi pare carino condividere che "non esiste nulla di cio'" tuttavia lo spazio fra un controllo e l'altro all'interno del FlowLayoutPanel è demandato alla proprietà margin dei controlli che sta usando... o impilando.

Bene quindi ricordate che se usate :
Button myButton = new Button();
myButton.Text = "My Button";
flpMain.control.Add(myButton);
di aggiungere
myButton.Margin= new Padding(0, 0, 0, 0);

Attenzione MARGIN è una struct e non è del tutto accessibile..

lunedì 16 agosto 2010

ConfigurationManager Add Key & Value...

Sempre a me...?? ma perchè quando voglio fare qualcosa io .. becco sempre qualche sfiga strana..

System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

config.AppSettings.Settings.Remove(settingName);

config.AppSettings.Settings.Add(settingName, settingValue);

config.Save(ConfigurationSaveMode.Modified);

ConfigurationManager.RefreshSection("appSettings");

Questo effettivamente più che un add.. è un Edit, ma non è questo il vero problema... anzi...
In questo breve brano di codice sono emerse ben 2 cose...

A) in debug mode... non funzionerà e potrete tentarci quanto vorrete... ma non andrà... questo
perche ????
- Semplice in debug non accedete al vero app config... ma all' [Applicativo].vshost.exe.config...

Sembra facile ma non è difficile.. c'e' un motivo più che semplice, non modificare nulla, nel caso
l'exe originale ( ma perchè ??? ) fosse nella stessa cartella... ( qualcuno in m.s. si droga ).

B) a runtime va...

C) Lo sapevate che ...
In proprietà del vostro applicativo c'e' una voce chiamata... AssemblyName...
scrivete li dentro il nome del vostro "exe"...ehm...
poi Andate su assembly information (sempre da proprietà ) e modificate la voce
product.. in modo che sia diverso del vostro nome "exe"...
ED ECCO CHE APPARE UN ALTRO [nome prodotto].vshost.exe.config... !!
Vuoto naturalmente...

Ora... Ora... e ancora ora ... mi pare lecito avere tante possibilità .. ma dai un cotrollino sui nomi,
lo si poteva anche mettere no ???

martedì 10 agosto 2010

Array di bottoni ?? eccolo...

Osservando le statistiche mi sono accorto che molti arrivano sul mio sito cercando "ARRAY di Bottoni"...

Ora posto che questo ricerca sia molto vicina a come creare un array di controlli, mi sembra carino pubblicare il seguente brano di codice che da qualche lume sull'argomento.

Anche se onestamente sconsiglio questa pratica.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

public class ButtonArrayController
{
Button[] _buttons = new Button[4];

public void CreateControlList()
{
_buttons = new Button[4];

_buttons[0] = new Button(); // create a new item
_buttons[0].Text = "New";
_buttons[0].Click += new EventHandler(ButtonArrayController_Click);

_buttons[1] = new Button(); // edit element (select to edit)
_buttons[1].Text = "Edit";
_buttons[1].Click += new EventHandler(ButtonArrayController_Click);

_buttons[2] = new Button(); // delete element (select to delete)
_buttons[2].Text = "Delete";
_buttons[2].Click += new EventHandler(ButtonArrayController_Click);

_buttons[3] = new Button(); // commit from new and edit
_buttons[3].Text = "Save";
_buttons[3].Click += new EventHandler(ButtonArrayController_Click);
}

public void SetStatus(int status)
{
for (int i = 0; i < 4; i++) { _buttons[0].Enabled = false; }

switch (status)
{
case 0: { _buttons[0].Enabled = true; break; } // init
case 1: { _buttons[1].Enabled = true; _buttons[2].Enabled = true; break; } // from grid click
case 2: { _buttons[3].Enabled = true; break; } // after case 0 or 1
}
}


public void ButtonArrayController_Click(object sender, EventArgs e)
{
// do action here..
}
}


Ad ogni modo un array di controlli in c# è un array e non quella strana situazione che creava VB6, tanto comoda quanto rischiosa.

mercoledì 28 luglio 2010

ERROR [NA000] [Microsoft][ODBC driver for Oracle][Oracle]ORA-06413: Connessione non aperta. Risolto

Quando si verifica?
Nella maggior parte dei casi si verifica quando da un web service pubblicato su un II7.# si tenta di accedere ad un driver odbc realizzato a 32 bit.
Ci sono Workaround Noti?
Facendo parecchie ricerche emergono "estenuanti affermazioni inutili" come
-Associare a utenze diritti vari...
-Spostare programmi in cartelle varie...
-Utilizzare prodotti di terze parti...

Ma nessuno di questi funzioni o è la suoluzione effettiva, perchè il problema non è li...

Risoluzione per windows 7
A) assicurarsi di aver realizzato un odbc a 32 bit
-Accedere a c:/windows/siswow64/odbcad32.exe
-Creare un odbc per Oracle Microsoft ODBC for Oracle

B) assicurarsi di aver compilato la propria soluzione (progetto per progetto) con:
-Framework 2.0
-cpu X86

C) aprire "Gestione Internet Information Server"
-Scegliere: Pool di applicazioni
Aggiungere quindi un nuovo pool con le specifiche come da immagine:


Accertarsi che la voce "ATTIVA APPLICAZIONI A 32 bit " sia a TRUE

D) pubblicare la propria soluzione



E) Tornare su IIS e configurare la soluzione per l'appPool appena creato
-Selezionare il gruppo di appPool
-Verificare fra le proprietà degli appPool le applicazioni associate
(Dovrebbe trovarsi sotto DEFAULT APPPOOL)
-Selezionare default apppool
-Selezionare il link dal blocco di destra Visualizza Applicazioni
-Doppio Click sull'applicazione



-Tornare alla soluzionee verificare che eventuali referenze al web service realizzato, puntino
al web service pubblicato.

Esito
In questo modo si ha la certezza che il Web Service sia Eseguito in modalità 32 bit, dall' IIS,
va da se che deve essere compilato a 32 bit.

Se al termine di questo processo si ottiene ancora lo stesso errore possono essersi verificati due
problemi .
a) IIS non ha visto le modifiche (conviene riavviare tutto - macchina compresa)
b) non tutte le web reference sono state aggiornate.

Note conclusive
Questo problema esiste da quando esiste IIS a 64 bit, tuttavia non sono documentati i processi per ottimizzare il sistema a lavorare in piena compatibilità con le applicazioni a 32 bit.

-Potrebbe essere necessario aver installato il client di Oracle in program files (x86).
-Alcuni set up di Oracle client potrebbero non accettare spazie e parentesi nei nomi.

giovedì 22 luglio 2010

Foreach (xmlNode in xmlDocument.childNodes) non va...

Sembra strano .. ma c'e' una possbilità di Bug....

Fate questa prova createvi un xml che abbia quest'aspetto:
Coordinate
coordinata
x = 1
y= 1

coordinata
x = 2
y= 2

coordinata
x = 3
y= 3

coordinata
x = 4
y= 4


Poi provate a scrivere un banalissimo :
xmlDocument xd = new xmlDocument()
xd.Load([l'xml che avete ralizzato])

foreach (xmlNode xN in xd.childNodes)
{
console.writeline ( xN.InnerText);
}

che cosa succede ??

Potrebbe essere che anche a voi capiti di vedere sempre il primo nodo ??
si credo che possa succedere... ora fate questa prova ...

for (int i = 0; i < xd.childNodes.count; i++)
{
xmlNode xN = xd.childNodes[i];
console.writeline ( xN.InnerText);
}

acc.. succede ancora la stessa cosa ??

for (int i = 0; i < xd.childNodes.count; i++)
{
xmlNode xN = xd.childNodes[i].clone();
console.writeline ( xN.InnerText);
}

Tho' va che bello così funziona ??

Le supposizioni che si possono fare sono tante .. tuttavia solo con il clone il cursore sugli elementi si sposta.. caso contrario no...

Che sia un bug ??

mercoledì 21 luglio 2010

Ecco un bel esempio di come togliersi dai guai.

Quante volte ci è capitato di dover accedere ad un componente esterno (realizzato da altri) che non ha un interfaccia condivisa ho condivisibile... e quindi non poterlo sfruttare al meglio ??

A me capita spesso.... ma questo caso mi sono fatto più furbo che tecnico...

using System;
using System.Collections.Generic;

namespace Test
{
// interfaccia
public interface IWsDerived
{
string DerivedMethod(string text);
}

// costruttore
public class WsConcrete
{
// modello primario
public IWsDerived GetDeploy(int model)
{
if (model == 1) { return new coModelA(); }
if (model == 2) { return new coModelB(); }
}

// modello non in uso
public IWsDerived GetTest()
{

}
}

// componente "a" che non implementa l'interfaccia
public class ModelA
{
public string DoMoreAboutIt(string message)
{
return message + "| Done";
}
}

// componente "b" che non implementa l'interfaccia
public class ModelB
{
public string DoMoreText(string message)
{
return message + "| Text Done";
}
}

// wrapper (+ o -) per modello A
public class coModelA : IWsDerived
{
public string DerivedMethod(string Message)
{
ModelA a = new ModelA();

return a.DoMoreAboutIt(Message);
}
}

// wrapper (+ o -) per modello B
public class coModelB : IWsDerived
{
public string DerivedMethod(string Message)
{
ModelB b = new ModelB();

return b.DoMoreText(Message);
}
}
}


Model A e Model B immaginateli come blocchi esterni...
All'interno del mio processo diventano accessibili tramite l'interfaccia...

Scritto e compilato !!

martedì 6 luglio 2010

ToOdbcDataType

Bene .. bene bene... dato che non credo di essere l'unico a litigare con Odbc nel realizzare un breve data provider mi sono visto costretto a realizzare anche questo simpatico oggettino....

Diciamo che è la base o il punto di partenza per la conversione fra un generico DBType e un OdbcDataType...

partendo da questo link "http://msdn.microsoft.com/en-us/library/bbw6zyha%28VS.71%29.aspx" che mamma microsoft ha ben pensato di pubblicare...
ho realizzato questo "mini" data converter..

private OdbcType ToOdbcDataType(DbType Type)
{
switch (Type.ToString())
{
case "AnsiString": return OdbcType.VarChar;
case "Boolean": return OdbcType.Bit;
case "Byte": return OdbcType.TinyInt;
case "Binary": return OdbcType.VarBinary;
case "Date": return OdbcType.Date;
case "DateTime": return OdbcType.DateTime;
case "Decimal": return OdbcType.Numeric;
case "Double": return OdbcType.Double;
case "Single": return OdbcType.Real;
case "Guid": return OdbcType.UniqueIdentifier;
case "Int16": return OdbcType.SmallInt;
case "Int32": return OdbcType.Int;
case "Int64": return OdbcType.BigInt;
case "String": return OdbcType.VarChar;
case "Time": return OdbcType.Time;

}
return OdbcType.VarChar;
}

Ipoteticamente il Valore di ritorno di default andrebbe gestito meglio .. ma l'occasione fa l'uomo "furbo"...

giovedì 24 giugno 2010

Configuration 64 bit vs 32 bit

Ed è bene parlare anche di questo... ad oggi si presentano alcuni problemi nuovi, e più complessi da gestire.
Fra i tanti ( contro cui dobbiamo già combattere... ) si presenta anche
Realizzare un applicazione a 32 su un archittettura a 64 ...

Visual studio 2008 presenta nelle propietà della nostra soluzione nel pannello build il
platform target dove possiamo scegliere anyCPU, X86 , X64
Sia per le soluzione in Relase, sia per le soluzioni in Debug.

Tuttavia questo non sempre è sufficiente...

Se per esempio utilizzate un ODBC pensato per X86 su una soluzione X64 vi prenderete una bella eccezione
per differenti architetture...
Già in questo blog ho argomentato un problema simile per Oracle...
cmq prima di lanciare odb32 ... lanicate C:\Windows\SysWOW64\odbcad32.exe che è quello vero.

Ancor più complesso e comprendere che cosa succede nelle soluzioni Web, dove è necessario realizzare
un pool AdHoc per le soluzioni a 32 bit.
E credetemi confiugrare il nuovo ISS, se siete abituati al vecchio, è un po complesso, non difficile
ma complesso.
Tanto è che il più delle volte sarete costretti a pubblicare la vostra soluzione...

Se poi dovete realizzare il setup di quello che avete prodotto e vi serve un framework precedente...
ancora una volta dovrete litigare un po per scoprire che se nel vostro pacchetto appare l'inclusione
del frame work, dovrete fare doppi click sull'icona e dalla finiesrtra delle proprietà settare
l'effettiva versione.

Oppure tramite doppio click sul progetto di setup e da proprietà settare il terget...

venerdì 4 giugno 2010

Network is Up or Down

Una delle situazioni ricorrenti nella programmazione client server è la disponibilità della rete... Effettivamente ci sono tanti (troppi) modi per comprendere se l'accesso ad una lan è ancora disponibile.

Di fatto c'e' una possibilità semplice semplice che ci costringe a lavorare bene e a far poca fatica:

nel namespace :System.Net.NetworkInformation abbiamo una classe che fa al caso nostro; la
NetworkChange.

Questa consente di delagare un evento sullo stato di Change della rete...

NetworkChange.NetworkAvailabilityChanged += NetworkAvailabilityChanged;

il delegato potrebbe quindi avere quest'aspetto :

private void NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
if (e.IsAvailable)
{
// la rete è su
}
else
{
// la rete è giù
}
}

Ed ecco come 5/6 righe di codice rendono semplice semplice , l'arduo compito di comprendere
se il client può contattare il server..

mercoledì 12 maggio 2010

Casi di studio D.d.d

Vi siete mai posti la domanda, " ma perchè non raccogliere tutte le attività di un azienda in un unica soluzione ?"...

Io si ma effettivamente la risposta a questa domanda è frammentata in troppe porzioni, tuttavia c'e' una buona possibilità di poter comprendere quanto è necessario fare per poter dare una risposta significativa a questa ardua domanda.

Nel mio caso di studio (in sunto questo... ) ho raccolto alcune delle informazioni base per poter implementare una soluzione D.D.D.

La base comune è il domino ossia l'azienda XYZ, questa per vivere scambia materiale fra due paesi differenti, gestisce ordini e magazzino... e in tutto questo ci sono persone che vi lavorano.

E' possibile che persone del paese A debbano poter accedere a informazioni del paese B.

Appare chiaro che nei due paesi esistano due filiali differenti, con persone differenti...

In questo breve esempio si comprende quanti potenziali progetti potranno essere realizzati,
e quanti di questi potranno avere aspetti differenti.
Un ipotesi e che dal paese A, la risorsa 1 richieda di vedere il magazzino o la giacenza o un elemento specifico del paese B...

Da qui l'idea di realizzare due front-end diversi per il magazzino un Web, e uno desktop, in modo che possa essere condiviso, da cui non è difficile comprendere che la risorsa 1 acceda da web, ad un sito che espone i dati del magazzino del paese B.

Ora è quindi necessario che a livello di XYZ (l'azienda) esista una rappresentazione unica di tutte
le risorse, indipendentemente da dove queste siano dislocate.

Non dimentichiamoci che i paesi potrebbero essere di differente cultura, e che quindi gli utenti del paese A parlino una lingua diversa dal paese B.

Per Aggregazione sarà doveroso aggiungere all'immutable risorsa 1 anche la provenienza, o eventuali profili per cui accedere alla soluzione.

Il mio dubbio in tutto questo era la posizione fisica della fonte dati, ma partendo dall'univocità di un utente e da una corretta profilazione, si può supporre di accedere (facade) ai sotto sistemi
sfruttando solo le loro basi dati.

Filosofia forse... o frose no, solo un caso di studio.

martedì 23 marzo 2010

XML jquery 1.4 IE 8 vs FF 3.6

Un titolo che è tutto un programma... cercherò di fare luce nell'ombra che io stesso ho proiettato.

Problema: IE 8 usando jQuery traduce l'XML in un modo diverso da FireFox 3.6, eppure la cosa è strana....
e tanto...

Ho realizzato un Web Services che crea un semplice XML nulla di eccezionale, poi tramite una banalissima
chiamata in POST con jQuery eseguo una parserata del XML, per costruire una table.

Morale IE8 legge sulla 80 l'xml è FF 3.6 pure, per altro mi sono scaricato vari tool per leggere l'xml in
modo da comprendere qualche dettaglio in più rispetto a quanto prodotto dal WS. e per tutti la presentazione
è UGUALE...

Quindi è l'interprete di FF 3.6 che differisce da IE... ( o uno lo scarta o l'altro lo aggiunge )
Quindi mi trovo su FF un tag TextElement che contiene "\n"... e la relativa tabulazione...

Nasce naturale la domanda: chi crea questo tag ??? utilizzando un tool per leggere l'xml.. questo tag non c'e'
provando a fare una scansione dei nodi con c#... il tag non c'e' ...

Questo perchè (credo) IE C# il prodottino condividono la stessa base per leggere l'xml.... quindi rimane
il povero FF ...

E dato che non è semplice.. mettere in single line tutta una data table... mi sa tanto che mi converrà
aggiungere un controllino per quel che sto leggendo da jQuery.


Morale... quando qualcosa non va.. gli strumenti giusti (fire bug) servono a trovare gl'inghippi...

lunedì 22 marzo 2010

Pubblico il Web Services e non va ...

Create il vostro web services, lo pubblicate su un server remoto interrogate il vostro web services
Autorizzazione negata....
Cosa fare ..
Cambiate la dicitura nel web config...
customErrors mode="Off"
In questo modo riuscite a vedere l'errore vero ...


interrogate il vostro web services
Request format is unrecognized for URL unexpectedly ending in '/[webMethodName]'.

Se state lavorando con il fw 2.# è possibile che per pura rogna dobbiate esplicitare nel web.config q
ualcosa in più tipo:
[system.web]
[webServices]
[protocols]
[add name="HttpGet"/]
[add name="HttpPost"/]
[/protocols]
[/webServices]
[/system.web]

Va da se la considerazione del perchè un Web ( e ci tengo a precisare .. "WEB") services debba necessitare
l'esplicitazione dei verbi... GET E POST....

Tuttavia fatto questo il gli errori spariscono e tutto funziona...

giovedì 18 marzo 2010

Grammaticalmente parlando...

"avere troppe scelte coincide con il non avere scelte"....
Giuro non ricordo l'autore di questa frase ma gli do' merito a catinelle...

Credo che tutti noi abbiamo nel nostro piccolo una misera applicazione di test che contiene tutti i nostri esperimenti i nostri casi di studio.. i più bravi iniziano con dare anche dei nomi utili alle soluzioni di studio ( quelle da 5 min di prova ) il tempo passa si aggiungono form su form ... e poi ... MA QUESTA E' UN’ALTRA STORIA.

Che differenza c'e' ???
Double? myVar;
Double myVar = null;
Double myVar = Double.NaN;

Quasi nessuna .. forse se avessimo un allegro 386 vi consiglierei di usare la prima dicitura...

Ma perchè consentire 3 modalità differneti per scrivere la stessa cosa ??

Ingenuamente ce ne basta una "la seconda" in questo modo abbiamo la certezza che myVar sia Null...
Ingenuamente "la terza" ci da la certezza che myVar sia "il null per il double"...

Ma ancora una volta ritengo che l'eleganza e la leggibilità debba essere premiante rispetto al resto.

Ed è incredibile quanto un "null" possa cambiare le cose.. !!

martedì 16 marzo 2010

ConfigurationManager / ConfigurationSection

Il web.config è nostro amico ??
Molti credo troppi, non conoscono le vere potenzialità di questo elemento. La letteratura sull'argomento è però molto molto sostanziosa.

Ma per l'appunto il web.config è davvero un nostro amico ?? SI certo che si, basta saperlo sfruttare bene.

Parafrasando i tanti esempi che si possono trovare sul web si possono ottenere mille e milli utilizzi, mille scopi e mille interpretazioni. Nel mio caso come più volte è capitato lo scopo è di presentare una funzionalità e di lasciare al lettore
la possibilità di estendere quanto ha letto con la sua realtà.

Partiamo però da una necessità:
- per un web method di un web services ci serve una particolare configurazione.
- questa è riassumibile con gli attributi "ip,hostName";

va da se che è un perfetto esempio di livello 0.

Creiamo quindi una classe che abbia quest'aspetto...

public class MySpecialConfiguration: ConfigurationSection
{
[ConfigurationProperty("IP")]
public string IP
{
get { return (string)base["IP"]; }
}

[ConfigurationProperty("hostName")]
public string hostName
{
get { return (string)base["hostName"]; }
}
}

Questa classe Estende configurationSection e aggiunge due attributi (IP, hostName).. entrambi come string..

Ora nel web Method...

MySpecialConfiguration msc = ConfigurationManager.GetSection("MySpecialConfiguration") as MySpecialConfiguration;
this.IP = msc.IP;
this.hostName = msc.hostName

tramite il configurationManager.GetSection ([nome]) recuperiamo il nome della section che ci serve.

MySpecialConfiguration msc = (MySpecialConfiguration) ConfigurationManager.GetSection("MySpecialConfiguration");

E' la stessa cosa, scegliete voi come scriverla...

E ora ... la parte clue... il web.config... ( ricordatevi che [ /] che devono essere viste come maggiore e minore...)

[configuration]
[configSections]
[section name="MySqlParameters"
type=" thisSample.MySpecialConfiguration, thisSample" /]
[/configSections]

Che cosa diavolo è type ??? thisSample.MySpecialConfiguration è il nome della classe che abbiamo implementato prima
mentre thisSample è l'assembly

Questa è la dichiarazione della nostra section, ma ci serve anche

[MySqlParameters
IP="127.0.0.1"
hostName="nudrubufu"/]

al richiamo del web method verranno letti i parametri di configurazione...

A ben pensarci questo è proprio tutto quello che serve per aggiungere le proprie "specia conf" al web conf...

martedì 23 febbraio 2010

Web Services C# to Php Pages

Mi porto dietro quest'idea da parecchio tempo, e la teoria nasce dall'esigenza di realizzare una web application ad alta integrazione. Che sia "davvero dinamica" che possa davvero crescere, indipendentemente dalla logica, ovviando a
tutte le costrizioni che l'ambiente .Net rischia di creare.

Con costrizioni intendo "ricompilazione di tutto il sito", revisione dei puntamenti, implementazione di tanto codice per
l'ottimizzazione e tutto quello che ne è correlato.

In ambiente .Net realizzando una web application si ha come output un "sito" e pur pensando alla generallizzazione
delle primitive o dei web controls pur cercando di "disaccopiare" dati e logica, si otterà una soluzione dove per aggiungere
pagine si è costretti a ricompilare tutto.

Ho quindi pensato di Staccare il "sito" da chi fornisce i dati, e chi segue il mio blog, si sarà accorto che soprattutto negli
ultimi post ho fatto qualche breve analisi su quel che mi poteva servire...

Ho pensato ad una Business Logic realizzato da web services in c# e le web page realizzate in php.

Grazie a jQuery è possibile "con uno sforzo iniziale un po' più sostanzioso" ottenere dei risultati decisamente buoni.

Il concetto di base è semplice:
php web page in post chiamata ajax ottiene l'xml dal web services.

E' possibile che la pagina contenga oggetti php che sappiano tradurre l'xml in quanto serve realmente mostrare.

Va da se che oggi l'esecutore della chiamata è una pagina php, ma parlando per Astrazioni è possibile che l'inteprete
dell'xml possa essere qualsiasi altra "Entità".


Integrazione con il project management... In perfetto stile "Agile" sfruttando questa logica d'implementazione si
ha un altro grosso vantaggio. Ogni funzionalità è "FINALIZZATA" è può essere verificata / testa / accettata / chiusa
senza impatti sul resto della soluzione.

Volete saperne di più ? contattatemi via mail.

lunedì 22 febbraio 2010

Div posizioni assolute e relative.....

Comprendo (div)entar matti, ma esagerare non fa bene alla salute.

Questo mini tutorial vi spiega come funzionano i vari allineamenti sfruttando
css Position.

Partiamo da una cosa semplice

Per ottenere questa pagina vi basta realizzare 4 div
1) {width:100%;height:100%}
2) {margin:0 auto;width:960px;background-color:yellow;}
3) {width:100px;height:100px;background-color:red;}
4) {width:100px;height:100px;background-color:lime;}

1) contiene 2)
2) contiene 3) e 4)

così:



Spostiamo il blocco verde

1) {width:100%;height:100%}
2) {margin:0 auto;width:960px;background-color:yellow;}
3) {width:100px;height:100px;background-color:red;float:left}
4) {width:100px;height:100px;background-color:lime;}



Spostiamo il Rosso al posto del verde
1) {width:100%;height:100%}
2) {margin:0 auto;width:960px;background-color:yellow;}
3) {width:100px;height:100px;background-color:red;float:right}
4) {width:100px;height:100px;background-color:lime;}

Rosso e verde uno sull'altro ..
1) {width:100%;height:100%}
2) {margin:0 auto;width:960px;background-color:yellow;}
3) {position:absolute;left:50;width:100px;height:100px;background-color:red;}
4) {width:100px;height:100px;background-color:lime;}

Rosso e verde uno sull'altro, ma in un altro modo.
1) {width:100%;height:100%}
2) {position:relative;margin:0 auto;width:960px;background-color:yellow}
3) {position:absolute;width:100px;height:100px;background-color:red;}
4) {position:absolute;top:10;width:100px;height:100px;background-color:lime;}


Non c'e' troppa chiarezza, quanto meno non è sempre specificato dove Mettere il RELATIVE. Ho fatto parecchie prove prima
di questo mini tutorial per comprendere quanto le posizioni siano "evanescenti".

Specificando nel div contenitore 2) il relative implica che tutti i div ( conenuti e con stile absolute ) faranno riferimento
alla posizione 0,0 (angolo in alto a sinitra).

Tutto quello che contine d4 non subirà variazioni, ma seguira la posizione in cui questo si trova.

giovedì 18 febbraio 2010

Decorator

Fra tanti pattern utili il decorator troneggia, anche se potrebbe non esser sempre chiaro lo scopo, o dove come quando applicarlo.

Tecnicamente parlando il decorator non fa altro che "estendere una base" e aggiungere "qualcosa".

Nel caso in esempio, esiste una classe Generic Item che è descritta da due proprietà e due metodi.
Le proprietà appartengono a Generic Item
I metodi sono virtuali (ossia) esiste la firma e volendo il codice del metodo, ma potrà essere esteso.

La classe book, estende Generic Item aggiunge due proprietà ed esegue l'override dei due metodi.
La classe brochure , estende Generic Item aggiunge una proprietà ed esegue l'override dei due metodi.

E' pur logico che il modo di Editare un libro sia diverso dal modo di Editare una brochure e tanto vale anche
per il modo in cui esse verranno visualizzati.

La classe astratta decorator ha un compito Creare Oggetti che abbiano come BASE Generic Item.

La classe coDecorator effettuerà il management di ciò che il decorator ha creato.

Ciò Detto ..
E' facile intuire quanto sia semplice individuare i casi d'uso, il rischio e che offuscati dalla voglia di utilizzare questo
pattern lo si "infili" un po da tutte le parti.

Un caso comune potrebbe essere la necessità di aggregare ad una classe Entity le sue derivate.

Utente e Utente Esteso
la classe utente (la nostra Generic Item ) contiene Nome Cognome, Login, Password
la classe utente Esteso (la nostra brochure o book) contiene in più Numero matricola, interno telefonico, sede di lavoro...

I dati della prima classe risiedono sulla tabelle Utenti mentre i dati di utente esteso potrebbero provenire da Ldap.

Per cui in Decorator creeremo UtenteEsteso e in coDecorator Popoleremo i dati.

WHERE 1=1

Per meglio dire quando barare via SQL.

Supponiamo che per un motivo qualsiasi abbiate realizzato un QueryHelpers che fra i suo metodi presenti AddContidion
e (sempre basandoci su una supposizione) la firma sia simle a questa:

public static string AddCondition(string OriginlQuery, string fieldName, string fieldValue, string Caluse)

Supponiamo (sempre) che il metodo verifichi prima che la sintassi sia corretta, che poi aggiunga la condizione
costruita più o meno così Caluse fieldName = fieldValue
dove Caluse può essere AND OR

Questo metodo potrebbe andare più o meno sempre... e per altro Va, ma davanti a query un po più complesse, si presentano
i primi problemi..
se per esempio ci si imbatte in query tipo :
select * from ( select * from blabla union select * from blabla ) as myBlabla

il rischio è che si debba valutare l'apertura di parentesi la chiusura e il breve parser fatto con affetto va a ramengo.

Torna comodo sapere ANCHE SE E' UN TRUCCO VECCHIO e stra VECCHIO, che ogni tanto si può far credere ai
vari "db engine" che stanno facendo qualcosa di utilmente inutile.

SELECT * FROM utenti WHERE 1=1 equivale per esempio a SELECT * FROM utenti.

Certo è vero equivalgono per risultati .. ma in tempi macchina ?? o cicli che dir si voglia ??

Su estrazioni piccole la differenza è minima (1000 rec), su estrazioni grosse(100000 rec) il peso si inizia a notare,
ma la cosa varia da db engine a db engine.

martedì 16 febbraio 2010

Per non perdere il focal point....

In troppi casi, nel nostro lavoro, rischiamo di perdere concentrazione o di allontanarci dalla soluzione. Le cause ? generalmente quisquiglie, rumore, pressione...

Il risultato... classi che fanno la stessa cosa, uno sproposito di righe di codici perfettamente inutili che appesantiscono notevolmente le prestazioni del prodotto finale.

E' possibile ovviare a questo problema ? di metodi ce ne sono tanti e ogniuno ha il suo, e per ogniuno può essere più o meno complesso.

Io mi sono fatto un schema, sul design pattern che più volte mi è tornato utile e più che altro mi serve per ragionare e soffermarmi su quello che sto facendo.

Intent
che cosa fa / o che cosa deve fare ?

Also konw as well
ho già fatto altro o esiste qualcosa di simile ?

Motivation / scenario
Uno scenario / disegno che illustra il problema e come gli oggetti lo risolvano

Applicability
In quali situazioni è applicabile il Design pattern ? Esistono esempi ? Come riconoscere le situazioni.

Structure
una rappresentazione grafica basata sulla notazione Object modelling

Partecipants
Le classi e gli oggetti che partecipano al pattern e le loro responsabilità.

Collaboration
Come le classi e gli oggetti collaborano per conncorrere alle loro responsabilità.

Consequences
Che cosa comporta l utilizzo del pattern in questione, quali sono i limiti e le sue dipendenze.

Implementation
Quali sono i limiti imposti nell'implementazione ?

Sample Code
una porzione di codice che chiarisca come implementare il codice nel linguaggio.

Konow Use
un esempio nel mondo reale del pattern in oggetto

Related Patterns
Ci sono altri pattern che definisco la soluzione? E possibile che questo debba o possa interagire con altri pattern?


Ora non ricordo se questa è una rielaborazione di un qualcosa che avevo trovato sul web tempo fa, ma l'adotto e ritengo che sia parecchio parecchio utile.

lunedì 15 febbraio 2010

Test-Driven ? Yes...

Come per tutte i concetti o paradigmi o metodologie, nell'ambito informatico tutto è astratto e tutto può essere applicato a diversi concetti.

Affrontando soluzioni in D.D.D, emerge subito il concetto di Test-Driven, ma è contestualizzabile solo per il D.D.D. ? Secondo me il concetto è decisamente astratto...

Parafrasando l'esempio banale la realizzazione di una calcolatrice, sia l'analista che il programmatore, daranno per scontato che al primo avvio non si verifichino problemi, poi basta premere una lettera al posto di un numero... e puf ci becchiamo un eccezione...

Questo perchè ? Semplice Troppe volte ci si dimentica di testare tutto il nostro codice... no.
Questo avviene perchè non è chiaro lo scopo di quello che stiamo facendo.

E gli elementi "di base" che il più delle volte diamo per scontati non sono stati presi in considerazione.

Il paradigma del T-D è che si deve testare una funzionalità per volta e che il test deve essere ripetibile e che il test deve essere positivo prima di aver scritto il codice...

Ma davvero ?? Si Tornando alla nostra calcolatrice, se la pressione del bottone 1 deve scatenare
la scrittura del testo 1 nella casella di testo e popolare un buffer, già sappiamo che cosa non dovrà fare il bottone 1...
Interpretando quindi il T-D è utile sapere e implementare il codice affinche la pressione del bottone 1 corrisponda solo a quell'azione...

Detto così forse è anche troppo semplice, ma il concetto di base è questo.

venerdì 12 febbraio 2010

Server-Client-Server

Quando programmare diventa quasi un paradosso...

Siamo sempre nell'ambito web, e in questi giorni mi sto divertendo un sacco con Ajax, devo dire che bisogna pestarci un
po la testa.. ma il risultato può essere sorprendente.

Diciamo pure che questo è l'ennesima evoluzione del controllo AutoCompleteExtender.... di cui ho scritto qualcosa
qualche giorno fa.. (se tutto va bene l'altro ieri).

Il concetto di aiutare l'utente nella composizione di un dato, è cosa buona, è più complessa quando quanto dobbiamo
prelevare è il frutto di una query fatta su una vista con 20 colonne... e strano a dirsi è necessario mostrare tutti i 20
attributi, in un form.

Va da se che se mostro nella lista del AutoCompleteExtender tutti i 20 campi.. anche l'utente più sgamato si ritrova
nel pieno dell'incomprensibilità di ciò che sta leggendo.

Quindi torna utile pubblicare nella lista del AutoCompleteExtender solo lo stretto necessario ... e riportarlo al lato
Client.

Qui mi è sorto un dubbio .. e da qui come ritorno sul server ?? magia ?? Prestidirigilibizzazione ?? no...

patiamo dalla form e da ciò che contiene:

asp:ToolkitScriptManager ID="tsMain" runat="server" EnablePageMethods="True"
/asp:ToolkitScriptManager

asp:TextBox ID="txtrSearch" runat="server" Text=""/asp:TextBox

asp:AutoCompleteExtender ID="AutoCompleteExtenderDemo" runat="server"
TargetControlID="txtrSearch" ServiceMethod="GetCompletionList"
MinimumPrefixLength="1" CompletionInterval="1000"
EnableCaching="true" CompletionSetCount="10" OnClientItemSelected="ItemForSplit"
/asp:AutoCompleteExtender

asp:TextBox ID="txtDemo" runat="server" /asp:TextBox

In più ci servono un paio di cosette nel codeBheind...

[System.Web.Services.WebMethod]
public static string DoMore(string valueText)
{
return "Simple Test";
}

Rimane valida la GetCompletionList ( documentata qualche post fa.. )

[System.Web.Services.WebMethod]
public static string[] GetCompletionList(String prefixText, int count)
{
List suggetions = GetData(prefixText, count);
return suggetions.ToArray();
}

private static List GetSuggestions(string key, int count)
{
List suggestions = new List();

for (int i = 0; i < count; i++)
{
// la vostra logica di estrazione...
}
return suggestions;
}

E nella pagina .. ci servono queste due function ( nel aspx )

function ItemForSplit(source, eventArgs)
{
Value = eventArgs.get_value();
var rs = document.getElementById('txtrSearch');
var dm = document.getElementById('txtDemo');
rs.value = "";
PageMethods.DoMore(Value,OnCallComplete,null,dm);
}

function OnCallComplete(result,txtOutput,methodName)
{
txtOutput.value = result;
}

Questi scrip devono essere MESSI NEL BODY .. e non nella header .. se non no va... by Design...

Che cosa ottenete ???

che il Client richiede al Server i dati la prima volta con GetCompletionList, li usa e richiede per la seconda volta
i nuovi dati al Server...

Funziona che è una meraviglia..

giovedì 11 febbraio 2010

Virtual (mente) parlando...

Consentire l'estensibilità/ereditarietà è sempre "Un bene" ?

Parafrasando l'idea di base "reduce-to-reuse" sono entrambe (estensibilità ed ereditarietà) "Un bene", perchè questo ci consente di scrivere meno codice, di usarlo meglio, di finalizzare lo scopo di una o più classi.

Tuttavia è necessario uno sforzo concreto in fase d'analisi.
Supponiamo che per applicativo venga richiesto di realizzare anagrafiche, attività è qualche funzionalità generica,
da rilasciare nella "Fase 1". Supponiamo quindi che per lo stesso applicativo sia prevista una "Fase 2" dove ogni azione
eseguita sulla anagrafica (insert/update/delete) debba essere scritta in un log.

In un primo momento al Team viene richiesto di implementare solo la "fase 1" e non venga presentata la "fase 2" .

Il team crea una serie di form per l'anagrafica con le azioni richieste che risulteranno non troppo diverse da queste..

protected void btnInsert()
{
// logiche di verifica dei campi...

// Altre logiche di verifica (doppi e/o simili)...

// codice per insert...
}

Dopo una serie di test il Team consegna l'applicativo... e solo dopo la consolidazione di quanto fatto, o l'accettazione
viene proposta al Team la "fase 2"...

In questo preciso caso eritidare i form implica una quasi completa revisione del codice già scritto ... con una conseguente
e onerosa perdita di tempo...

Se si facesse un passo in più... in fase d'analisi comunicando le due Fasi e comunicando che la seconda non è necessaria
subito...

il team potrebbe produrre per la "fase 1" qualcosa di simile a :

protected virtual void btnInsert(object sender, EventArgs e)
{
// logiche di verifica dei campi...

// Altre logiche di verifica (doppi e/o simili)...

// codice per insert...
}

e ( a fronte dell'aver ereditato la base .. )

protected override void btnInsert_Click(object sender, EventArgs e)
{
base.btnInsert_Click(sender, e);

// codice per Log
}

A fronte di un obbiettivo ben definito è possibile prendere delle decisioni che rendano più semplice il
raggiugimento dello stesso, ed in questo caso "semplice" implica anche Meno costoso...

mercoledì 10 febbraio 2010

OnRowDataBound

Le girdview hanno qualche piccola potenzialità nascosta.

Questo evento si scatena subito dopo aver invocato il DataBind() che ricordiamo essere l'associazione fra i dati e la gridview.
L'intercettazione dell' OnRowDataBound consente parecchie attività sfiziose.

L'evento è un GridViewRowEventHandler e i GridViewRowEventArgs sono davvero tanti.


System.Data.DataRowView dataValue = (System.Data.DataRowView)e.Row.DataItem;

if (dataValue != null) // nel caso dell'intestazione è NULL
{
if (dataValue.Row.ItemArray[_VerifyDeleted].ToString().ToLower() == "true")
{
e.Row.CssClass = "rL";
}
}

In questo caso specifico cambio lo stile di una Row se trovo un valore in un campo della dataTable ...

dataValue
(vista o presentazione dei dati) la riga non è ancora stata renderizzata.
dataValue.Row.ItemArray
(dati fisico o record rappresentato come Item Array]
e.Row
(riga che sta per essere renderizzata)

Il mio consiglio è di usare una gridview gestita a pagine ... con un quantitativo di righe più o meno basso.
Nell ordine dei 25 / 30 elementi, questo perchè OnRowDataBound è generato per ogni riga..
va da se che fare IO su 20 elementi ha un costo .. su 100 costa un bel po di più.

In questo evento ha delle buone potenzialità, è in qui che si possono aggiungere controlli e handler sui
controlli (tipo Button + Click, CheckBox ... e via e via) il limite rimane la creatività ed eventuali tempi di Render

martedì 9 febbraio 2010

Consentitemi ...

Clamore e stupore... ancora una volta chi opera le traduzioni merita un PLAUSO...

errore di runtime ASP.NET:
Solo i controlli del contenuto sono consentiti
direttamente in una pagina di contenuto che contiene
controlli del contenuto.

Come mi è capitato ???
Ho aggiunto uno script ad una MASTER PAGE FILE contenente uno script manager..
probabilmente ho messo lo script nel punto "sbagliato"... ma si può dare un messaggio così??

script language ="javascript"

/script
asp:Content ID="cpPrototype" ContentPlaceHolderID="cpHolder" runat="server"
(errore )

asp:Content ID="cpPrototype" ContentPlaceHolderID="cpHolder" runat="server"
script language ="javascript"

/script

tutto ok... mamma mia...

AutoCompleteExtender...

Sembra facile ma non è difficile...
Il nuovo ajax tool kit sembra semplice da usare ma non è sempre chiaro ne l'esempio di Ms, ne l'esito che otteniamo.

Decisamente inatteso è il comportamento dell' AutoComplete, ci sono esempi sparpagliati pseudo casuali, e come sempre
alcuni vanno.. altri no. E' possibile che chi scrive "si fa così" o "vi basta fare così" ( lo faccio anch'io... eh eh ) non documenti
l'ambiente in cui è stato fatto il test.

AjaxControlToolkit ver: 3.0.31106.0
System.Web:2.0.0.0
System.Web.Extensions:3.5.0.0

La vera domanda è perchè tutta sta zozzeria non è allineata ??? c'e' da chiederlo all'installer o a come dover
roccambolescamente installarsi tutto a mano... "fare una cosa fatta bene no ?"

cmq

1) createvi una nuova Web Solution ( non ho provato con il sito web)
2) assicuratevi di mettere...
Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp"
3) nel form..
form id="form1" runat="server"
asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server"
/asp:ToolkitScriptManager

asp:TextBox ID="txtInOrderToComplete" runat="server" Text="" /asp:TextBox
asp:AutoCompleteExtender ID="AutoCompleteExtenderDemo" runat="server"
TargetControlID="txtInOrderToComplete" ServiceMethod="GetCompletionList"
MinimumPrefixLength="1" CompletionInterval="1000"
EnableCaching="true" CompletionSetCount="10"
/asp:AutoCompleteExtender
/form

Nota come al soliti il maggiore e il minore sto blog me li cassa !!

4)nel CodeBehind

[System.Web.Services.WebMethod]
public static string[] GetCompletionList(String prefixText, int count)
{
List suggetions = GetData(prefixText, count);
return suggetions.ToArray();
}


private static List GetSuggestions(string key, int count)
{
List suggestions = new List();

for (int i = 0; i < count; i++)
{
// la vostra logica di estrazione...
}
return suggestions;
}

Tecnicamente parlando non abbiamo implementato un web service per ottenere i dati .. chiaro che la logica
la si può spostare un po dove si vuole .. un po dove più fa comodo...

Ragioniamoci un po sopra...

Perchè statico ???
Perchè se non è statico non funziona.. certo come risposta fa un po acqua, ma sono convinto che la chiamata di un
web services ( o web method ) debba essere statica.

Perchè string[] e non List ???
Perchè il bind sulla lista ( o pseudo lista) che viene creata come output necessita una array di string[]

Perchè (String prefixText, int count) nella firma ???
Perchè la firma proviene dall'oggetto AutoCompleteExtender, e se non corrisponde... non va.

Posso cambiare ServiceMethod="GetCompletionList" ???
ServiceMethod="GetCompletionList" deve corrispondere al
public static string[] GetCompletionList(String prefixText, int count)

Bene questo è il quanto ..

Per altro non è necessario un update panel, ma funziona anche con lui.

buon divertimento.

giovedì 4 febbraio 2010

C:\WINDOWS\system32\ctfmon.exe "Ma che cosa è?"

Parliamo giusto per qualche attimo ... che non fa male.

Qusto eseguibile E' DI MICROSOFT, ma ha un gemello detto anche il virus della "pen Drive" o "Chiavetta".
Più o meno l'abbiamo contratto tutti o se l'abbiamo non lo sappiamo.
Prendete la vostra penna usb infilatela nella vostra presa usb... e sbirciate se vi trovate un autorun.inf....
L'avete trovato, bene ... non troppo, ma bene.. ora apritelo con il NOTEPAD, non eseguitelo, editatelo,
e dentro troverte una zozzeria simile a

[autorun]
shellexecute=Recycled\ctfmon.exe
shell\Open(&0)\command=Recycled\ctfmon.exe
shell=Open(&0)

Come noterete .. "ctfmon.exe" è molto uguale a "ctfmon.exe" ma non sono la stessa cosa...
Il primo è un brillante Trojan il secondo è .. (suspance) un componente aggiuntivo di Office 2003
perfettamente INUTILE ai più.

C:\WINDOWS\system32\ctfmon.exe Questo ragazzaccio serve per l'input alternativo, che può essere
Riconoscimento Scrittura..
Sintesi e riconoscimento vocale..

Come sostengo può essere importante per chi ha dei deficit, ma dato che ms l'installa di default
supponendo che siamo tutti possibili portatori di Deficit... tuttavia non ti dice che lo fa....

La situazione finale è tristemente la seguente...
apri word ne parte uno ( che rimane appeso )
apri excel ne parte un'altro .. ( che strano ma vero rimane appeso )
apri outlook e caso strano (perchè usare uno di quelli appesi ) te ne apre un'altro che fa sempre bene...

Morale se aprite in un giorno 20 documenti vi trovate 20 ctfmon.exe...

Potete fare una cosa semplici crearvi una Restriction POLICY per evitare che parta... m alla fine vi ritrovate
l'event viewer pieno di zozzerie..

Oppure rimuoverlo
per il virus .. la cosa è semplice.. formattate la chiavetta.. e svuotate il cestino del pc e puf non ha persitenza...
Più che altro c'e' da tirarlo giù via Taskmanager...

per l'applicazione ms .... ci vuole un po di più, ma è semplice..
1) teskmanager e tirate giù tutti ctfmon ..
2) pannello di controllo / installazione applicazioni / Microsoft Office 2003
3) Scegliere "Cambia"
4) Aggiungi/Rimuovi caratteristiche
5) Fleggare Personalizzazione avanzata applicazioni
6) da Personalizzazione avanzata Espandere Microsoft Office

a questo punto cercate tutto quello che è inerente alla sintesi per il riconoscimento vocale o per gli input alternativi...
In più vi conviene rimuovere... il traduttore dal francese, dallo spagnolo, dal tedesco e tutte le altre lingue di cui
non avete bisogno ..


7) esegui / regedit
8) rimuovere da HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run\ Ctfmon.exe
9) esegui / regsvr32 /u msctf.dll

Fatto abbiamo finito ...