febbraio 2007 - Posts
Da novembre 2006, abbiamo inserito la parte VSTS for DB Pro, nel nostro corso di Visual Studio Team System.
Oggi abbiamo aggiornato anche la scheda tecnica ufficiale, che come sempre viene poi personalizzata in base alle esigenze del cliente finale : http://www.devleap.com/document.aspx?id=3792.
I primi di maggio andrò alla MEDC di Las Vegas dove, fra le altre, si terranno sessioni su Windows Mobile 6.0, .NET CF 3.5 (dovrebbe allinearsi come versione a Orcas).
Le novità sull'SDK per WM 6.0 sono molte ma non stravolgenti; ben più corposo il tema .NET CF 3.5 dove sembra sarà disponibile una versione ridotta di WPF e di Windows Workflow.
Se vi interessa l'argomento potremmo inserire nello spazio riservato alle sessioni Bonus una sessione sul nuovo SDK e sui nuovi device. Se l'argomento interessa possiamo anche valutare di sostituire una macro-sessione del terzo giorno con una sessione più approfondita su Windows Embeddeb CE 6.0 e i relativi SDK.
Il nostro obiettivo è quello di fornire contenuti il più possibile aderenti alle vostre esigenze: per farci sapere il tuo parere scrivete direttamente a me all'indirizzo Roberto@DevLeap.it.
Ricordo a tutti che fra qualche giorno scade la possibilità di iscriversi alla confrenza a prezzo super scontato.
Il 12/2 è uscito il .NET Micro Framework 2.0 SDK destinato a chi sviluppa su quanto conosciamo con il nome di SPOT. Il modello di sviluppo è classico .NET, basato su linguaggio C# (da notare che non c'è VB.NET) e integrato con Visual Studio 2005.
Non esiste un designer di interfaccia per adesso. Come sempre si fa in questi casi vi sottopongo il mio Hello World Tiny CLR di qualche anno fa in versione Micro Framework 2.0.
using
System;
using
Microsoft.SPOT;
using
Microsoft.SPOT.Input;
using
Microsoft.SPOT.Presentation;
using
Microsoft.SPOT.Presentation.Controls;
namespace
MFWindowApplication1
{
public class Program : Microsoft.SPOT.Application
{
public static void Main()
{
Program myApplication = new Program();
Window mainWindow = myApplication.CreateWindow();
GPIOButtonInputProvider inputProvider = new GPIOButtonInputProvider(null);
myApplication.Run(mainWindow);
}
private Window mainWindow;
public Window CreateWindow()
{
mainWindow = new Window();
mainWindow.Height =
SystemMetrics.ScreenHeight;
mainWindow.Width =
SystemMetrics.ScreenWidth;
Text text = new Text();
text.Font =
Resources.GetFont(Resources.FontResources.small);
text.TextContent =
Resources.GetString(Resources.StringResources.String1);
text.HorizontalAlignment = Microsoft.SPOT.Presentation.
HorizontalAlignment.Center;
text.VerticalAlignment = Microsoft.SPOT.Presentation.
VerticalAlignment.Center;
mainWindow.Child = text;
mainWindow.Visibility =
Visibility.Visible;
Buttons.Focus(mainWindow);
return mainWindow;
}
}
Il 12 Febbraio è uscito sul sito MSDN Download una preview del nuovo SDK per Windows Mobile 6, ma dopo qualche ora è stato rimosso. Sembra sia stato uno errore di pubblicazione.
Dovremmo aspettare fino al primo marzo per vederne una versione pubblica e il primo di maggio per vedere la versione completa.
La notizia arriva dal team: http://blogs.msdn.com/windowsmobile/archive/2007/02/14/where-are-the-windows-mobile-6-sdks.aspx
Ho pubblicato un doppio articolo sulle tecniche asincrone in ASP.NET 1.x e 2.0 pubblicato nel 2006 su Computer Programming.
Ecco il link: http://blogs.devleap.com/articolidevleap/archive/2007/02/07/asp-net-2-0-async-techniques.aspx.
Buona lettura
Riprendo quanto segnalato da Paolo nel primo paragrafo del suo ottimo sul problema della rientranza delle chiamate in WF per ribadire il concetto sul numero dei thread utilizzabili in WF.
Lo scheduler di default di Windows Workflow Foundation (DefaultSchedulerService) lavora in asincrono rispetto al thread che avvia l'istanza di workflow dall'host.
Il parametro MaxSimultaneousWorkflows del DefaultSchedulerService determina quanti thread massimi simultanei sono consentiti dallo scheduler. Se il limite è impostato a 3, il DefaultWorkflowSchedulerService può lavorare con 3 thread del thread pool di .NET per eseguire i workflow. Quando viene avviata una nuova istanza di workflow, se i tre thread stanno già eseguendo altre istanze, la nuova istanza viene inserita in una coda interna al DefaultSchedulerService ed eseguita quando un thread dei 3 ritorna disponibile. Questo accodamento avviene anche dopo una sospensione o recupero del workflow tramite il PersistenceService.
I valori di default a prima vista sono molto bassi (un po' come accade per i 25 thread di ASP.NET che sembrano non pochi, ma praticamente inesistenti :-)): il numero, rappresentato da un Int32 è 5 nel caso di macchine mono-processore e calcolato con questo algoritmo nel caso di macchine multi-processore: (int)(5 * Environment.ProcessorCount * .8). A prima vista l'algoritmo restituisce gli stessi risultati di "4 * numeroprocessori", ma visto che si lavora con una regola aritmetica sugli interi non sempre restituisce lo stesso numero della semplice moltiplicazione per 4.
Alzare il numero dei thread sembra essere la soluzione per far girare più workflow in contemporanea, ma è bene ricordare che:
1) Host in ASP.NET: anche ASP.NET ha bisogno di thread del thread pool di .NET per poter servire le richieste che arrivano. Alzare quindi il numero di workflow contemporanei, se da una parte può migliorare il parallelismo fra workflow, sicuramente fa calare drasticamente il numero di thread disponibili in asp.net per servire le varie richieste all'applicazione per le varie pagine. Non tutte le pagine tra l'altro utilizzeranno workflow e quindi otteniamo un rallentamento generale dell'applicazione ingiustificato: una pagina velocissima da eseguire deve aspettare che si liberi un thread del thread pool per poter essere accolta. Inoltre è probabile che la singola richiesta per una pagina ASP.NET (una insert, un'update o una lettura di dati) sia più veloce dell'esecuzione di un flusso di operazioni di un workflow, non perchè il workflow rutime di per se sia più lento (anche se un po' di overhead lo aggiunge, ma ritengo che i benefici vadano ben oltre), ma perchè probabilmente in un workflow dobbiamo eseguire varie operazioni con una certa logica.
Questo problema non è confinato solamente a Windows Workflow Foundation: i generale tutte le operazioni complesse in ASP.NET occupano un thread del thread pool per molto tempo. Se ci mettiamo anche 10 thread, ad esempio, per lo scheduler del workflow (sui 25 totali di default) abbiamo veramente pochi thread a disposizione per le pagine.
Per una trattazione approfondita delle tematiche ASP.NET e l'esecuzione delle pagine in modo asincrono si veda l'articolo da me pubblicato su Computer Programmin nel 2006. E' diviso in due parti: Parte 1 e Parte 2.
Queste problematiche riguardano anche l'esecuzione di Workflow su un application server che condivide le richieste a più client.
2) Host fuori da ASP.NET e Application Server. Anche se non ospitiamo il workflow runtime in applicazioni server side, alzare il numero di workflow contemporanei, può non essere una buona idea.
Il motivo è lo stesso: questi thread vengono recuperati dal thread pool di .NET, lo stesso thread pool che viene utilizzato da altre operazioni nel codice. Ad esempio se usiamo un oggetto Transaction (manuale o automatico dal TransactionScope) nel codice del workflow o anche nel codice dell'host, questo signore utilizza il thread pool di .NET, di conseguenza andiamo a pestare i piedi (nel senso di cercare di utilizzare) allo stesso pool di thread utilizzato dai workflow: in pratica andiamo gestire le transazioni (che credo importanti :-)) aspettando che si liberi un thread alla fine di un workflow solo perchè vorremmo vedere tanti workflow in contemporanea. Il problema è enorme se pensate che le transazioni hanno (E DEVONO AVERE) un timeout...non è bello avere dei rollback per timeout solo perchè ci piace vedere il Concurrent Workflow ad un valore alto pensando che significhi alte prestazioni.
Come sempre un sistema deve esere guardato nel suo complesso, altrimenti toccando le singole impostazioni per migliorare una delle sue parti, si rischia di rallentare TUTTO.
Una "chicca" finale: sapete con cosa viene gestita dietro le quinte (ed è giusto che sia così) la persistenza di un workflow su DB ? Con un oggetto Transaction :-)
Quindi diventa facile andare in timeout durante la persistenza di workflow (con conseguente retry probabile che necessita sempre di un thread libero del thread pool) se aumentiamo il numero di workflow contemporanei. L'espressione tecnica per indicare questo "stallo" è: Il cane che si morde la coda.
La storia potrebbe essere questa:
Ho una applicazione ASP.NET che usa WF: spesso non riesco a eseguire quanti workflow vorrei. Alzo il numero di istanze possibili (MaxSimultaneousWorkflows a 20). Se chiamo i 20 WF da un singolo client vado come i fulmini. Penso di aver risolto il problema. Se non provo sotto carico (bastano 3/4 richieste concorrenti) non mi accorgo di niente. Poi gli utenti si lamentano della lentezza...vedo che ASP.NET ha solo 25 thread usabili e alzo il valore a 100. Penso di aver risolto...ma ho tanti thread switch che causano operazioni onerose e mi si rallenta ulteriormente il tutto...Alla fine compro un server da 200K così non ho problemi :-)...si ma per gestire un carico che potrebbe essere gestito da un server di fascia molto più bassa.
La soluzione necessita di attenzione su tutte le varie componenti: ormai i software sono molto legati l'uno all'altro e occorre fare molta attenzione nello spostare qualche flag isolato di una delle componenti. Ad esempio, se il nostro DB fa di tutto, anche le analisi comparative sul fatturato dei tre anni precedenti con query da 18 Join (magari senza indici perchè il DB lo abbiamo ereditato da sviluppatori precedenti), non possiamo pretendere che la nostra applicazioni vada veloce su quel db anche se il codice ASP.NET è performance e i thread sono pochi. Se appoggiamo i Workflow sullo stesso DB come persistenza probabilmente peggioriamo il tutto: sono tante le operazioni che il PersistenceService deve fare sul DB, soprattutto per applicazioni ASP.NET dove ogni richiesta è una toccata e fuga; il WF deve essere persistito molte volte in una singola operazioni di un utente. Magari abbiamo deciso di usare anche il TrackingService per tracciare tutto quello che ci serve...e magari lo abbiamo fatto sullo stesso DB peggiorando anche le statistiche di cui sopra.
Le soluzioni ci sono e constano nel calibrare bene i parametri. Molto spesso basta spostare un po' i carichi di lavoro, magari usando servizi di accodamento (MSMQ, Sql 2205 Broker Service, Oracle Advanced Queuing, MQSeries) rendendo il più possibile asincrono il lavoro già a partire da pagine .aspx e service .asmx, .svc, self-hosted di WCF. Su questi argomenti ci confronteremo a DevCon dove, ad oggi, in agenda abbiamo pesato ad una sessione tripla su queste tecniche. Il titolo della sessione dovrebbe essere esplicativo: Server-side Async: With some tricks your 5K Server can perform as a 40K server. Per il contenuto: http://devcon2007.devleap.com/sessioni.aspx#ALL01.
Nota conclusiva: esiste anche un ManualWorkflowSchedulerService, che lavora in sincrono risolvendo molti dei problemi accennati da Paolo nel suo post e da me in questo post. In alcuni post sul web si indica questa come soluzione. NON E' DETTO CHE SIA LA SOLUZIONE, ANZI, ma ne parliamo un'altra volta....
Articolo da me pubblicato su Week.it il 21/11/2006
Visual Studio 2005 offre vari template per facilitare la creazione di progetti. Per esempio creando un progetto di tipo Window Form (Windows Application) viene creata una solution e un progetto che contiene una prima Form, un file di configurazione, un file assemblyinfo.cs, un file di risorse e, non ultime, le reference più comuni per un progetto di questo tipo.
Nel caso di creazione di una Device Application viene, invece, preparato il terreno per iniziare a creare una soluzione mobile con tanto di reference verso le librerie del .Net Compact Framework.
In pratica, con un template di progetto si ottiene un nuovo progetto con alcuni item predefiniti. Vs 2005 espone anche un menu per aggiungere item rilevanti per il progetto: per esempio, a un progetto mobile non si può aggiungere (per ora) un form Xaml, ma si può aggiungere un file .sdf.
Alcuni item fanno partire un wizard di configurazione di alcuni parametri: per continuare con l’esempio, aggiungendo un file .sdf parte il wizard di configurazione del Data Source. I template di progetto e singolo item facilitano l’uso e la creazione degli elementi del progetto. I Guidance Package estendono queste funzionalità fornendo “ricette” (recipes) preconfigurate per eseguire determinate azioni, facilitando l’uso di determinate librerie o fornendo wizard per la creazione guidata d’elementi multipli: per esempio si può creare un form con il pattern Mvp (Model View Presenter) in un passo singolo e ottendeno la classe del form, il suo presenter e la relativa configurazione. I nuovi Software Factory, disponibili su Msdn, vengono di solito accompagnati da un Guidance Package che appunto consente d’automatizzare, in modo predefinito, le operazioni più comuni del Software Factory stesso. Anche in questo caso il pacchetto contiene un Guidance Package con varie ricette per la creazione di elementi che usano il Software Factory.
Per fare in modo che Visual Studio possa utilizzare Guidance Package è necessario installare il Guidance Automation Extension. Si tratta di un componente di runtime per Visual Studio 2005 (a partire dalla versione Standard), disponibile per adesso solo in inglese, che ne estende le funzionalità.
Una volta installate le estensioni nel menu Tools di Visual Studio troviamo il Guidance Package Manager che consente di visualizzare, abilitare, disabilitare i vari Guidance Package nonchè di lanciare le singole ricette per la creazione guidata di elementi.
I Guidance Package vengono forniti in formato .msi o sorgente. Nel primo caso è sufficiente lanciare il .msi per ottenere il package nel Guidance Package Manager, nel secondo occorre installare anche il Guidance Automation Toolkit, lo strumento per creare e debuggare Package.
Tutte le volte si che porta in produzione una modifica di schema viene utilizzato dietro le quinte lo Schema Compare, strumento che appunto esegue una comparazione fra gli oggetti del progetto rispetto al database indicato.
Se, durante lo sviluppo, si aggiunge un campo ad una tabella e si esegue successivamente un Refactor (operazione che capita spesso quando si progettano aggiunte ad un db esistente: "aggiungo intanto il placeholder e poi decido i nomi") non si incorre nel problema di DROP table che affligge il Refactor dei campi. Questo perchè appunto lo Schema Compare vede solo le differenze fra progetto e target DB. Il risultato quindi è una ALTER TABLE per l'aggiunta del campo.
In questo caso ho aggiunto il campo DescrizioneAggiuntiva per poi eseguire un Refactor su DescrizioneAggiuntivaEx. Al termine ho creato lo script di update.
/*
This script was created by Visual Studio on 02/02/2007 at 12.53.
Run this script on peppe.SalamiManagement.dbo to make it the same as IntroVSTSDBPRO02.
Please back up your target database before running this script.
*/
GO
SET
NUMERIC_ROUNDABORT
OFF
GO
SET
ANSI_PADDING
ON
GO
SET
ANSI_WARNINGS
ON
GO
SET
CONCAT_NULL_YIELDS_NULL
ON
GO
SET ARITHABORT ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET
ANSI_NULLS
ON
GO
IF EXISTS
(
SELECT *
FROM tempdb..sysobjects
WHERE id=
OBJECT_ID(
'tempdb..#tmpErrors'))
DROP TABLE #tmpErrors
GO
CREATE TABLE
#tmpErrors (Error
int)
GO
SET
XACT_ABORT
ON
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRANSACTION
GO
PRINT
N
'Altering [dbo].[tabSalami]'
GO
ALTER TABLE
[dbo].[tabSalami]
ADD
[SalameDescrizioneAggiuntivaEx] [dbo].[udtDescrizione] NULL
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
IF EXISTS
(
SELECT *
FROM #tmpErrors)
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT>0
BEGIN
PRINT
'The database update succeeded'
COMMIT TRANSACTION
END
ELSE PRINT
'The database update failed'
GO
DROP TABLE
#tmpErrors
GO
Mi è arrivata questa domanda da uno dei nostri blogger: ho 120 aggregate view per un mio post ma sulla home dei blog vedo solo 22 View. Come mai ?
La pagina http://blogs.devleap.com riporta come View solo le visite online e non tiene conto degli Aggregate View eseguiti con RSS Reader.
Dopo il post di ieri sul rename di un campo, ecco come si comporta VSTS for DBPro nel caso di aggiunta di un campo ad una tabella. In questi giorni, sto testando VSTS for DB Pro in vari scenari per capire come si muove dietro le quinte.
Nel caso specifico ho aggiunto il campo DescrizioneAggiuntiva (usando un UDT esistente) alla tabella tabSalami (chi ha partecipato al VSTS Day a settembre si ricorderà questa tabella interessantissima :-)).
In questo caso viene generato una ALTER TABLE e quindi non soffriamo il DROP&RECREATE.
Il commento iniziale dice tutto: applica questo script al DB vero per renderlo identico a quanto presente nel progetto disconnesso.
/*
This script was created by Visual Studio on 02/02/2007 at 12.00.
Run this script on peppe.SalamiManagement.dbo to make it the same as IntroVSTSDBPRO02.
Please back up your target database before running this script.
*/
GO
SET
NUMERIC_ROUNDABORT
OFF
GO
SET
ANSI_PADDING
ON
GO
SET
ANSI_WARNINGS
ON
GO
SET
CONCAT_NULL_YIELDS_NULL
ON
GO
SET ARITHABORT ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET
ANSI_NULLS
ON
GO
IF EXISTS
(
SELECT *
FROM tempdb..sysobjects
WHERE id=
OBJECT_ID(
'tempdb..#tmpErrors'))
DROP TABLE #tmpErrors
GO
CREATE TABLE
#tmpErrors (Error
int)
GO
SET
XACT_ABORT
ON
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRANSACTION
GO
PRINT
N
'Altering [dbo].[tabSalami]'
GO
ALTER TABLE
[dbo].[tabSalami]
ADD
[SalameDescrizioneAggiuntiva] [dbo].[udtDescrizione] NULL
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
IF EXISTS
(
SELECT *
FROM #tmpErrors)
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT>0
BEGIN
PRINT
'The database update succeeded'
COMMIT TRANSACTION
END
ELSE PRINT
'The database update failed'
GO
DROP TABLE
#tmpErrors
GO
Una delle numerose e interessanti feature di VSTS for DB Pro consente di modificare gli oggetti database direttamente nel progetto per poi generare lo script di update che può essere applicato al DB reale o di test.
Inoltre VSTS for DB Pro consente di effettuare Refactoring consistente su oggetti DB. Ad esempio si può rinominare un campo di una tabella ottenendo in automatico il rename del campo in tutti gli oggetti db che in qualche modo hanno a che fare con il campo stesso (stored procedure, function, trigger, indici, relazioni etc).
Esiste però un problema (a cui sto cercando un workaround semplice e indolore..vi farò sapere) nello script generato per effettuare la modifica. Ad esempio: rinominando un campo si ottiene il seguente script (ho preso una tabella di un mio famoso DB di Demo).
Ho rinominato il campo Nome in CampeggioNome.
BEGIN TRANSACTION
GO
PRINT
N
'Dropping constraints from [dbo].[Campeggi]'
GO
ALTER TABLE
[dbo].[Campeggi]
DROP CONSTRAINT [PK_Campeggi]
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
PRINT
N
'Dropping constraints from [dbo].[Campeggi]'
GO
ALTER TABLE
[dbo].[Campeggi]
DROP CONSTRAINT [MSmerge_df_rowguid_F7AE60381A2441C8BA14010D6380F219]
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
DROP INDEX
[MSmerge_index_661577395]
ON [dbo].[Campeggi]
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
PRINT
N
'Rebuilding [dbo].[Campeggi]'
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SET
XACT_ABORT
ON
GO
BEGIN TRANSACTION
CREATE TABLE
[dbo].[tmp_ms_xx_Campeggi]
(
[IdCampeggio] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[CampeggioNome] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[rowguid] [uniqueidentifier] NOT NULL ROWGUIDCOL CONSTRAINT [MSmerge_df_rowguid_F7AE60381A2441C8BA14010D6380F219] DEFAULT (newsequentialid())
) ON [PRIMARY]
INSERT INTO
[dbo].[tmp_ms_xx_Campeggi]([IdCampeggio], [rowguid])
SELECT [IdCampeggio], [rowguid]
FROM [dbo].[Campeggi]
DROP TABLE
[dbo].[Campeggi]
EXEC
sp_rename N
'[dbo].[tmp_ms_xx_Campeggi]', N
'Campeggi'
COMMIT TRANSACTION
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
PRINT
N
'Creating primary key [PK_Campeggi] on [dbo].[Campeggi]'
GO
ALTER TABLE
[dbo].[Campeggi]
ADD CONSTRAINT [PK_Campeggi]
PRIMARY KEY CLUSTERED ([IdCampeggio])
ON [PRIMARY]
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
PRINT
N
'Creating index [MSmerge_index_661577395] on [dbo].[Campeggi]'
GO
CREATE UNIQUE NONCLUSTERED INDEX
[MSmerge_index_661577395]
ON [dbo].[Campeggi] ([rowguid])
ON [PRIMARY]
GO
IF
@@ERROR<>0
AND @@TRANCOUNT>0
ROLLBACK TRANSACTION
GO
IF
@@TRANCOUNT=0
BEGIN INSERT INTO #tmpErrors (Error)
SELECT 1
BEGIN TRANSACTION END
GO
Come si nota vengono dapprima rimossi indici, constraint e trigger per poi
1) Creare una tabella temporanea con la nuova struttura
2) Importare i dati dalla tabella originale nella tabella temporanea
3) Eseguire un DROP della tabella originale
4) Rinominare la tabella temporanea con il nome originale
Questa operazione, è impossibile da eseguire se la tabella è stata oggetto di una pubblicazione. Quando una tabella è stata inserita in una Publication di una replica di tipo merge non può essere rimossa dal DB se non dopo aver tolto il riferimento nella pubblicazione.
Quindi l'aggiornamento automatico da DB Professional non può essere effettuato e occorre modificare lo script generato per tutte le tabelle sotto Merge Replication. In pratica lo stesso risultato che si ottiene da Management Studio o dall'Enterprise Manager.