venerdì 13 agosto 2010 #

Collection e predicati cercare elementi senza cicli

Una delle cose che risultano essere più utili, quando si lavora con le collezioni, sono i metodi di Ricerca, che ci permettono di trovare un elemento della collezione utilizzando come criterio una o più delle sue proprietà. Questi metodi, possono essere creati sia come Indexer, ovvero le property che ci permettono di trovare un elemento passando il valore di una o più proprietà dello stesso, oppure come metodi specifici. Questa seconda opzione, si utilizza perché spesso ci possono essere più property dello stesso tipo che possono essere usate per la ricerca, pertanto non è possibile creare degli indexer per tutte loro. Lavoriamo su un esempio:

La classe Entity, da noi definita che chiameremo AnagraficaAzienda, espone le seguenti proprietà:

string DescrizioneRicerca
string RagioneSociale
string PartitaIva
string Indirizzo
string CAP
string Citta
string PV
int IDTipo
int IDAnagrafica

Per creare un Indexer per cercare dati usando la Descrizione di Ricerca possiamo usare questo codice:

public AnagraficaAzienda this[string pDescrizioneRocerca]
{
    get
    {
        return( Find(item=>item.DescrizioneRicerca==pDescrizioneRicerca);
    }
}

per creare un metodo che ritorna una lista dei dati in cui la ragione sociale soddisfa una regular expression:

public List<AnagraficaAzienda> FindWithRegEx( string pRegEx )
{
    return (FindAll(items => Regex.Match(items.RagioneSociale, pRegEx).Success));
}

per creare un metodo che ritorna una anagrafica con una determinata partita IVA:

public AnagraficaAzienda FindXPartitaIva(string pPartitaIva)
{
    return( Find(item=>item.PartitaIva==pPartitaIva);
}

Per creare un indexer multi dimensionale possiamo usare il seguente codice

public AnagraficaAzienda this[int pIDZona, int pIDAnagrafica]
{
    get
    {
        return( Find(item=>item.IDZona==pIDZona && item.IDAnagrafica==pIDAnagrafica);
    }
}

Come possiamo vedere, un predicato ci permette di indicare una qualsiasi espressione binaria quale criterio di ricerca.

posted @ venerdì 13 agosto 2010 17.02 | Feedback (0)

martedì 10 agosto 2010 #

Come generare un Guid.Empty in SQL

Venerdì scorso, il mio collega Luca stava litigando con un problema determinato da valori null, DbNull e Guid in una classe che si occupa di  effettuare query su Database.

La classe Guid, è una classe di tipo Value Type, pertanto non è nullabile. Per contro, nelle Stored Procedure, io utilizzo il valore DbNull come indicatore di Default per indicare che un parametro di filtro è vuoto e deve quindi essere ignorato.

Modificare parecchie classi C# per trasformare il Guid in un Nullable Guid diveniva complicato e non facile da gestire e aggiornare, pertanto abbiamo provato a ragionare ed è risultato che se avessimo potuto usare un Guid.Empty nelle nostre stored procedure (o nelle query ad hoc), avremmo risolto il problema senza complicate manovre.

Facendo varie prove siamo stati in grado di produrre la seguente Scalar Function:

CREATE FUNCTION Guid_Empty()
RETURNS UniqueIdentifier
AS
BEGIN
RETURN cast(cast(0 as binary) as uniqueidentifier)
END
GO

Dopo aver generato la funzione qui sopra ed una semplice tabella nel mio famigerato database di test, sono stata in grado di scrivere la query seguente:

SELECT [ID]
      ,[Descrizione]
      ,[Prezzo]
      ,[Quantità]
      ,[Umi]
      ,IsNull(AltGuid, dbo.Guid_EMPTY()) AS Alt
  FROM [paperinik].[dbo].[TbRighe]

Che produce la seguente tabella risultante:

guidquery

Posso inoltre usare la mia funzione per creare la query seguente:

SELECT [ID]
      ,[Descrizione]
      ,[Prezzo]
      ,[Quantità]
      ,[Umi]
      ,IsNull(AltGuid, dbo.Guid_EMPTY()) AS Alt
  FROM [paperinik].[dbo].[TbRighe]
WHERE
IsNull(AltGuid, dbo.Guid_EMPTY()) = dbo.Guid_EMPTY()

Che mi permette di filtrare le righe con Guid.Empty oppure posso usare l’operatore <> per cercare le sole righe con un Guid non vuoto. Usando questo tipo di Query, posso tranquillamente passare come parametro il Guid.Empty di C# come filtro senza la necessità di creare un Guid nullable o implementare complicate espressioni di controllo nelle mie classi.

Technorati Tag: ,,

posted @ martedì 10 agosto 2010 23.05 | Feedback (0)

lunedì 9 agosto 2010 #

Accedere alle righe cancellate di una Datatable

Oggi sono in modalità “inventario” sul codice delle mie librerie standard, ovvero sto passando al framework 4.0 e facendo ciò mi dedico alle “pulizie di primavera” eliminando cose ormai obsolete, usando funzionalità un po’ più evolute su vecchie funzioni, e già che ci sono, apportando al mio generatore di codice che dal 2006 produce automaticamente le classi per la manipolazione dei dati che utilizzo con i miei database alcuni aggiornamenti. Uno di questi è l’aggiunta di alcuni eventi che saranno scatenati dalle classi quando vengono effettuate le funzioni di aggiornamento. Due di questi sono l’evento RowAdded e l’evento RowDeleted, in entrambi i casi, se decidiamo di gestire l’evento scatenato, potrebbe esserci utile avere delle informazioni sui dati aggiunti o cancellati. Per questo motivo, ho deciso di inserire negli argomenti dell’evento, l’intera DataRow aggiunta o cancellata.

Passare una DataRow appena aggiunta ad una DataTable  è semplice, mentre invece passare una DataRow cancellata non è proprio una cosa immediata. Per capire come fare ho creato un progettino di test, il cui codice riporto in questo post con qualche commento.

La form di esempio è la seguente:

righecancellate

Sulla form ci sono 3 button – Carica, Test Aggiunta, Test cancellazione. c’è anche una textbox con l’opzione Multiline a True e la scrollbar verticale visibile.

Di seguito, vediamo il codice dietro alla form:

        DataTable mDt;
        private const string FLD_ID = "ID";
        private const string FLD_Description = "Description";
        private const string FLD_Date = "Date";
        private const string FLD_Price = "Price";

        public Form1()
        {
            InitializeComponent();
            mDt = new DataTable("Tbtest");
            DataColumn col = new DataColumn(FLD_ID, typeof(int));
            col.AutoIncrement = true;
            col.AutoIncrementSeed = 1;
            col.AutoIncrementStep = 1;
            mDt.Columns.Add(col);
            col = new DataColumn(FLD_Description, typeof(string));
            mDt.Columns.Add(col);
            col = new DataColumn(FLD_Date, typeof(DateTime));
            mDt.Columns.Add(col);
            col = new DataColumn(FLD_Price, typeof(Decimal));
            mDt.Columns.Add(col);
        }

Nel costruttore, Genero la mia datatable e vi aggiungo le colonne, per comodità i nomi delle colonne sono delle costanti.

        private void btnCarica_Click(object sender, EventArgs e)
        {
            mDt.Rows.Clear();

            DataRow row = mDt.NewRow();
            row[FLD_Description] = "Quaderno";
            row[FLD_Date] = DateTime.Now;
            row[FLD_Price] = 1.5;
            mDt.Rows.Add(row);

            row = mDt.NewRow();
            row[FLD_Description] = "Penna";
            row[FLD_Date] = DateTime.Now;
            row[FLD_Price] = 2.8;
            mDt.Rows.Add(row);

            row = mDt.NewRow();
            row[FLD_Description] = "Gomma";
            row[FLD_Date] = DateTime.Now;
            row[FLD_Price] = 0.7;
            mDt.Rows.Add(row);

            row = mDt.NewRow();
            row[FLD_Description] = "Matita";
            row[FLD_Date] = DateTime.Now;
            row[FLD_Price] = 1.1;
            mDt.Rows.Add(row);

            row = mDt.NewRow();
            row[FLD_Description] = "Block notes";
            row[FLD_Date] = DateTime.Now;
            row[FLD_Price] = 2.8;
            mDt.Rows.Add(row);

            row = mDt.NewRow();
            row[FLD_Description] = "Pennarelli 12 colori";
            row[FLD_Date] = DateTime.Now;
            row[FLD_Price] = 6.7;
            mDt.Rows.Add(row);

            mDt.AcceptChanges();

            Mostra();
        }

Il tasto Carica, aggiunge alcune righe alla DataTable e chiama la funzione Mostra che ne visualizza il contenuto sulla textbox.

        private void Mostra()
        {
            this.txtResult.Text = string.Empty;
            for( int i=0; i< mDt.Rows.Count;i++)
            {
                MostraRiga(mDt.Rows[i]);
            }
    
        }

        private void MostraRiga(DataRow pRow)
        {
            this.txtResult.Text += "ID: " + pRow[FLD_ID].ToString() + Environment.NewLine;
            this.txtResult.Text += "Description: " + pRow[FLD_Description].ToString() + Environment.NewLine;
            this.txtResult.Text += "Date: " + pRow[FLD_Date].ToString() + Environment.NewLine;
            this.txtResult.Text += "Price: " + pRow[FLD_Price].ToString() + Environment.NewLine;
            this.txtResult.Text += Environment.NewLine;
        }

Il metodo Mostra utilizza MostraRiga per scrivere il contenuto delle righe sulla textbox.

        private void btnAdd_Click(object sender, EventArgs e)
        {

            DataRow row = mDt.NewRow();
            mDt.Rows.Add(row);
            row[FLD_Description] = string.Format("Nuova riga {0}", row[FLD_ID]);
            row[FLD_Date] = DateTime.Now;
            decimal pippo = Convert.ToDecimal(row[FLD_ID]);
            row[FLD_Price] = pippo*0.98m;

            mDt.AcceptChanges();

            MostraRiga(row);
        }

Il bottone Aggiungi riga aggiunge una riga e passa la stessa a MostraRiga per la visualizzazione.

        private void btnDelete_Click(object sender, EventArgs e)
        {
            DataRow row = mDt.Rows[0];
            int id = Convert.ToInt32(row[FLD_ID]);
            row.Delete();
            string filter = string.Format("{0} = {1}", FLD_ID, id);
            DataView vi = new DataView(mDt, filter,"", DataViewRowState.Deleted);
            DataTable tt = vi.ToTable();
            mDt.AcceptChanges();
            MostraRiga(tt.Rows[0]);
        }

Ed infine, il pezzetto di codice più interessante, il codice che ci permette di mostrare le informazioni relative alla riga cancellata.
Come potete vedere, il metodo cancella semplicemente la prima riga della DataTable salvandosi il suo ID.
Dopo la cancellazione, per poter leggere l’intera riga, creo una DataView che legge la riga appena cancellata e utilizza poi il metodo ToTable della DataView per mettere i dati in una nuova Tabella. Infine, passo la DataRow della nuova tabella alla funzione MostraRiga e posso vederne il contenuto nella mia textbox.

Il passaggio per ToTable è molto importante, infatti, se provassimo ad usare invece il codice seguente:

MostraRiga(vi[0].Row);

Che è perfettamente legale con una riga non cancellata, otterremo invece una Exception, perché non è permesso accedere direttamente ai dati di una riga cancellata.

posted @ lunedì 9 agosto 2010 17.37 | Feedback (0)

domenica 1 agosto 2010 #

Le ovvietà che tagliano le gambe ai beginner

Non sempre chi fa il nostro mestiere può seguire tutte le nuove tecnologie dai loro primi passi, ad esempio io ho lavorato pochissimo con ASP.Net, anche se conosco molto bene HTML e CSS, pertanto ci sono cose che chi ha fatto un sito con ASP.Net 2.0 appena uscita la tecnologia conosce e considera ovvie, ma che per un beginner non lo sono, soprattutto se il beginner, come me è un professionista che lavora con altre tecnologie che conosce perfettamente e quindi, pur iniziando un tutorial a livello zero, non ha una macchina da studente, ma una macchina configurata per creare applicazioni che girano nel mondo reale.

Questo non vuole essere un rimprovero a tutti quelli che hanno costruito i tutorial, perché é perfettamente logico che questi tutorial siano predisposti per fare in modo che chi vuole avvicinarsi ad una tecnologia possa farlo senza dover avere una macchina da professionista. Ma vuol essere invece un invito a chi si trova nella situazione di trovarsi davanti ad un muro a non perdere coraggio,perché la soluzione non è mai troppo lontana e quindi, F1, BING, Google e chi più ne ha più ne metta.

Vediamo qual’è l’esempio che mi porta a scrivere questo post:

Stamattina (domenica mattina) per tutta una serie di motivi per cui non vi tedio, ho deciso di iniziare a guardarmi ASP.Net MVC, una tecnologia che reputo interessante per una serie di cose che dovrò sviluppare nel prossimo futuro. Io lavoro con Winforms e SQL Server quotidianamente su applicazioni vere, pertanto, sulla mia macchina di sviluppo così come sulla macchina per gli esperimenti, ho installata la SQL Server Developer edition, e non ho installato SQLExpress, trovo inutile avere due istanze di SQL Server sul PC.

La prima cosa che ho fatto stamattina per fare le mie prove è stata generare un progetto ASP.Net MVC e guardare cosa c’è dentro. Il progetto è perfettamente funzionante e mostra delle cose interessanti. In particolare, nasce già con una pagina che gestisce la sicurezza. Che bello, mi sono detta vediamo come fa, provo a registrare un nuovo utente. Ovviamente la cosa non ha funzionato, andando a guardare il perché, è piuttosto ovvio, il sistema ha bisogno di un DB SQL Server, e per default è collegato a un database attaccato “al volo” ad un SQLExpress che peraltro non esiste nel template del progetto, perciò non può funzionare. Pertanto ho creato un database sul mio SQL Server ed ho modificato la connection string, da buon DBA, per far connettere l’applicazione al server SQL. Ma purtroppo le cose non funzionavano ancora, sia usando il tool di amministrazione del sito, sia usando la pagina di registrazione di MVC mi veniva restituito un errore, perché nel database mancava tutta la struttura e le tabelle per la gestione della sicurezza.

A questo punto un beginner 1.0 cosa fa? Pianta tutto li e dice, ci riprovo la prossima volta. Un Beginner 1.1 come me inizia da BING o Google e cerca, dopo vari tentativi, con varie parole chiave, che mi hanno portato a forum, tutorial, articoli senza darmi una soluzione, la frase magica è “asp.net create authentication db on sqlserver” ,che mi ha portato qui: Creating the Membership Schema in SQL Server, (Faccio notare che il sito è ASP.Net, lo stesso dove sono i tutorial per MVC) dove ho trovato la soluzione del problema, ovvero usare una bellissima utility installata automaticamente con il framework 2.0 che lanciata dal RUN con il comando: %WINDIR%\Microsoft.Net\Framework\v2.0.50727\aspnet_regsql.exe permette di generare all’interno del mio database sql server le tabelle per la gestione della security e degli utenti, che mi permettono di utilizzare il tool di configurazione del sito per creare utenti all’interno dell’applicazione ed attivano la pagina di login e registrazione della mia applicazione modello base ASP.Net MVC.

Chiunque abbia lavorato con ASP.Net 2.0 dirà, ma è ovvio ed elementare.
Ma chi si trova a partire da zero con una tecnologia che esiste da anni concorderà con me che spesso, i primi gradini sono quelli più alti da superare.

posted @ domenica 1 agosto 2010 10.15 | Feedback (1)

sabato 31 luglio 2010 #

Filtrare le righe con un certo Rank dopo averlo generato

Sul post Usare la funzione Rank per estrarre una lista di righe con la data più alta ho mostrato come usare il ranking per verificare qual’è la data più alta o la più bassa in un determinato range, ma non ho mostrato come poi filtrare in base al ranking generato come mi è stato giustamente fatto osservare da Maurizio, perciò ecco come si fa:

SELECT [IDListinoRg]
      ,[IDListino]
      ,[IDArticolo]
      ,[Prezzo]
FROM (
   SELECT [IDListinoRg]
      ,[IDListino]
      ,[IDArticolo]
      ,[Prezzo]
      ,[ValidoDal]
      ,RANK() OVER (PARTITION BY IDLISTINO, IDARTICOLO ORDER BY ValidoDal DESC) DATERANK
  FROM [paperinik].[dbo].[TbListiniRg]) Li
WHERE
    DateRank = 1

perché non mettere l’espressione di Rank direttamente nella Where? Perché si ottiene un errore, non è permesso probabilmente per le modalità con cui la funzione di Rank viene elaborata, pertanto bisogna fare una select nidificata.

Ovviamente questo tipo di Query non è efficientissima, però da una soluzione ad un problema reale.

Technorati Tag: ,

posted @ sabato 31 luglio 2010 21.31 | Feedback (1)

venerdì 30 luglio 2010 #

Un trucco cattivello per intercettare lo screen capture (printscreen)

Premetto che si tratta di un trucco non di una soluzione, fatto per evitare che l’utente medio faccia cose che preferiremmo non facesse, per risolvere il problema in modo più serio biosgnerebbe intercettarlo a livello di messaggio di sistema. Non so se esiste una policy di macchina per farlo (così come si può disattivare lo screen saver, chissà), ad ogni modo come indicato nel titolo è solo un viscido trucco da programmatore VBI (VB non sta per visual basic).

Se installate un qualsiasi pacchetto per l’intercettazione e la cattura dello schermo questo trucco non funzionerà, però se siete voi (o il Sistemista cerbero) a controllare che cosa viene installato sulle macchine può dare un aiutino.

Su una form attivate il KeyPreview ponendo la apposita property a True, e utilizzando il seguente codice:

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            if( e.KeyCode == Keys.PrintScreen || e.KeyCode == Keys.Print)
            {
                Clipboard.Clear();
                e.Handled = true;
            }
        }

evitate che dopo un Ctrl+Stamp o un Alt+Stamp andando in Paint e premendo Incolla, la videata venga inserita su una bitmap e spedita via e-mail o stampata.

Se qualcuno ha implementato un metodo più serio per farlo, ce lo faccia sapere e ne pubblico volentieri un link.

posted @ venerdì 30 luglio 2010 9.44 | Feedback (0)

martedì 27 luglio 2010 #

Come determinare quale controllo ha sollevato l’evento Item Changed di un Currency Manager

Un post veloce su C# al solito per sapere dove trovare una soluzione la prossima volta che mi serve.

L’oggetto CurrencyManager è sia amato che odiato da chi pone quesiti nei forum dove di solito partecipo, io l’ho sempre trovato utile, anche se a volte è un pochino contorto nelle sue funzionalità, soprattutto per chi è nuovo al databinding.

l’evento ItemChanged, Viene sollevato dal Currency manager ogni volta che un controllo bindato (italiese puro) nel contesto corrente, modifica i dati collegati ad una delle sue property.

per determinare quale controllo ha sollevato l’evento, all’interno dell’ ItemChangedEventArgs abbiamo una proprietà Index. E’ un indice a base 1, strano per essere in .Net, ad ogni modo, usando questo indice nella collezione Bindings del Currency manager, siamo in grado di ricavare quale dei controlli ha modificato il dato. nell’esempio seguente, se una particolare textbox ha modificato i dati, aggiorniamo il database utilizzando una classe data provider.

 void mCurMgr_ItemChanged(object sender, ItemChangedEventArgs e)
        {
                if (mCurMgr.Bindings[e.Index - 1].Control.Name == txtPercentualeCons.Name)
                {
                    mDpScenariAziende.Update();
                }
        }

 

Technorati Tag: ,,

posted @ martedì 27 luglio 2010 11.34 | Feedback (0)

martedì 20 luglio 2010 #

La clausola NOLOCK su una SELECT per velocizzare una query

Qualche giorno fa, il mio collega Luca stava verificando come ottimizzare una query complessa per ottenere una riduzione dei tempi di esecuzione, e consultando vari help, ha effettuato una serie di test. La clausola NOLOCK, aggiunta alle tabelle coinvolte nella query, velocizza notevolmente questo tipo di query. Leggendo i dati senza alcun tipo di blocco.  E’ chiaro che non è sempre applicabile, perché il fatto di non bloccare e quindi leggere i dati “come sono” nel momento della query, può portare a incongruenze se la tabella è una tabella modificata molto velocemente. Ma in casi in cui la tabella non ha variazioni molto rapide, nell’ordine di secondi o meno, oppure dove una fotografia momento per momento è comunque significativa, questa clausola aiuta a diminuire notevolmente i tempi di esecuzione.

Ecco un esempio di query che usa la clausola:

SELECT 
     au.[au_id]
    ,au.[au_lname]
    ,au.[au_fname]
    ,au.[phone]
    ,au.[address]
    ,au.[city]
    ,au.[state]
    ,au.[zip]
    ,au.[contract]
    ,ta.[au_ord]
    ,ta.[royaltyper]
    ,tt.[title]
    ,tt.[type]
    ,tt.[pub_id]
    ,tt.[price]
    ,tt.[advance]
    ,tt.[royalty]
    ,tt.[ytd_sales]
    ,tt.[notes]
    ,tt.[pubdate]
    FROM 
        [pubs].[dbo].[authors] au (NOLOCK)
    Left outer join
        [pubs].[dbo].[titleauthor] ta (NOLOCK)
    ON
        au.au_id = ta.au_id
    Left outer join
        [pubs].[dbo].[titles] tt (NOLOCK)
    ON
        ta.title_id = tt.title_id 
    order by
        au.au_id

posted @ martedì 20 luglio 2010 22.22 | Feedback (0)

mercoledì 14 luglio 2010 #

Come leggere l’ennesimo record di una tabella in SQL Server

A seguito di una domanda postami, scrivo qui, al solito anche per ricordarmelo, come si fa a leggere una specifica riga di una tabella in base ad un arbitrario ordinamento.

Esistendo solo la clausola SELECT TOP non è sempre possibile farlo, ma dalla versione 2005 di SQL Server esiste la funzione Row_Number, che può darci una mano in questo caso. Il test l’ho fatto sulla tabella Authors del famigerato ed antico db di test Pubs, fornito con SQL Server fino alla versione 2000.

Ecco come impostare la query:

SELECT [au_id]
      ,[au_lname]
      ,[au_fname]
      ,[phone]
      ,[address]
      ,[city]
      ,[state]
      ,[zip]
      ,[contract]
      ,[brthdate]
    , row_number() over( order by au_lname) as rnum
  FROM [pubs].[dbo].[authors]

In questo caso, Otteniamo la colonna rnum che contiene un contatore che numera le righe della tabella in ordine di Cognome, volendo, questa funzione ci permette anche di numerare le righe per gruppi, ovvero, se sostituiamo la riga di Row_Number con questa:

    , row_number() over(partition by [state] order by [state]) as rnum

Otteniamo che i nostri autori sono numerati da 1 a n per ognuno degli stati presenti.

Fatta questa query è necessario incapsularla in una seconda select per poter ottenere la riga che ci interessa. 

select * from (
SELECT [au_id]
      ,[au_lname]
      ,[au_fname]
      ,[phone]
      ,[address]
      ,[city]
      ,[state]
      ,[zip]
      ,[contract]
      ,[brthdate]
    , row_number() over( order by au_lname) as rnum
  FROM [pubs].[dbo].[authors]) aut
WHERE aut.rnum = 15

In questo caso otteniamo il quindicesimo record in ordine di cognome, cambiando la clausola order by possiamo decidere di  leggere il dodicesimo in ordine di numero telefonico, o di codice contratto e così via.

posted @ mercoledì 14 luglio 2010 15.58 | Feedback (0)

lunedì 12 luglio 2010 #

SQL Server Aggiornare una tabella con i valori di un’altra tabella

Oggi, proseguendo il lavoro relativo al post precedente, mi si è presentata la seguente necessità,  Creata una tabella contenente alcune informazioni, avevo la necessità di aggiornare le informazioni su questa tabella usando i dati contenuti su un’altra tabella.

Esempio: Ho creato una tabella inventari, contenente Codice Articolo, descrizione, quantità, per crearla ho utilizzato una query di insert dalla tabella articoli di magazzino, ora gli utenti hanno compilato le giacenze, ed io devo aggiungere ultimo costo ed ultimo prezzo leggendoli dalla tabella listini.

Per semplicità, le mie tabelle sono le seguenti:

TbInventari
   IDInventario int
   IDArticolo nvarchar(32)
   Quantita decimal(18,5)
   Perzzo decimal(18,5)
   Costo decimal(18,5)

TbListini
   IDListino int
   IDarticolo nvarchar(32)
   Prezzo decimal(18,5)
   Costo decimal (18,5)

Per aggiornare il prezzo ed il costo sulla tabella inventari, utilizzo una query di Update, con una Join.

UPDATE TbInventari
    SET Prezzo = isnull(lis.Prezzo,0)
      , Costo = isnull(lis.Costo,0)
FROM TbInventari inv
INNER JOIN
    TbListini lis
ON
  inv.IDArticolo = lis.IDArticolo  

In questo modo, metto in relazione le due tabelle utilizzando la JOIN poi aggiorno la tabella Inventari con i Valori della tabella listini.

Tags: ,

posted @ lunedì 12 luglio 2010 18.22 | Feedback (0)

mercoledì 7 luglio 2010 #

Usare la funzione Rank per estrarre una lista di righe con la data più alta

Oggi avevo il problema di dover creare una query che mi restituisse il prezzo più recente contenuto in una tabella listini. Ogni listino può contenere gli stessi articoli ripetuti più volte, ciascuna riga con una data di validità diversa. Ho fatto qualche ricerca in merito ed ho trovato la funzione RANK, usata in un caso simile, pubblico quindi un esempio in modo da ricordarmi come fare quando mi servirà di nuovo e ovviamente dare a chi legge un suggerimento per risolvere simili problemi.

La tabella:

CREATE TABLE [dbo].[TbListiniRg](
    [IDListinoRg] [int] NOT NULL,
    [IDListino] [nvarchar](10) NOT NULL,
    [IDArticolo] [nvarchar](32) NULL,
    [Prezzo] [decimal](18, 5) NULL,
    [ValidoDal] [date] NULL,
 CONSTRAINT [PK_TbListiniRg] PRIMARY KEY CLUSTERED 
(
    [IDListinoRg] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  
= ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]

Ogni riga è collegata al listino tramite IDListino, la tabella listini non ci serve quindi è stata omessa.

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'ABC'
           ,10
           ,CONVERT(date, '20080105', 112))
GO

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'OCQ'
           ,14
           ,CONVERT(date, '20080105', 112))
GO

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'FYS'
           ,21
           ,CONVERT(date, '20080105', 112))
GO

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'MUI'
           ,13
           ,CONVERT(date, '20080105', 112))
GO

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'SIO'
           ,42
           ,CONVERT(date, '20080105', 112))
GO

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'GOU'
           ,18
           ,CONVERT(date, '20080105', 112))
GO

INSERT INTO [dbo].[TbListiniRg]
           ([IDListino]
           ,[IDArticolo]
           ,[Prezzo]
           ,[ValidoDal])
     VALUES
           ('CLD'
           ,'HOU'
           ,20
           ,CONVERT(date, '20080105', 112))
GO

Questo script inserisce alcuni articoli, ripetuto variando il codice listino, le date ed i prezzi ci permetterà di dimostrare la nostra funzionalità.

SELECT [IDListinoRg]
      ,[IDListino]
      ,[IDArticolo]
      ,[Prezzo]
      ,[ValidoDal]
      ,RANK() OVER (PARTITION BY IDLISTINO, IDARTICOLO ORDER BY ValidoDal DESC) DATERANK
  FROM [paperinik].[dbo].[TbListiniRg]

La funzione RANK ci permette di indicare uno o più campi di raggruppamento su cui il calcolo del Ranking viene effettuato,  usando la clausola PARTITION BY, ovvero su quali campi spezzare il calcolo del ranking in questo caso codice listino ed articolo. La clausola ORDER BY indica invece qual’è il campo su cui il Ranking viene misurato, mentre la clausola DESC dice che vogliamo andare dalla data più recente indietro.

Questa query ci da questo risultato.

IDListinoRg IDListino IDArticolo Prezzo ValidoDal DATERANK
107 CLD ABC 1.000.000 24/10/2010 1
86 CLD ABC 1.000.000 15/05/2010 2
65 CLD ABC 1.000.000 08/01/2010 3
44 CLD ABC 1.000.000 08/09/2009 4
23 CLD ABC 1.000.000 16/04/2009 5
1 CLD ABC 1.000.000 05/01/2008 6
2 CLD ABC 1.000.000 05/01/2008 6
109 CLD FYS 2.100.000 24/10/2010 1
88 CLD FYS 2.100.000 15/05/2010 2
67 CLD FYS 2.100.000 08/01/2010 3
46 CLD FYS 2.100.000 08/09/2009 4
25 CLD FYS 2.100.000 16/04/2009 5
4 CLD FYS 2.100.000 05/01/2008 6
112 CLD GOU 1.800.000 24/10/2010 1
91 CLD GOU 1.800.000 15/05/2010 2
70 CLD GOU 1.800.000 08/01/2010 3
49 CLD GOU 1.800.000 08/09/2009 4
28 CLD GOU 1.800.000 16/04/2009 5
7 CLD GOU 1.800.000 05/01/2008 6
113 CLD HOU 2.000.000 24/10/2010 1
92 CLD HOU 2.000.000 15/05/2010 2
71 CLD HOU 2.000.000 08/01/2010 3
50 CLD HOU 2.000.000 08/09/2009 4
29 CLD HOU 2.000.000 16/04/2009 5
8 CLD HOU 2.000.000 05/01/2008 6

Pertanto per poter vedere solo il prezzo più recente, ci basta un filtro sul DATERANK = 1, così come per avere la data meno recente ci basta togliere la clausola DESC dalla query.

Technorati Tag: ,,

posted @ mercoledì 7 luglio 2010 23.18 | Feedback (2)

giovedì 1 luglio 2010 #

Una piccola utility per le Enumerazioni

Spesso può capitare che sia necessario sapere quali sono i valori di una enumerazione, soprattutto quelle di sistema e guardacaso, sulla descrizione delle classi nell’help, le enumerazioni vengono descritte con i loro nomi ma difficilmente con i valori numerici.

Oggi a me serviva sapere quali erano i valori di CharacterCasing, per un problema di reference, pertanto ho scritto un minuscolo programmino Winforms, per visualizzarle. Ovviamente il mio esempio visualizza 2 enumerazioni, potete aggiungere il necessario a visualizzare tutte quelle che volete.

        private void btnCharacterCasing_Click(object sender, EventArgs e)
        {
            int[] values = (int[])Enum.GetValues(typeof(CharacterCasing));
            string[] names = Enum.GetNames(typeof(CharacterCasing));
            this.txtResult.Text = "CharacterCasing" + Environment.NewLine;
            for (int i = 0; i < names.Length; i++)
            {
                this.txtResult.Text += string.Format("{0} = {1}", names[i], values[i]) + Environment.NewLine;
            }

        }

        private void btnDialogResult_Click(object sender, EventArgs e)
        {
            int[] values = (int[])Enum.GetValues(typeof(DialogResult));
            string[] names = Enum.GetNames(typeof(DialogResult));
            this.txtResult.Text = "DialogResult" + Environment.NewLine;
            for (int i = 0; i < names.Length; i++)
            {
                this.txtResult.Text += string.Format("{0} = {1}", names[i], values[i]) + Environment.NewLine;
            }

        }

Ecco il codice dietro la form

showEnums01

showenums02

Ed il risultato finale.

Tags: ,

posted @ giovedì 1 luglio 2010 9.58 | Feedback (0)

giovedì 24 giugno 2010 #

Nuove dal Remix 10 – Milano 23 giugno 2010

Un po’ di appunti presi al volo e organizzati scuramente a caso che vogliono aiutare me stessa ad organizzare le idee su quanto ascoltato e, se non c’eravate,  spero possano darvi qualche spunto per un approfondimento sugli argomenti di vostro interesse. Siamo partiti con una KeyNote di 2 ore, presumo che qualcuno lo trovi strano, perché la KeyNote di solito è una piccola presentazione degli argomenti trattati e magari un saluto da parte dei manager, che per una volta possono uscire e mostrarsi al popolo degli sviluppatori. Non è stato così, perché la conferenza è stata orientata esclusivamente agli argomenti tecnici, e soprattutto la quantità di novità tecnologiche che dovevano essere presentate era tale che la KeyNote non poteva essere solo un cappello di saluto, perché dividendoci poi nei vari percorsi avremmo perduto alcune informazioni davvero importanti, consiglio tutti di tener nota della homepage dell’evento: http://www.microsoft.com/italy/nextweb/remix/keynote.aspx dove verrà pubblicato il video della KeyNote per guardarlo se non avete potuto assistere alla diretta in streaming.

Il motivo per cui sono andata al Remix è stato capire che cos’è Windows Phone, quale sarà la sua funzione, se lo strumento potrà essere interessante per quello che è il mio lavoro, quanto dovrò studiare ed imparare se deciderò di usarlo, e soprattutto, tutto questo darà valore aggiunto alle applicazioni sviluppate per i miei clienti?

Obiettivamente, Windows Phone non è semplicemente la nuova versione di Windows Mobile, Windows Mobile e tutta la parte dei sistemi Windows per apparecchiature con sistemi e software embedded è ancora dov’era ieri e continuerà ad andare avanti per un percorso che la condurrà verso Windows 7 ma per step più ponderati che assicureranno come sempre in casa Microsoft la continuità a tutti i produttori e gli sviluppatori (e non sono pochi) che lavorano su device con questo tipo di sistemi operativi.

Quindi, appurato che Windows Phone è un oggetto nuovo, un sistema operativo completamente nuovo e non ha nulla a che vedere con Windows mobile 6.5 la domanda che sorge è: ma è bene o male? Ovviamente nessuna delle due, Windows Phone è una nuova piattaforma, che ci da tutta una serie di opportunità come sviluppatori, si tratta di capire se coglierle può essere uno stimolo al nostro business attuale oppure se sono così lontane dal nostro core business che magari è meglio aspettare.

Cerchiamo di capire quindi cosa è stato puntualizzato più volte in tutti gli interventi.

  • Windows Phone è un nuovo sistema operativo.
  • Windows Phone è un sistema User Centric, ovvero orientato all’utente, l’utente decide cosa fare, quando, come, ciò che noi svilupperemo su questo sistema sono degli oggetti che forniranno all’utente servizi su richiesta, le nostre applicazioni non potranno mai essere invasive o prendere il controllo della device.
  • Windows Phone è uno strumento che ha bisogno di essere connesso per funzionare, pertanto è orientato al fatto che adesso e nel prossimo futuro, il telefono sarà sempre in grado di essere online, che i servizi di connettività forniti dagli operatori telefonici saranno sempre maggiori e sempre più a basso costo. Se questo non accadrà, ci sarà la tecnologia mobile che continuerà la sua evoluzione a questo scopo (mai chiudere le porte a nulla :P).
  • Tutte le applicazioni Windows Phone per poter essere installate su un telefono dovranno passare attraverso due fasi, l’invio ad un sito (Marketplace) che ne validerà il codice e aggancerà un certificato digitale che dirà che sono installabili. E la pubblicazione sul sito di questo shop online in modo che siano scaricabili dai nostri clienti. La versione 1.0 dello store avrà esclusivamente un tipo di applicazione vendibile/free, una applicazione Pubblica.
  • Pertanto, quanto scritto nell’ultima frase qui sopra, porta ad una ulteriore specifica. La versione 1.0 di WIndows Phone è specificamente un prodotto per il consumer, e si comporta (guardacaso) esattamente come Iphone (per non fare nomi). Ovviamente la prima domanda che abbiamo fatto è stata, “Ma scusate, noi che facciamo applicazioni B2B specifiche per i nostri clienti, non potremo quindi usare la tecnologia?” La risposta ufficiosa al momento è, nel Marketplace futuro, è già stato richiesto e quindi sarà con ottime probabilità possibile, creare degli Store Privati, per questo tipo di prodotti. Ma questo è futuro ed obiettivamente, chiunque lavora con il B2B probabilmente avrà bisogno di tempo per muoversi in un nuovo mondo quindi aspettare la 2.0 magari ci sta.
  • Però tutto questo non toglie che abbiamo l’opportunità di iniziare a giocare con il sistema per produrre qualche programma di uso generico che ci permetta di sperimentare l’oggetto e le sue potenzialità.
  • Gli attuali telefoni non saranno in grado di essere upgradati a Windows Phone, anche quelli con Mobile 6.5 perché nelle specifiche Hardware, Microsoft ha incluso 3 tasti: Start, Back, Search e il tasto Search non c’è sui telefonini windows mobile attuali.
  • Le applicazioni per Windows Phone saranno scritte esclusivamente in .NET, al momento solo C# ma scommetterei che il popolo di VB si farà sentire e probabilmente otterrà la “traduzione”.
  • Le applicazioni per Windows Phone non avranno alcun accesso diretto alla device, e potranno memorizzare dati solo nell’isolated storage. [A questa affermazione sono certa che più di qualche boccheggio c’è stato].
  • Disinstallando l’applicazione, i dati della stessa verranno eliminati. Pertanto, le applicazioni Windows Phone, per persistere informazioni importanti dovranno usare degli strumenti Cloud (Es. Webservices) per salvare i propri dati in luogo sicuro.

Qui ovviamente c’è qualche perplessità, anche perché al momento non c’è nulla in previsione relativamente alla sincronizzazione con il PC.Creare un prodotto per Phone di tipo generico che salva i dati su un cloud server mi sembra un po’ un controsenso, se il programma è sul telefono al massimo parlerò al mio PC, ma probabilmente questa mia perplessità è dovuta al fatto che da Dinosauro informatico quale sono io, mettere su un server pubblico i miei dati privati (anche solo i numeri telefonici di famiglia ed amici) è una cosa non concepibile.
Però, vi sono adesso molti nuovi servizi forniti in the cloud che danno alle persone spazio per mettere i propri dati, oppure servizi di infrastruttura di rete quindi magari, sarà una cosa normalissima fra un mese, acquistare in the cloud un servizio di VPN che ci permetta di parlare in modo sicuro al nostro PC di casa da ovunque noi siamo e quindi soddisfare al mio bisogno di privacy e alla possibilità di trasmissione dei dati da Phone al Cloud o di richiedere i dati al Cloud da parte del Phone.

  • Con che strumento si scriveranno applicazioni per Windows Phone? Silverlight oppure XNA, il primo lo conoscete tutti credo, il secondo è il sistema di librerie per i Giochi, creato per chi fa giochi per XBox, per WIndows e adesso per Windows Phone. Quindi una ulteriore opportunità per aprire le nostre menti a cose nuove.

La sessione su XNA tenuta da Giuseppe Maggiore, un ragazzino che con tutta probabilità potrebbe essere mio nipote, ha mostrato anche a noi dinosauri che ci sono dei mondi che probabilmente da ragazzini abbiamo sognato di esplorare giocando a Space Invaders o ad Asteroids, e che ora, sono resi accessibili grazie alle librerie di XNA, intendiamoci, non è che domattina io posso mettermi a fare un videogioco in casa, comunque le conoscenze che ci stanno dietro non sono banali, però, può essere che possiamo usare questo tipo di librerie per implementare cose non pensabili in una applicazione windows normale e ciò è bello.

  • C’è una cosa importante relativamente agli strumenti da ricordare, La UI del nostro prodotto potrà essere fatta con Silverlight oppure con XNA e non con un misto dei due, è impossibile per questioni di architettura. Ma I due sistemi possono invece usare ciascuno le librerie non UI dell’altro, quindi ad esempio Silverlight può usare XNA per acquisire uno stream audio dal microfono e poi riprodurlo.
  • Per sviluppare per phone sono disponibili le CTP per Visual studio 2010 e per Expression Blend, e a questo proposito è uscita la versione 4.0 e l’upgrade per chi ha la 3.0 è gratuito quindi scaricatela subito. Entrambe per provare i vostri progetti usano un emulatore che in realtà è una virtual machine che utilizza le risorse hardware del vostro PC visto che Windows Phone userà in tutto e per tutto la grafica hardware via Direct X (ricordo a tutti che WPF e Silverlight usano le Direct X).
  • Per ora non ci sono telefoni veri, saranno presentati per la Holiday season (Ottobre – novembre) solo pochi eletti, fra cui è Lorenzo Barbieri, sono dotati di un prototipo su cui giocare. Il Phone sarà multitouch  con la possibilità di seguire almeno 4 touch multipli.
  • Noi programmatori, come già detto, avremo a disposizione il framework ma non avremo in alcun modo la possibilità di accedere direttamente alla device, potremo parlare al telefono lanciando applicazioni potremo in alcuni casi interagire con delle applicazioni, ma non saremo mai noi a far eseguire delle cose al telefono. Potremo mettere un messaggio in spedizione sulla posta, potremo chiedere un indirizzo di e-mail alla rubrica dei contatti, ma non potremo leggere la posta ne modificare o inserire un contatto.

Su alcune cose concordo, su altre un po’ meno, se il mio Phone può connettersi in the cloud al mio server centrale dove sono gli indirizzi dei miei contatti aziendali, perché non posso richiedere che uno di questi sia inserito nella mia rubrica? Presumo comunque che essendo una 1.0 ci siano state una serie di decisioni prese per dare delle milestones concrete ai programmatori, poi, ovviamente c’è tutto lo spazio per crescere. Potrebbe anche essere che ho capito male io e c’è o ci sarà un metodo per interagire con la rubrica in modifica, ma direi che per questo ci servono degli approfondimenti.

  • Ci sono delle linee guida piuttosto strette relative alla user interface,  che possono essere scaricate sul portale di Windows Phone, credo che il documento abbia 90 pagine o giù di li è opportuno dargli un’occhiata, perché la UI non ha molto a che vedere con le mie beneamate (o beneodiate) Windows Forms.
  • Ci sono anche dei blocchi fisici, 4 bottoni nella appbar, menu ove mettere il resto delle funzioni, nessuna interattività con la system tray se non un Visibile/Invisibile
  • In compenso c’è un controllo webbrowser che possiamo governare e su cui possiamo sovrapporre dei controlli (almeno quello).
  • Prossimamente saranno di certo disponibili webcast relativi a molti degli aspetti trattati al Remix, pertanto tenete d’occhio il portale BEIT, e il blog del team di MSDN, nonché, se vi piacciono, twitter e facebook dei team.
  • Una delle cose che potremo fare e qui spero di aver capito bene, è dare la possibilità all’utente del nostro Phone, di sottoscrivere dei servizi da noi offerti in connettività (in the cloud) e tramite questi servizi mandare a chi è iscritto delle notifiche in Push, ovvero il nostro server chiama i telefoni e li avvisa che qualcosa ha cambiato stato, dopodiché sarà l’utente che deciderà se chiedere ulteriori informazioni oppure se ignorarci. Queste notifiche sono di tre tipi:
  1. RAW, invio di dati diretto alla nostra applicazione, se l’applicazione non è in funzione il messaggio viene ignorato.
  2. TOAST, viene inviata una richiesta in primo piano all’utente che deve rispondere (come una messagebox improvvisa, un tipo di evento da usare con parsimonia se non volete che la vostra applicazione sia immediatamente disinstallata).
  3. TILE, una notifica che modifica lo stato di una Tile ovvero un elemento dell’interfaccia che il nostro utente ha sul telefono (ad esempio se mi arriviano degli SMS, sulla tile che apre il SMS reader ho il numero di sms ricevuti non letti. Ho in homepage del telefono il collegamento alla pagina facebook del moroso o della morosa, lui o lei cambia la sua foto su facebook ogni mattina e io la ricevo immediatamente sulla Tile del collegamento. (Spero che ovviamente vi siano anche dei servizi + utili).

Un ultima annotazione, Ottimo lo spogliarello di Giorgio Sardo, l’italiano più americano del team del Remix, Guardate con attenzione quanto ha mostrato su Internet Explorer 9, e quanto, finalmente, il team di IE stia lavorando gomito a gomito con il W3C per darci HTML5 e CSS3 ed il modo di farli funzionare nello stesso modo su tutti i browser.

Scaricate la preview di IE9 dal sito http://ie.microsoft.com/testdrive/ e osservate quanto si potrà fare finalmente senza dover usare plugin, immagini e sotterfugi ignobili quando si vorrebbe solo ottenere un sito graficamente carino ma con poco overhead. Voglio dire ma serviva arrivare a HTML5 per permetterci di fare i bordi arrotondati??? :D:D:D

Conclusione, nuovi mondi da scoprire, nuove porte da aprire, speriamo solo che gli abitanti dei mondi e i mostri che stanno dietro alle porte siano amichevoli!

Alcuni link da visitare:

http://ie.microsoft.com/testdrive/

http://www.windowsphone7.com/

http://blogs.academicclub.org/xna/

Potrei continuare, ma sicuramente tutti i links saranno disponibili sul portale del remix assieme alla keynote e alle altre sessioni.

Concludo con una Menzione Speciale a Giuseppe Maggiore, volevo dargli un 6 6 6 invece che 5 5 5 (il massimo) nella votazione di feedback, perché ha tenuto la sessione in modo piacevole, ed essendo l’ultima se non lo fosse stata poteva risultare in una fuga generale. Perché ha parlato di cose difficili cercando di mantenerle abbastanza terraterra da poter essere comprese da noi dinosauri che non abbiamo mai avuto dimestichezza con la matematica seria, perché la sua simulazione di powerpoint tridimensionale era davvero bellissima, e perché ha davvero una “bella testa” (A beautiful mind :D). Spero di avere occasione di ospitarlo a qualcuna delle nostre iniziative locali, visto che studia a Ca’Foscari, così da far conoscere un po’ di cose fuori dalla normale routine a tutti noi programmatori di tipo “Gestionale”.

posted @ giovedì 24 giugno 2010 22.43 | Feedback (2)

venerdì 18 giugno 2010 #

This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.

Scusate lo strano titolo del post, ma probabilmente chi incontrerà questo errore proverà a cercarlo su un motore di ricerca e me ne sarà grato.

Premessa: I miei programmi, quando installati sui PC degli utenti finali per principio Non usano la GAC a meno che io non vi sia obbligata per poter esporre una qualche funzionalità a .COM.

Però, per la struttura con cui sono costruite le molte ed articolate librerie di base utilizzate per i miei progetti, tali librerie di base sulle macchine di sviluppo (e solo sulle macchine di sviluppo) vengono pubblicate in GAC. Questo serve a permettere a noi programmatori di poter aggiornare una libreria di base ed essere certi che qualsiasi soluzione/progetto che usa questa libreria base istantaneamente abbia le modifiche disponibili.

Per quale motivo facciamo tutto questo, perché le DLL poste in GAC non vengono copiate sulla cartella locale di ogni progetto che le usa da Visual Studio e quindi, ricompilando il progetto che referenzia la DLL base, esso fa certamente riferimento alla DLL giusta.

Questa soluzione è stata adottata dopo che il setup di una applicazione, a causa dell’ordine in cui il progetto di setup ha copiato le DLL referenziate quando è stato generato, ha inserito una versione vecchia di una dll e quindi l’aggiornamento installato dal cliente non funzionava.

Detto questo, che roba è il titolo di questo post?

Il titolo di questo post è il messaggio di errore che gacutil.exe, l’utility che installa e disinstalla dalla GAC gli assembly produce tentando di caricare in GAC una DLL compilata con il framework 4.0. Il gacutil.exe che uso io, me lo porto dietro dal 2005, infatti tale utility si trova sperduta nei meandri dell’SDK di Windows quindi, opportunamente, io ne ho fatto copia e la metto su C:\Windows così sono sicura di trovarla (Lo so che per qualche sistemista sarà un eresia, ma ragazzi, noi programmatori dobbiamo lavorà!)

Detto ciò, la mia utility scritta per il framework 2.0 funziona correttamente fino al framework 3.5 poi non funziona più e da questo messaggio.

Il motivo per cui non funziona più non lo so esattamente ma, con il framework 4.0 la GAC non si trova più dove era prima, ovvero:

c:\Windows\Assembly

ma è stata spostata (per ragioni più che giustificate ovviamente) e si trova sotto:

C:\Windows\Microsoft.NET\assembly\GAC_MSIL (per lo meno sul mio pc con windows Vista business)

pertanto, è necessario procurarsi ed usare il nuovo gacutil.exe fornito con Visual Studio 2010 accessibile senza tema usando il prompt dei comandi di Visual Studio, ma nascosto e praticamente introvabile a chi non conosca per filo e per segno le cartelle degli SDK di Microsoft.

Sulla mia macchina, il nuovo gacutil.exe si trova qui:

C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools

Sostituito questo con l’originale la registrazione va a buon fine e gli assembly vengono copiati sulla GAC correttamente, e ovviamente vengono correttamente registrati anche gli assembly delle versioni precedenti del framework.

posted @ venerdì 18 giugno 2010 14.56 | Feedback (0)

giovedì 10 giugno 2010 #

Lettura dati da Excel via ADO e Date Nulle

Un appunto veloce che arriva dall’esperienza, se utilizzate ADO all’interno di Excel per connettervi ad un Database e riempire le celle del foglio elettronico con dei dati, assicuratevi che la query o la stored procedure che utilizzate non ritorni mai NULL sulle colonne di tipo datetime.

Un NULL su una colonna di tipo data, provoca un rallentamento enorme nella lettura dei dati. nella nostra esperienza, siamo passati da 2 minuti e mezzo a 10 secondi togliendo i NULL sui campi data.

Potrebbe essere un problema dei driver o delle librerie usate, potrebbe essere una antipatia di excel verso i Db.NULL non saprei, obiettivamente l’importante è che il workaround funzioni ;)

SELECT
    ....
    ISNULL(Data, Convert(datetime, "20000101", 103)) as Data
    ....
FROM
    TbDati
    ....
WHERE
    ....
Tags: , ,

qui

posted @ giovedì 10 giugno 2010 11.38 | Feedback (0)

Liberarsi del Bonjour service di Apple

Un appunto da tener presente quando cambiando PC si reinstalla tutti i tools:

Questo servizio viene installato automaticamente da alcune applicazioni Adobe come ad esempio Photoshop CS3 (so che è vecchio ma bisogna accontentarsi).

Per eliminarlo  questa è la procedura:

Dal prompt dei comandi

sc stop "bonjour service"
sc delete "bonjour service"
Perché non sia + eseguito
Cancellare la cartella che trovate su programmi perché nessuno lo faccia ripartire
E facendo un giro sul registry cancellate anche tutte le chiavi che contengono il suo nome.

Tags:

posted @ giovedì 10 giugno 2010 11.26 | Feedback (1)

martedì 1 giugno 2010 #

Un interessantissimo articolo per capire i threads

Cercando sui motori di ricerca a volte capita di trovare delle cose interessanti anche se non hanno molto a che vedere con quello che stai cercando.

Oggi, cercando alcune notizie sull’intercettazione di un keypress, il mio collega ha trovato questo articolo:

http://www.albahari.com/threading/

scritto da un certo signor Joseph Albahari, che spiega in dettaglio tutto quello che è necessario sapere per lavorare con i threads in una applicazione.

Pertanto lo appunto qui sia per me che per chi legge e faccio i miei complimenti al Signor Albahari per la sua chiarezza.

posted @ martedì 1 giugno 2010 18.18 | Feedback (0)

mercoledì 12 maggio 2010 #

Rappresentazione delle Join SQL

Più di qualche volta, sui forum e nelle richieste che mi vengono fatte da chi sta iniziando a lavorare con il linguaggio SQL mi viene detto:

“Non capisco la differenza tra Inner Join, e Left o Right outer Join” Pertanto provo a spiegarlo con un disegnino e un paio di note:

 

Date due tabelle:

Tabella Left (testata) contiene i campi IDTs e DescrizioneTs

Tabella Right (righe) contiene i campi IDRg, IDTs, DescrizioneRg, ValoreRg

Una Inner Join:

SELECT ts.IDTs, ts.DescrizioneTs, rg.IDRg, rg.DescrizioneRg, rg.ValoreRg
    FROM
        TabellaLeft ts
    INNER JOIN
        TabellaRight rg
    ON
        ts.IDTs = rg.IDTs

E’ rappresentata graficamente dal disegno qui sotto e restituisce Tutte le righe della tabella TS (left) che hanno almeno una corrispondenza nella tabella RG (right) pertanto, se vi fossero delle testate senza alcuna riga, non verrebbero visualizzate.

Una Left Outer Join:

SELECT ts.IDTs, ts.DescrizioneTs, rg.IDRg, rg.DescrizioneRg, rg.ValoreRg
    FROM
        TabellaLeft ts
    LEFT OUTER JOIN
        TabellaRight rg
    ON
        ts.IDTs = rg.IDTs

E' rappresentata graficamente nel disegno qui sotto e restituisce tutte le righe della tabella TS (Left) e solo le righe della tabella RG che hanno corrispondenza nella tabella TS, pertanto se vi sono Righe la cui TS non esiste (posto che non vi sia una relazione di foreign key a impedire questo tipo di possibilità) queste righe non saranno visibili in questa Join. Se la testata non avesse alcuna riga, comunque comparirà e i dati relativi alla riga saranno a NULL

Una Right Outer Join:

SELECT ts.IDTs, ts.DescrizioneTs, rg.IDRg, rg.DescrizioneRg, rg.ValoreRg
    FROM
        TabellaLeft ts
    RIGHT OUTER JOIN
        TabellaRight rg
    ON
        ts.IDTs = rg.IDTs

E' rappresentata graficamente nel disegno qui sotto e restituisce tutte le righe della tabella RG (Right) e solo le righe della tabella TS che hanno corrispondenza nella tabella RG, pertanto se vi sono Righe la cui TS non esiste (posto che non vi sia una relazione di foreign key a impedire questo tipo di possibilità) queste righe saranno visibili ma i campi rappresentanti i dati di Testata saranno NULL.

 

Sperando di aver contribuito a fare un po’ di chiarezza ai principianti, vi auguro buon lavoro.

Tags: ,

posted @ mercoledì 12 maggio 2010 16.09 | Feedback (0)

martedì 11 maggio 2010 #

Alcune News su TFS2010 e Visual Studio 2010

Considerato il numero di innovazioni, aggiunte, rivoluzioni, nuove funzionalità e chi più ne ha più ne metta, che sono state introdotte da Visual Studio 2010 e da Team Foundation Server 2010, ho deciso (visto che almeno in questocampo sono il “CAPO”) di far migrare il nostro team di sviluppo immediatamente. Pertanto, mercoledì scorso abbiamo aggiornato tutti i nostri componenti, compilato e rilasciato tutti i progetti, fatto una copia sulle macchine di sviluppo di tutto ciò che c’era sul server e siamo partiti con l’aggiornamento.

Le operazioni effettuate per aggiornare da TFS2008 Workgroup a TFS2010 sono state le seguenti:

  • Backup dei database di TFS
  • Stop del server
  • Copia fisica dei database di TFS

Avendo già installato TFS 2008 su SQL Server 2008 non era necessario aggiornare il server dati che comunque ha installato SP1 e tutte le patches necessarie.

Poi, visto che non siamo così ricchi da poterci permettere una macchina nuova abbiamo fatto le seguenti operazioni:

Disinstallazione di TFS 2008, Visual Studio 2008 e tutto ciò che ad esso era collegato dal server. Riavvio, patch e upgrades di Windows, la macchina è un 2003 standard con tutti gli SP installati.

Installazione di Visual Studio 2008 sul server, Installazione di TFS sul server, Configurazione del TFS (indicandogli quali erano i database del vecchio TFS) e alcune opzioni veramente banali.

Installazione di Team Explorer sul server.

E magicamente tutto funzionava.

Abbiamo in contemporanea passato tutte le macchine di sviluppo a Visual Studio 2010 e installato il Team explorer per 2010, e dopo l’aggiornamento, tutto funziona perfettamente, abbiamo solo perso un paio di giornate a convertire e ricompilare tutti i progetti e ad aggiustare un paio di cose sulle Build (relative ai progetti di Setup per inciso) ma per il resto ogni cosa ha funzionato senza alcun problema o stranezza.

Posso dire che le compilazioni dei progetti sono notevolmente più veloci e che gli addin e le extension che si trovano già in quantità via extension manager permettono di configurare visual studio in modo confortevole e migliorare notevolmente il lavoro di sviluppo.

Pertanto tutti coloro che avessero dubbi e perplessità, chiedano pure, ma davvero problemi non ve ne sono stati.

Tags: ,

posted @ martedì 11 maggio 2010 13.22 | Feedback (0)

domenica 11 aprile 2010 #

DNN 5.3 Skinning ed emicranie

Spero che questo post possa evitare qualche emicrania e soprattutto la caduta nel più profondo degli inferi a causa delle maledizioni lanciate a chi ha deciso di modificare in modo così poco comprensibile una cosa che fino alla versione 4.9 era semplice.

La creazione di uno skin personalizzato per DNN è una cosa facile se sapete cosa fare è invece molto difficile se non lo avete mai fatto. Anche il manuale di base scaricabile dal sito è in grado di darvi un sacco di nozioni su come fare ma alla fine della sua lettura, difficilmente sarete in grado di fare uno skin.

Pertanto, come sempre si fa in informatica, è meglio copiare per capire. Il mio consiglio è, andate su uno dei siti che vendono skin per DNN, sceglietene uno che vi piace, comperatelo, scaricatelo, createvi un mini web (o meglio ancora installatelo su un DNN locale sul vostro PC) dategli un occhiata e poi iniziate a provare a cambiare una classe CSS qui e una Table Li, per vedere cosa succede.

Ma non è di questo che voglio parlare in questo post, bensì di come la gestione degli skin è stata stravolta con la versione 5.3.

Con la versione 5 di DNN nella gestione degli skin sono cambiate alcune cose fondamentali, che ho trovato DEVASTANTI per il mio modo di lavorare.

  1. Gli skin non possono più essere installati dall’utente administrator del portale ma devono essere installati dall’utente Host di DNN.
  2. Pertanto se io avessi un cliente che acquista un portale sul mio DNN, lui non potrebbe con il suo utente amministrativo installarsi lo skin, e gestirselo come vuole ma dovrebbe passare per me. A questo per fortuna c’è un Workaround, infatti, i programmatori di DNN per il momento ci hanno fatto la grazia di permetterci di caricare a manina uno skin sulla cartella apposita del portale e fare in modo che si veda dalla finestra di amministrazione skins anche se non installato usando il wizard.
    Per fortuna che è stato fatto, altrimenti tutti coloro che hanno skin personalizzati probabilmente avrebbero dato fuoco alla DNN corporation.
  3. Gli skin non possono più essere installati dalla pagina ad essi dedicata perché sono divenuti delle estensioni di DNN. (Domanda: una chiamata allo stesso oggetto che sta sulla pagina estensioni dalla normale pagina skin era davvero così difficile?)
  4. Gli skin non sono più un file zip che contiene 2 file zip, uno con lo skin e l’altro con i container, e non contengono solo i files ascx, css e le immagini che formano lo skin ma devono essere dotati di un file XML chiamato Manifest che deve descrivere come è fatto e cosa contiene lo skin.
  5. Gli skin ed i container devono essere installati come estensioni separate, quindi bisogna fare un extension per lo skin e uno per i suoi container. Sulla documentazione c’è scritto che i vecchi skin si installano ancora normalmente, io ci ho provato ed ho fallito, probabilmente colpa mia, ma non sono riuscita a capire perché.

Se avete uno skin già installato e funzionante su un portale, ma lo avete caricato con il Workaround di usare l’FTP sulla cartella degli skin del portale, potete farlo divenire una extension usando il DNN. Essendo questa una operazione poco chiara, la scrivo anche per me stessa, in modo che la prossima volta che mi dimenticherò come fare, posso ricordarmelo cercando qui:

  1. Loggatevi come Host del DNN
  2. Dal menu Host aprite la pagina Extensions
  3. Sulla pagina Extensions, dal menu in cima al modulo o da quello in fondo alla pagina selezionate Create Extension
  4. Nella pagina che vi apparirà, vi verranno chieste le seguenti cose:
    • Select Extension type – è una combo in cima alla pagina che contiene già l’opzione “Authentication System” io dimentico sempre di modificarla in Skin e dato che non viene richiesto nulla in merito vado avanti e faccio dei danni! Se vi dimenticate di impostare questa combobox su Skin per creare lo skin e su Container per creare il container, vi troverete delle extension installate e inesistenti, fortunatamente c’è il tasto di cancellazione… Ma non era più user friendly mettere la combo a “None” o vuota e alla pressione del tasto Prossimo step, far suonare una sirena da nave e darci dell’idiota perché non l’avevamo compilata???
    • Name – qui dovete indicare il path dello skin nel formato (Portals\10\Skins\Business04_org) e non c’è scritto da nessuna parte.
    • Friendly Name – qui scrivete un nome amichevole per il vostro skin (Business04_org)
    • Description – Qui descrivete cosa c’è o cosa fa lo skin
    • Version – indicate un numero di versione usando le 3 combobox
  5. Cliccate sul prossimo  step e fate attenzione che non c’è il tasto Back se avete dimenticato qualcosa siete fritti!
  6. Nella pagina che vi apparirà verranno chieste le seguenti cose:
  7. Cliccate sul tasto prossimo step e semplicemente vi ritroverete sulla pagina Extensions di nuovo, però se scendete sulla lista Skins ora troverete il vostro skin, che avrà a disposizione l’icona di una matitina per modificarlo, ma usualmente non avrà a disposizione l’icona con la X rossa per cancellarlo. Questo non so ancora perché accada.

Adesso che il vostro skin (e volendo potete ripetere per il container) è divenuto parte di DNN come extension, se volete potete creare un package per installarlo ed esportarvelo. Come, lo scrivo anche quello perché mi sono serviti 6 tentativi per farlo.

  1. Loggatevi come Host del DNN
  2. Dal menu Host aprite la pagina Extensions
  3. Sulla pagina Extensions, scendete fino alla lista degli skin (o filtrate la visualizzazione sul tipo skin) da esportare e fate click sull’icona con la matita che gli sta accanto.
  4. Nella pagina dello skin, in fondo appare l’opzione Create Package, cliccate sul link per far partire il wizard.
  5. La prima pagina vi informa di cosa state creando e vi espone 2 checkbox, lasciate tutto come sta e cliccate sul prossimo step.
  6. Se state esportando uno skin di un portale, già la seconda pagina contiene dati errati, infatti, la prima textbox che dovrebbe contenere il path del vostro skin contiene qualcosa di simile a questo:
    Portals\_default\Skins\Portals\10\Skins\MyskinFolder
    è palesemente sbagliato perché somma la cartella del vostro skin alla cartella di default degli skin a livello di host.
  7. Modificate il tutto togliendo la parte iniziale:
    Portals\10\Skins\MyskinFolder
    e premete il link Refresh File List che compare accanto alla textbox.
  8. Nella text multiline compariranno tutti i files del vostro skin, verificate che vi siano solo quelli giusti e nel caso compaiano anche quelli della cartella _vti_conf che potrebbe essere presente, eliminateli perché sono doppi e non servono se non a dare errori.
  9. Premete il link prossimo step, vi apparirà una pagina con una text multiline contenente il testo XML del manifest file del nuovo skin, potete lasciarlo com’è o modificare qualsiasi descrizione vogliate.
  10. Premete il link prossimo step, vi apparirà una pagina con 2 textbox single line minuscole che contengono il manifest file name e l’archive file name, questi due path sul mio DNN hanno questa forma:
    MyDnn_Portals\10\Skins\nomeskin.dnn
    MyDnn_Portals\10\Skins\Nomeskin_01.00.00_Install.zip
    E sono fondamentalmente sbagliati perché non esiste una cartella con questo nome sul mio server, pertanto lasciandoli così DNN produce un Errore e non produce lo skin. è necessario quindi cancellare la parte iniziale dei 2 nomi di file trasformandoli in:
    nomeskin.dnn
    Nomeskin_01.00.00_Install.zip
  11. Premete il link prossimo step e incrociate le dita. Se nella pagina successiva trovate un Pallino verde e la dicitura
    The Package was created and can be found in the www.MioSito.it/Install/Skin folder
    Accedendo alla cartella del server via ftp o in locale se lavorate sul vostro server, troverete i 2 files sopra descritti con il vostro skin, potrete quindi farne download e reinstallarlo su altro portale.
    Non ho ancora fatto una prova ma presumo, visto il modo poco chiaro in cui vengono memorizzati i path nel manifest dello skin, che sia necessario modificarne il path di base del portale per installarlo su un diverso portale o impostarlo a _default per installarlo a livello di Host.

Sperando di aver contribuito alla prevenzione delle emicranie da Skinning, vi auguro buon lavoro.

Tags:

posted @ domenica 11 aprile 2010 10.37 | Feedback (1)

giovedì 25 marzo 2010 #

Dotnetnuke suggerimenti post aggiornamento

Ho appena aggiornato il DNN aziendale che funzionava pacificamente dal 2007 con la versione 4.8, l’ho portato alla 05.02.03 (74), senza gravi problemi, è stato davvero semplice dopo aver fatto un test su una macchina locale applicare quanto fatto sulla macchina di rete. ATTENZIONE! se qualcun’altro pigro come me avesse bisogno di fare questo tipo di aggiornamento, è necessario prima passare per la versione 4.09.05 e poi andare alla versione 5 oppure potreste farvi del male. I link per scaricare entrambe le versioni da Codeplex sono accessibili dal portale di DNN e sono poco visibili, seguite con un po’ di fiuto i vari links della pagina di aggiornamento della versione community.

Detto questo, tutto è andato bene salvo 2 cose: io ho i portali bilingue, italiano e inglese e il link delle lingue è sparito. Le icone di sistema sui menu non si vedono perché il loro url è scritto in forma sbagliata.

Il secondo problema mi riservo di sbudellarlo quanto prima aggiungendo una nota a questo post. Per il primo problema, che può portare ad attacchi di panico, la soluzione trovata ovviamente su un forum è semplice ma ben nascosta.

per ogni portale ove serve:

Menu Admin> Languages (attenti che a volte è nascosto oltre i … in fondo al menu.

Sulla pagina languages selezionate dalla combobox ad esempio su English (United States) o su qualsiasi altro linguaggio abbiate installato e tradotto.

click su Edit Language sotto alla combobox nella pagina che appare selezionare la checkbox Enabled e i link ai vari linguaggi appariranno sullo skin oppure verranno listati nella combobox apposita.

Tags: ,

posted @ giovedì 25 marzo 2010 17.59 | Feedback (0)

lunedì 8 marzo 2010 #

Ma dove è finita la cartella di pubblicazione dei plugin DxCore?

Probabilmente è il post dell’idiota del mese, però dato il tempo che ci ho perso…

Ho appena installato Visual Studio 2010 perchè devo preparare le sessioni per la conferenza del 19 marzo prossimo a Pordenone, quando lavoro in un ambiente di sviluppo io devo stare a mio agio, avere i miei quattro plugin funzionanti indipendentemente dal fatto che stia scrivendo codice serio oppure codice demo (non che non sia serio anche quello…) perciò ho deciso di vedere se potevo trasportare i miei bellissimi plugin da una versione all’altra.

Su Visual studio 2008 ho ancora installato Dxcore 3.0.5 (Se funziona perché aggiornarlo?) ma su 2010 non funziona, pertanto ho dovuto installare la versione 10.0.0 beta. Fin qui non ci sono problemi, l’installazione è semplice. Però, dopo l’installazione sono andata a cercare la familiare cartella bin\Plugins ove copiare i vecchi plugin e non c’era più. Ho provato a generarla ma ovviamente non funziona così dopo alcuni minuti di scoramento ho iniziato le ricerche, non è stato facile perché sia con Bing che con Google ho trovato molto poco, però ho trovato quasi per caso il sito dei plugin di community che si trova qui:

http://code.google.com/p/dxcorecommunityplugins/ e dopo varie ricerche sullo stesso in un link secondario ho trovato quello che mi serviva:

Dalla versione 9.1.1 la cartella dei plugin è: C:\Program Files\DevExpress 2009.1\IDETools\Community\Plugins\ ovvero: Cartella di installazione\IDETools\Community\Plugins\.

Dalla versione 9.1.3 la cartella dei plugin è: MyDocuments\DevExpress\IDE Tools\Community\PlugIns ovvero:

Documenti dell’utente corrente\DevExpress\IDE Tools\Community\PlugIns

Per evitare che come me perdiate due ore per installare un plugin, ho deciso di postare il tutto qui, sperando che l’indicizzazione possa aiutarvi a trovare le cose più in fretta. Per inciso, i due plugin più importanti, ovvero Commenter e ClassCleaner, funzionano perfettamente. I plugin di tipo visuale (Evidenziatore del TODO e visualizzatore del Color) non funzionano nel nuovo editor, probabilmente perché i comandi dell’ide WPF sono diversi.

Nel mentre facevo le mie ricerche, sono incappata anche in un altro plugin di Refactoring che trovo molto interessante perché permette di configurare il layout delle proprie classi in modo personalizzato e da delle funzioni molto carine di riorganizzazione del codice, se volete potete provarlo su 2008, per 2010 c’è un workaround ma non funziona ancora perfettamente; si chiama Regionerate e lo trovate qui: http://www.rauchy.net/regionerate/ ovviamente funziona solo in C# mi spiace per gli amici di VB ma sembra che i produttori di plugin per Visual Studio vi trascurino molto…

posted @ lunedì 8 marzo 2010 15.43 | Feedback (2)

giovedì 4 marzo 2010 #

Funzionalità FILESTREAM in SQL server 2008

Fra le novità più importanti di SQL Server 2008, una è certamente la possibilità di utilizzare il file System per la memorizzazione dei dati di tipo BLOB. Pertanto se lavorate con Immagini, File binari ed altro che dovete memorizzare in un database SQL Server, è possibile utilizzare questa funzionalità, importante ad esempio per chi usa SQL Express, perché in questo modo, la zona per i BLOB non occupa spazio utile nei files del database, che sono limitati se non vado errata a 4GB.

Come si attiva questa funzionalità:

  1. Primo passo, attivare la funzionalità a livello di protocolli del server, dal Menu Programmi > Microsoft SQL Server 2008 > Configuration Tools – lanciare SQL Server Configuration Manager (Icona con la cassetta attrezzi rossa).
    • Selezionare SQL Server Services nella treeview a sinistra
    • Sulla finestra di destra selezionare SQL Server (MSSQLSERVER) per l’istanza di default oppure l’istanza nominata.
    • Tasto destro proprietà
    • Sulla finestra c’è una serie di Tab, scegliere FILESTREAM attivare l’accesso per Transact SQL ed eventualmente l’accesso via API e l’accesso da client remoti ( non sono certa sia obbligatorio settarlo se si usa il TSQL ma non ho ancora effettuato test in merito.)
  2. Secondo passo, in SQL Management Studio, collegarsi al server con diritti da Sysadmin, sul Server SQL tasto destro Properties, Selezionare Advanced nella lista sulla sinistra e sulla prima riga della property grid a destra, attivare l’opzione Filestream In modalità Transact SQL o Full Access.
  3. Creare un database che vi permetta di memorizzare dati FILESTREAM:
      USE [master]
      GO
      
      /****** Object:  Database [Paperinik]    Script Date: 03/04/2010 18:09:07 ******/
      IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = N'Paperinik')
      BEGIN
      CREATE DATABASE [Paperinik] ON  PRIMARY 
      ( NAME = N'Paperinik', FILENAME = N'C:\SQL.DIR\Data\Test\Paperinik.mdf' , 
      SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ), FILEGROUP [Binari] CONTAINS FILESTREAM DEFAULT ( NAME = N'Paperinik_Bin', FILENAME = N'c:\sql.dir\data\test\PaperinikFS\paperinik_bin' ) LOG ON ( NAME = N'Paperinik_log', FILENAME = N'C:\SQL.DIR\Data\Test\Paperinik_log.ldf' ,
      SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%) END
      Questo esempio, fatto con il mio database preferito :D crea il database vero e proprio con il file Paperinik.mdf come master data file ed il file Paperinik_log.ldf come file di log. Aggiungo poi un Filegroup che chiamo Binari che definisce la zona relativa ai FILESTREAM, indicando la cartella dove memorizzare i dati, nel mio caso, c:\sql.dir\data\test\PaperinikFS\
  4. Creare una tabella che contenga un campo Varbinary(MAX) con l’opzione FILESTREAM
      USE [Paperinik]
      GO
      
      CREATE TABLE [dbo].[TbFiles](
          [IDFile] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
          [IDFileSerial] [int] NOT NULL,
          [DDFile] [nvarchar](255) NULL,
          [FileData] [varbinary](max) FILESTREAM  NULL,
       CONSTRAINT [PK_TbFiles] PRIMARY KEY NONCLUSTERED 
      (
          [IDFile] ASC
      )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  =     OFF, 
          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON,
          ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
      ) ON [PRIMARY] FILESTREAM_ON [Binari]
      GO
      
      USE [Paperinik]
      /****** Object:  Index [UQ_TbFileSerial]    Script Date: 03/04/2010 17:23:33 ******/
      CREATE UNIQUE CLUSTERED INDEX [UQ_TbFileSerial] ON [dbo].[TbFiles] 
      (
          [IDFileSerial] ASC
      )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
          SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, 
          DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 
          ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY] FILESTREAM_ON [Binari]
      GO
      In questo caso, ho creato una tabella per memorizzare dei file, anche se poi nell'esempio memorizzeremo qualcosa di più facile, per le cose più complesse farò ulteriori test in seguito. Per poter contenere un campo di tipo FILESTREAM è indispensabile che la tabella contenga un ID univoco di tipo GUID (Il campo IDFile) ho anche generato un campo univoco con codice numerico che può divenire un metodo “potabile” per cercare i record, e gli ho quindi assegnato un indice univoco clustered.
  5. Ora non ci resta che Inserire dei dati di test, questi dati possono essere creati in modo semplice usando uno script T-SQL di inserimento:
      INSERT INTO [Paperinik].[dbo].[TbFiles]
                 ([IDFile]
                 ,[IDFileSerial]
                 ,[DDFile]
                 ,[FileData])
           VALUES
                 (NEWID()
                 ,1
                 ,'Nullo'
                 ,null)
      GO
      
      INSERT INTO [Paperinik].[dbo].[TbFiles]
                 ([IDFile]
                 ,[IDFileSerial]
                 ,[DDFile]
                 ,[FileData])
           VALUES
                 (NEWID()
                 ,2
                 ,'Vuoto'
                 ,cast('' as varbinary(max)))
      GO
      
      INSERT INTO [Paperinik].[dbo].[TbFiles]
                 ([IDFile]
                 ,[IDFileSerial]
                 ,[DDFile]
                 ,[FileData])
           VALUES
                 (NEWID()
                 ,3
                 ,'Stringa'
                 ,cast('Questa stringa è binaria' as varbinary(max)))
      GO
      
      INSERT INTO [Paperinik].[dbo].[TbFiles]
                 ([IDFile]
                 ,[IDFileSerial]
                 ,[DDFile]
                 ,[FileData])
           VALUES
                 (NEWID()
                 ,4
                 ,'La quarta'
                 ,cast('Questa riga contiene questa stringa' as varbinary(max)))
      GO
      In questo caso, ho generato quattro righe, contenenti un campo binario nullo, un fantomatico file di testo vuoto, e due files di testo con una stringa al loro interno.
  6.   Cosa troveremo nel nostro file system dopo queste operazioni?
    Verrà generato un sistema di cartelle, sotto alla cartella ROOT  c:\sql.dir\data\test\PaperinikFS\ i cui nomi sono dei GUID, che saranno utilizzate per memorizzare i dati relativi ai nostri VarBinary (MAX) di tipo filestream. Se curiosate nelle sottocartelle, troverete dei file senza alcuna estensione con nomi in stile nnnnnnnn-nnnnnnnn-nnnn che aperti con notepad, visualizzeranno il contenuto del campo varbinary. Non ho ancora provato a memorizzare nella tabella un file binario e vedere cosa troverò, lascio ad un post futuro il compito di rivelarlo.

A che conclusioni ci porta questo piccolo esperimento? Se abbiamo bisogno di memorizzare dati non strutturati in un database, questa può essere una buona soluzione, perché SQL Server fornisce comunque le transazioni, il log delle modifiche e variazioni, con un purge che può essere configurato, la possibilità di attivare l’indicizzazione. La possibilità di fare il backup del filesystem che costruisce come qualsiasi altro filesystem. Ci toglie dall’imbarazzo per il problema del limite a 4GB dei database di SQL Express, ma attenzione, il formato del filesystem non è comunque direttamente accessibile dall’esterno, se si perdono i database, recuperare fisicamente i files binari è possibile, ma sapere cos’è ciascuno dei files recuperati NON è una cosa semplice.

Chi avesse bisogno di indicizzare su database dati destrutturati posti su file system che vuole comunque vedere e poter accedere anche usando il file system stesso, non deve usare questa funzionalità, ma costruire un gestore manuale del file system e memorizzarsi sul database i percorsi dei files che gestisce.

posted @ giovedì 4 marzo 2010 18.41 | Feedback (0)

venerdì 29 gennaio 2010 #

Come cercare del testo nel codice SQL di definizione di tutte le viste

Scenario: C’è un problema su SQL 2005 e SQL 2008 dovuto alle funzionalità di compatibilità con SQL 2000. In SQL 2000 infatti era possibile inserire in una vista una clausola ORDER BY inserendo semplicemente una SELECT TOP 100 PERCENT nella sua definizione.

Ovviamente, essendo una cosa possibile se pure sconsigliata tutti noi l’abbiamo usata in modo massiccio :D, dopo l’SP2 o SP3 in SQL 2005 questa funzionalità disponibile per i database in modalità compatibilità SQL 2000, cessa di funzionare.

L’ho scoperto su un sistema di produzione presso un cliente, dove una piccola funzione sviluppata in VBA su Access, e usata dal 2001 ha improvvisamente cessato di funzionare lo scorso 15 ottobre.

C’è una Hot Fix (fatta dopo SP2 ma non inclusa in SP3) che risolve il problema disponibile qui, ma ovviamente è opportuno trovare tutti i posti in cui si usano queste Order By e provvedere a ordinare le Query dopo la vista anziché dentro la vista.

Dopo aver modificato il VBA Access per risolvere il problema contingente la domanda è stata:

Ma quante viste con una ORDER BY ho ancora in questo vecchio database? (Vecchio ma ovviamente pesantemente usato in quanto è il DB del Gestionale).

Aprire a mano 96 viste è una operazione da suicidio, quindi ho cercato in internet se c’è un modo per cercare dentro l’SQL di definizione degli oggetti di un database ed ho trovato in un forum su stack overflow quello che inserisco qui sotto, in caso serva a qualcun’altro:

SELECT 
    v.name, 
    m.definition 
FROM  
    sys.views v 
INNER JOIN  
    sys.sql_modules m ON v.object_ID = m.object_id 
WHERE m.definition LIKE '%TOP%'

Con questo script posso cercare tutte le definizioni di viste che contengono la parola TOP, ma è facilmente modificabile per cercare qualsiasi stringa in qualsiasi porzione di codice SQL relativo alla definizione di un oggetto in un database.

Grazie a Mark S. per averlo condiviso.

P.s. le viste da modificare sono 72 [sigh! :-((( ]

Technorati Tag: ,,

posted @ venerdì 29 gennaio 2010 12.43 | Feedback (0)

giovedì 31 dicembre 2009 #

Alcuni test per gli script di manutenzione database

Se come me, lavorate con Analisti creativi, succede spesso di dover modificare i database per aggiungere tabelle, cancellare tabelle, modificare tabelle, pertanto visto che è buona norma che se eseguite due volte uno script SQL ci sia un minimo di controlli affinchè non vadano in errore scrivo qui la sintassi base di alcuni dei più comuni controlli di esistenza per gli oggetti database, così anche io quando ne ho bisogno vengo qui e faccio copia ed incolla :P

IF NOT EXISTS (
    SELECT 1 FROM sysobjects WHERE xtype='u' 
       AND name='MyTable')
    BEGIN
       CREATE TABLE Mytable …;
    END

Questo primo pezzettino può anche essere usato togliendo il not per fare un Drop Table.

IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = 
OBJECT_ID('[FK_MyForeignKeyConstraintName]')
AND parent_object_id = OBJECT_ID('[MyTable]')) ALTER TABLE [MyTable] DROP CONSTRAINT [FK_MyForeignKeyConstraintName]

Questo pezzettino va usato quando abbiamo bisogno di cancellare una tabella che ha all’interno delle relazioni, prima si devono cancellare le relazioni, poi la tabella oppure da errore.

IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id=object_id('MyTable') AND name='MyColumnName')
    ALTER TABLE MyTable ADD MyColumnName int NULL;

Questo controllo è opportuno sugli script che aggiungono colonne, ma allo stesso modo, senza NOT può essere usato per cancellare colonne.

posted @ giovedì 31 dicembre 2009 11.28 | Feedback (1)

Copyright © Sabrina C.

Design by Bartosz Brzezinski

Design by Phil Haack Based On A Design By Bartosz Brzezinski