Dienstag, 18. Dezember 2012

Xpert.Ivy Query Interface für Tasks und Cases

Bin gerade auf das "fluent" Query-Interface gestossen um Tasks und Cases zu suchen.

Hier ein Beispiel um alle Tasks in einem bestimmten Status zu finden:

import ch.ivyteam.ivy.workflow.TaskState;
import ch.ivyteam.ivy.workflow.ITask;
import ch.ivyteam.ivy.workflow.query.TaskQuery;

TaskQuery query = ivy.wf.getTaskQueryExecutor().createTaskQuery()

   .where().state().isEqual(TaskState.FAILED)

   .orderBy().name();

List<ITask> results = ivy.wf.getTaskQueryExecutor().getResults(query);
for(ITask task : results) {
    // Do something
}


Zum Vergleich das "findTasks"-API:

import ch.ivyteam.ivy.workflow.PropertyOrder;
import ch.ivyteam.ivy.workflow.IPropertyFilter;
import ch.ivyteam.ivy.workflow.TaskState;
import ch.ivyteam.ivy.workflow.ITask;
import ch.ivyteam.ivy.persistence.IQueryResult;

IPropertyFilter filter = ivy.wf.createTaskPropertyFilter(
    ch.ivyteam.ivy.workflow.TaskProperty.STATE,
    ch.ivyteam.logicalexpression.RelationalOperator.EQUAL,
    TaskState.FAILED);
List<PropertyOrder> order;
order.add(new PropertyOrder(ch.ivyteam.ivy.workflow.TaskProperty.NAME));

IQueryResult result = ivy.wf.findTasks(filter,order,0,-1,true);

for(ITask task : result.getResultList()) {
    // Do something
}


https://sourceforge.net/p/xivy/wiki/IvyCookbook/
http://www.ivyteam.ch/downloads/XIVY/4.3.0/doc/PublicAPI/ch/ivyteam/ivy/persistence/query/package-summary.html


Freitag, 14. Dezember 2012

Smalltalk Cheat Sheet

Cincom hat eine kurze Übersicht zu Smalltalk veröffentlicht:

"Cincom Smalltalk – The Language on Two Pages"


SQLServer log keeps growing

Recently our database log was growing bigger and bigger on our main development database. The DBA just blamed the developers because he meant they were not closing transactions correctly.

But DBCC OPENTRAN did not display any open transactions, but instead reported that SQLServer tried to replicate transactions, also we did not have any replication configured for this database.
DBCC OPENTRAN
Transaktionsinformationen für die ...-Datenbank.

Replizierte Transaktionsinformationen:
Älteste verteilte LSN : (0:0:0)
Älteste nicht verteilte LSN : (214984:95479:1)
Using fn_dblog I found the statement which belonged to the mentioned LSN (214984:95479:1 is in HEX 347C8:174F7:1)
select top 100 [Current LSN], [Begin Time], [Transaction Name], [AllocUnitName] from ::fn_dblog( null, null)

000347c8:000174f7:0001 2012/11/25 14:31:18:997 CheckDb NULL
000347c8:000174f7:0002 NULL NULL Unknown Alloc Unit
000347c8:000174f7:0003 NULL NULL ...
I found some information in the SQLServer Logs at this time that showed that CHECKDB did have some problems at this time, maybe it was even crashing, probably leaving the database in a problematic state.

Unfortunately I do not know how our DBA solved the problem, but at least he stopped blaming the developers ;-)

You will find some informations on the web about similar problems, search Google for sp_removedbreplication or sp_repldone.

This query may also help to find out why log space is not reused:
select name, log_reuse_wait, log_reuse_wait_desc from sys.databases
Also check out these links:

Mittwoch, 28. November 2012

System Integration mit Xpert.Ivy: Intermediate Events über Dateien

Für die asynchrone Kommunikation zwischen Xpert.Ivy und externen Systemen innerhalb eines Prozesses stellt Xpert.Ivy Intermediate Events zur Verfügung.

Ein mögliches Szenario könnte wie im folgenden Bild sein. Eine Geschäftsapplikation sendet eine Meldung an Xpert.Ivy um einen Prozess zu starten. Im Laufe des Prozesses sendet Xpert.Ivy an ein Druckoutput System eine Anfrage zum Aufbereiten und Versenden eines Formulares. Anschliessend wartet der Prozess auf eine Meldung vom DMS System, dass das Formular zurückgesendet und archiviert wurde.




Und so könnte das in Xpert.Ivy aussehen:





Wie  ein Prozess Start über eine Datei funktionieren kann, habe ich in diesem Post beschrieben:
System Integration mit Xpert.Ivy: Event Start über Dateien

Intermediate Events

Jedem Intermediate Event muss in Xpert.Ivy eine eindeutige ID, genannt "Event ID", zugewiesen werden. Dies kann die Ivy Case ID sein, oder eine andere eindeutige ID.Wenn man die Case ID nimmt, muss man während dem Entwickeln mit dem Designer etwas aufpassen, da nach jedem Start der Engine wieder mit der Case ID "1" begonnen wird.

Im oben beschriebenen Szenario würde beim Print-Request bereits die gleiche Event-ID an das Druck-System übergeben. Dieses könnte die Event-ID als Strichcode auf ein Formular drucken, welcher dann wenn das Formular zurückkommt, vom Scanner bzw. Dokument Management System gelesen und wieder in die Event-ID umgewandelt wird. Diese wird dann vom DMS wieder an Xpert.Ivy zurückgegeben, wo so die Event-ID wieder einem bestimmten Prozess zugeordnet werden kann.

Hier das etwas überarbeitet Szenario.


Xpert.Ivy liefert die Klasse FileIntermediateEventBean aus, welche wir uns im folgenden etwas genauer ansehen.

FileIntermediateEventBean

Die Klasse FileIntermediateEventBean nimmt eine Datei im folgenden Format entgegegen:

Attribute1=Wert1,Attribute2=Wert2,...

Die Werte können über die Ivy Variable "result" im Output-Tab des Intermediate Events abgefragt werden.

Beispiel:


Mittwoch, 14. November 2012

Smalltalk versus Java

Als Entwickler im Umfeld von Geschäftsapplikationen mit Smalltalk und Java Erfahrung möchte ich hier einen kleinen Vergleich zwischen den beiden Sprachen bzw. Plattformen machen.

Die Geschichte von Smalltalk beginnt irgendwann in den 70er Jahren, wobei es erst in den 80er und 90er Jahren vermehrt Verbreitung fand, um dann mit dem Aufstieg von Java wieder Marktanteile zu verlieren. Heute sind Smalltalk-Projekte (zumindest in der Schweiz) ziemlich selten geworden.

Hier noch ein Zitat zu den Ursprüngen von Java:
Java. The elegant simplicity of C++. The blazing speed of Smalltalk.
http://c2.com/cgi/wiki?SmalltalkMinusMinus
Zum beginnen einige Gemeinsamkeiten von Smalltalk und Java, quer über Sprache und Plattform:
  • Beide sind Objekt-Orientiert
  • Beide haben eine grosse Klassenbibliothek z.Bsp. für Collections
  • Beide laufen in einer Virtual Machine und verwenden ein Garbage Collector
  • Viele Smalltalk VM's laufen wie die JVM auf mehreren Betriebssystemen
Und hier einige Unterschiede:
  • Smalltalk verwendet keine statische Typisierung
  • Smalltalk hat nur sechs Key-Wörter, alles andere wird über Klassen, Objekte, Methoden und Blöcke (entspricht in etwa den Closures in Java 8) abgebildet
  • Smalltalk verwendet sogenannte Images anstatt Dateien
  • In Smalltalk bilden Sprache, Runtime und IDE eine stärkere Einheit. Dafür gibt es nicht wirklich einen Hersteller übergreifenden Standard, ein Wechsel der IDE ist praktisch nicht möglich
In der täglichen Arbeit wirken sich diese Unterschiede mal weniger, mal mehr aus.

Statische versus Dynamische Typisierung

Als Nachteil von dynamischer Typisierung wird häufig angeführt, dass dadurch zur Laufzeit ein falsches Objekt verwendet werden könnte. In der praktischen Arbeit kommen solche Fehler aber extrem selten vor, wenn überhaupt. Viel häufiger tretten aber auch in Smalltalk folgende Probleme auf: Null Pointer Exceptions.

Umbenennen von Klassen oder Methoden geht in Java etwas einfacher (vorausgesetzt die ganzen Verweise liegen im Projekt oder Workspace vor), weil beim Compilieren Fehler auftreten. Dafür spart man in Smalltalk einiges an Zeit weil es keine merkbaren Compilierzeiten gibt.

Sprache und Klassenbibliothek

Was einem als Smalltalk Entwickler beim Einstieg in Java vermutlich als erstes auffällt, ist der grosse Aufwand den es zu Beginn braucht um nur einmal sämtliche Schlüsselwörter und Konstrukte der Java Sprache zu erlernen. Während sich ein Java Entwickler also noch mit der Sprache herumschlägt, kann sich ein Smalltalk Entwickler bereits in die Klassenbibliothekt einarbeiten. Bzw. er muss, da in Smalltalk nun mal sämtliche Konstrukte wie IF-Statements und Loops in Form von Klassen und Methoden vorliegen. Ansonsten finden sich praktisch alle Klassen in Smalltalk auch in Java wieder oder umgekehrt, hier als Beispiel ein Auszug aus den Collections-Klassen:

SmalltalkJava
DictionaryMap
IdentityDictionaryHashMap
CollectionList
OrderedCollectionArrayList
Array(primitive Arrays)
SetSet
IdentitySetHashSet
usw.


Tools und IDE

In Smalltalk findet der grösste Teil der Entwicklung im Class-Browser statt. Im Gegensatz zu Java IDEs wie Eclipse, wo Editierung und Browsing getrennt sind, bildet dies in Smalltalk immer eine Einheit. Die Klassen werden nicht primär in einzelnen Dateien gespeichert sondern in einem gemeinsamen Image. Dies macht die Entwicklung mit Smalltalk sehr effizient.

Im Gegensatz dazu ist es in keiner mir bekannten Smalltalk Umgebung möglich, verschiedene Arten von Resourcen oder gar Sprachen über die gleiche IDE zu editieren.

Class-Browser in VisualWorks - Browsen und Editieren gleichzeitig

Smalltalk oder ...?

Die Sprache Smalltalk ist einfacher als Java aber trotzdem sehr mächtig ist und wirkt dank der konsequenten Umsetzung der Konzepte immer noch modern. Im direkten Vergleich zu Smalltalk (oder auch C#) wirkt Java (die Sprache) für mich eher schwerfällig und veraltet. Alternative Sprachen bringen zwar einige der Vorteile dynamischer Sprachen auch auf die Java Plattform, die IDE Unterstützung scheint mir aber bei Smalltalk immer noch einiges komfortabler zu sein.

OO-Konzepte, Exception-Handling und die Klassenbibliotheken sind ein weiten Teilen vergleichbar.
Das grösste Problem im Smalltalk Umfeld ist die sehr kleine Community, die eher beschränkten Resourcen (Entwickler) seitens der Smalltalk Hersteller und dass es kein wirklicher Standard gibt und nur sehr wenige Hersteller übergreifende Frameworks. Diese Punkte dürften schlussendlich doch für Java sprechen.

Es ist zu hoffen, dass sich noch einige der Ideen und Konzepte von Smalltalk in die Java oder andere Plattformen retten können und dann vielleicht irgendwann zu Mainstream-Technologien werden. Ich bin aber ziemlich sicher, dass dies noch einige Jahr(zehnt)e dauern wird.

http://www.cincomsmalltalk.com
http://www.instantiations.com/
http://www.object-arts.com/

Donnerstag, 8. November 2012

ObjectStudio 8.4 ListView extensions

Some years ago I implemented some ListView extensions using WindowProc subclassing and primitives. While testing the new ObjectStudio Release 8.4.1 I found that registering our own WindowProc lead to crashes. I originally used the CHookWnd class, but because this class was marked obsolete by its author I had to find another way.

Using CWindowImpl I was now able to create a first working demo again.

Here is the primitive method which I use to register my own WindowProc for a ListView. It gets called from Smalltalk right after the ListView opens:

 BOOL LV_Init() {

     INIT_LOCAL_GVARS;
    OPTR oSelf;
    oSelf = PTOSn(0);

    // Get CPP handle of ListView form item
    OPTR oCppHandle = GetObjVar(oSelf,GetInstVarOffset(oSelf,SymbolHash((PSZ)_T("cppHandle"))));
    if (IsNil(oCppHandle)) return FALSE;

    // Check if handle is valid and get a reference to the MFC CListCtrl object
    if(ObjHandle(oCppHandle) == NULL) return FALSE;
    if(!AfxIsValidAddress((void*)ObjHandle(oCppHandle),sizeof(CListCtrl), true)) 
                 return FALSE;
    CListCtrl* m_pCtrl = (CListCtrl*)ObjHandle(oCppHandle);
    if(!m_pCtrl) return FALSE;


    HWND hwnd = m_pCtrl->GetSafeHwnd();
    if (hwnd == NULL) return FALSE;

   // New Code to Subclass the WindowProc instead of CHookWnd
    CMyListViewEx *myListViewEx = new CMyListViewEx();
    BOOL result = myListViewEx->SubclassWindow(hwnd);


    return result;
}

And this is the extension demo class, printing out middle mouse buttons down events to the transcript:

class CMyListViewEx : public CWindowImpl<CMyListViewEx,CListCtrl>
{
public:

    BEGIN_MSG_MAP(
CMyListViewEx)
        MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMButtonDown)
    END_MSG_MAP()

    LRESULT OnMButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {

       INIT_LOCAL_GVARS;
       Out(_T("MBUTTONDOWN %i %i %i"), uMsg, wParam, lParam);

       bHandled = TRUE;
       return TRUE;
    }

   
CMyListViewEx(void);
    ~
CMyListViewEx(void);
   
    void OnFinalMessage(HWND hWnd) {
        UnsubclassWindow(TRUE);
        delete this;
    }
}


If we ever finish porting from ObjectStudio 7 to 8 I may contribute our extensions to the Cincom public Store repository.

See also:

Donnerstag, 18. Oktober 2012

Xpert.Ivy Subversion Einstellungen

Ich hatte heute das Problem, dass bei einer neuen Installation vom Ivy Designer der SVN Benutzer und Passwort nicht gespeichert werden konnten und daher bei jedem SVN Zugriff wieder  Benutzer/Passwort abgefragt hat.

Subclipse (die in Xpert.Ivy verwendete Subversion Implementierung) speichert einige Daten im Workspace ab, einige liest es aus der Subversion Konfiguration im Benutzer Profil.

Die SVN-Benutzer-Angaben werden unter Windows im Benutzer Profil unter AppData gespeichert, zum Beispiel:

C:\Users\(User)\AppData\Roaming\Subversion\auth\svn.simple\

Nachdem wir die Datei in diesem Verzeichnis gelöscht haben, konnte Subclipse Benutzer/Passwort wieder speichern.

Die Daten im Workspace werden abgelegt unter:

workspace\.metadata\.plugins\org.tigris.subversion.subclipse.core\


Defaults von Ivy Workspace Einstellungen ändern

Nach der Installation vom Ivy Designer oder nach dem Erstellen eines neuen Workspaces musste ich bis jetzt immer einige der Einstellungen erneut anpassen, oder ich habe die Einstellung ganz vergessen. Daher habe ich heute nach Wegen gesucht, wie sich das vereinfachen lässst.

Die Ivy- und anderen Eclipse-Workspace Einstellungen sind im Workspace im folgendem Verzeichnis abgelegt:

(workspace)\.metadata\.plugins\org.eclipse.core.runtime\.settings

Erstellt man einen neuen Workspace, kann man diese Dateien in den neuen Workspace kopieren.

Möchte man die Einstellungen nicht jedesmal kopieren, kann man die Default Einstellungen auch in der folgenden Datei anpassen:

(XpertIvyDesigner)\plugins\ch.ivyteam.ivy.designer.branding_4.3.0\plugin_customization.ini

Hier einige Beispiele für diese Datei:

ch.ivyteam.ivy.designer.ide/VISIBILITY_LEVEL=EXPERT
ch.ivyteam.ivy.designer.ide/ANIMATION_FOLLOWING_MODE=FOLLOW_ONLY_OPEN_PROCESSES
org.eclipse.core.resources/encoding=UTF-8
org.eclipse.ve.java.core/SWING_LOOKANDFEEL=my.look-and-feel


Das Format der Einträge in dieser Datei ist:

plugin-name/configuration=value

Wie die Einstellungen heissen findet man am einfachsten heraus indem man die Settings-Dateien im Workspace (.metadata\.plugins\org.eclipse.core.runtime\.setting) in einem Text-Editor öffnet. Sind dort noch keine vorhanden, kann man die Einstellung im Ivy Designer ändern und dann den Designer beenden, dann sollten die Einstellungen in die Dateien geschrieben werden.

Der Plugin-Name entspricht normalerweise der Settings-Datei ohne die Endung "prefs". Man kann den Plugin-Namen aber auch verifizieren wenn man im Designer die Einstellungsseite öffnet und dann Alt-Shift-F1 drückt (Plugin Spy).

Als Beispiel hier den Inhalt der Datei ch.ivyteam.ivy.designer.ide.prefs im:

#Thu Oct 18 13:32:13 CEST 2012
VISIBILITY_LEVEL=EXPERT

ANIMATION_FOLLOWING_MODE=FOLLOW_ONLY_OPEN_PROCESSES
eclipse.preferences.version=1


Es sollte auch möglich sein eine eigene plugin_configuration.ini Datei über die Commandline mittels -pluginCustomization anzugeben.

Einige Einstellungen lassen sich auch direkt über die Commandline ändern, z.Bsp. das Encoding:

-Dfile.encoding=UTF-8

Die Commandline Argumente können in der Datei "Xpert.Ivy Designer.ini" auf einer neuen Zeile eingefügt werden:

-startup
plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-nl
en
-vmargs
-Xms128m
-Xmx768m
-XX:MaxPermSize=192m
-Dosgi.parentClassloader=ext
-da
-Dsun.java.command=XpertIvyDesigner
-Dfile.encoding=UTF-8


Daneben  gibt es noch die Möglichkeit über das Export/Import-Menu Einstellungen zu exportieren/importieren.

Allerdings konnte ich bis jetzt nicht alle Einstellungen mit oben genanntem Weg übernehmen, z.Bsp. muss ich die SVN-Repositories (workspace\.metadata\.plugins\org.tigris.subversion.subclipse.core\.svnProviderState) immer noch von Hand kopieren oder erfassen.

Links

Donnerstag, 27. September 2012

System Integration mit Xpert.Ivy: Event Start über Dateien

Xpert.Ivy bringt von Haus aus bereits Möglichkeiten mit, andere Systeme über eine Datei basierte asynchrone Kommunikation in Prozesse einzubinden. Die Datei basierte Integration hat den Vorteil, dass sie einfach und ohne zusätzliche Software umsetzbar ist. In einem Prozess Showcase, bei dem drei verschiedene Systeme integriert wurden, haben wir daher auf diese Variante gesetzt für die asynchrone Kommunikation.


Wenn man einen Prozess in Xpert.Ivy durch eine Datei starten oder weiterlaufen lassen möchte, kann man dies über ein "Event Start" oder ein "Intermediate Event" realisieren.


Schauen wir zuerst das Event "Event Start" an. Xpert.Ivy wird bereits mit drei Java Klassen (auch StartEvenBeans genannt) ausgeliefert.


Im Projekt IvyAddOns gibt es darüber hinaus noch weitere EventBeans. Um diese zu finden wechselt man am besten in die Java Perspektive (Menu Window => Open Perspective => Other => Java). Wenn man nun das Projekt IvyAddOns aufmacht, findet man im Ordner src zwei Packages mit weiteren Start- und Intermediate-EventBeans:


Die Klasse XMLFileStartEventBean ist dabei ein guter Startpunkt, wenn man eine eigene Klasse implementieren möchte. Ich werde später ein Beispiel dazu zeigen.



Hier eine kurze Beschreibung der StartEventBeans.

FileStartEventBean

Startet für jede Datei in einem bestimmten Verzeichnis ein Prozess und kopiert die Werte in der Datei in die Prozessdaten. Die Datei muss dafür folgendes Format haben:

Attribute1=Wert1,Attribute2=Wert2,...

Die Pollzeit ist fest auf eine Minute eingestellt. Die Datei wird gelöscht. Attribute die nicht übereinstimmen werden ignoriert.

Beispiel mit Debug Ausgabe der Prozess Attribute:

FilePickupStartEventBean

Startet für jede Datei in einem bestimmten Verzeichnis ein Prozess und kopiert den Dateinamen in ein Prozess-Attribut vom Typ String.

Die Pollzeit ist fest auf eine Minute eingestellt. Die Datei wird wenn das Setzen des Dateinamens funktioniert gelöscht, im Fehlerfall stehen gelassen.

Beispiel mit Debug Ausgabe:

XMLFileStartEventBean (aus IvyAddOns)

Startet für jede Datei in einem Verzeichnis ein Prozess und kopiert die XML Elemente in die Prozess Attribute (analog FileStartEventBean aber mit einer XML Datei).

Die Pollzeit kann eingestellt werden. Das Verzeichniss kann aus den globalen Variablen von Ivy gelesen werden. Im Fehlerfall wird die Datei in ein Verzeichnis "error" verschoben.

Beispiel:




Fazit

Mit den in Ivy integrierten EventStartBeans ist es möglich, schnell eine Datei basierte Kommunikation zwischen dem Prozess und verschiedenen Systemen umzusetzen. Allerdings sind sie für einen produktiven Einsatz nur bedingt brauchbar, da:
  • Die Pollzeit (ausser bei XMLFileStartEventBean) nicht konfigurierbar ist
  • Das Verzeichnis (ausser bei XMLFileStartEventBean) nicht über eine globale Ivy Variable konfigurierbar ist
  • Das Fehler-Handling zu einfach ist
  • Fehler beim gleichzeitigen Schreiben (vom externen System) und Lesen (durch Ivy) nicht ausgeschlossen sind
  • Kein Datei-Filter angewendet werden kann
Wie man selber eine StartEventBean schreiben kann, welche diese Punkte umsetzt, zeige ich in einem späteren Post.

Mittwoch, 26. September 2012

Xpert.Ivy HTML UI based on JSF?

Looks like Soreco decided to take JSF to replace the JSP based HTML dialogs in Xpert.Ivy:

http://www.ivyteam.ch/flatpress/?entry=entry120917-133839

That would be interessting for us, because we already have some JSF knowledge.

http://fzkvision.ch/

SQLServer Plan Info

I was debugging today a SQL statement which took about 40 seconds in one app, but only 200 ms in SSMS. I learned that SQLServer creates different execution plans depending on the SET options.

Here is a SQL statement to print out some infos about cached execution plans for a specific statement:

SELECT
    query_info.sql_handle,
    query_info.plan_handle,
    plan_options.usecounts,
    query_info.total_elapsed_time,
    CASE WHEN ((1 & set_options) = 1) THEN 'ON' ELSE 'OFF' END AS 'ANSI_PADDING',
    CASE WHEN ((4 & set_options) = 4) THEN 'ON' ELSE 'OFF' END AS 'FORCEPLAN',
    CASE WHEN ((8 & set_options) = 8) THEN 'ON' ELSE 'OFF' END AS 'CONCAT_NULL_YIELDS_NULL',
    CASE WHEN ((16 & set_options) = 16) THEN 'ON' ELSE 'OFF' END AS 'ANSI_WARNINGS',
    CASE WHEN ((32 & set_options) = 32) THEN 'ON' ELSE 'OFF' END AS 'ANSI_NULLS',
    CASE WHEN ((64 & set_options) = 64) THEN 'ON' ELSE 'OFF' END AS 'QUOTED_IDENTIFIER',
    CASE WHEN ((128 & set_options) = 128) THEN 'ON' ELSE 'OFF' END AS 'ANSI_NULL_DFLT_ON',
    CASE WHEN ((256 & set_options) = 256) THEN 'ON' ELSE 'OFF' END AS 'ANSI_NULL_DFLT_OFF',
    CASE WHEN ((512 & set_options) = 512) THEN 'ON' ELSE 'OFF' END AS 'NoBrowseTable',
    CASE WHEN ((4096 & set_options) = 4096) THEN 'ON' ELSE 'OFF' END AS 'ARITH_ABORT',
    CASE WHEN ((8192 & set_options) = 8192) THEN 'ON' ELSE 'OFF' END AS 'NUMERIC_ROUNDABORT',
    CASE WHEN ((16384 & set_options) = 16384) THEN 'ON' ELSE 'OFF' END AS 'DATEFIRST',
    CASE WHEN ((32768 & set_options) = 32768) THEN 'ON' ELSE 'OFF' END AS 'DATEFORMAT',
    CASE WHEN ((65536 & set_options) = 65536) THEN 'ON' ELSE 'OFF' END AS 'LanguageID',
    query_info.text,
    plan_info.query_plan
FROM (
    SELECT plan_handle, usecounts, CAST(pvt.set_options AS INT) AS set_options
    FROM (
        SELECT plan_handle, usecounts, epa.attribute, epa.value
        FROM sys.dm_exec_cached_plans
            OUTER APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa
        WHERE cacheobjtype = 'Compiled Plan') AS ecpa
    PIVOT (MAX(ecpa.value) FOR ecpa.attribute IN ("set_options", "objectid")) AS pvt
) as plan_options
JOIN (
    SELECT qs.sql_handle AS sql_handle, qs.plan_handle AS plan_handle, qs.total_elapsed_time, st.text
    FROM sys.dm_exec_query_stats qs
    CROSS APPLY sys.dm_exec_sql_text(sql_handle) st
) AS query_info ON query_info.plan_handle = plan_options.plan_handle
CROSS APPLY  sys.dm_exec_query_plan(query_info.plan_handle) plan_info
WHERE text LIKE 'SELECT ...%';
 
Thanks to:

Generate SQLServer Scripts with PowerShell

I needed a new script to generate SQL scripts for tables, views etc. The old script did export all tables as files with extension "TAB", all views with extension "VIW", etc. I wanted to keep this, but needed all the files in correct order for dependencies. So here we go: my first PowerShell script.

In order to connect to a SQLServer we must first load some assemblies:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("System.Data") | out-null

Now it is possible to connect to the server:

$srv = new-object "Microsoft.SqlServer.Management.SMO.Server" "NAME_OF_SERVER"
$srv.ConnectionContext.LoginSecure = $false
$srv.ConnectionContext.Login = "USER_NAME"
$srv.ConnectionContext.Password ="PASSWORD"


Get a reference to the database:

$dbName = "DATABASE_NAME"
$db = New-Object "Microsoft.SqlServer.Management.SMO.Database"
$db = $srv.Databases[$dbName]


Get a scripter instance and set options:

$scripter = New-Object "Microsoft.SqlServer.Management.Smo.Scripter"
$scripter.Server = $srv

$scrOpts = New-Object "Microsoft.SqlServer.Management.SMO.ScriptingOptions"
$scrOpts.Encoding = [System.Text.Encoding]::GetEncoding(1252)
$scrOpts.AllowSystemObjects = $false
$scrOpts.AnsiFile = $true
$scrOpts.AppendToFile = $false
$scrOpts.ClusteredIndexes = $true
$scrOpts.Default = $true
$scrOpts.DriAll = $true
$scrOpts.Indexes = $true
$scrOpts.NonClusteredIndexes = $true
$scrOpts.ToFileOnly = $true
$scrOpts.WithDependencies = $false
$scrOpts.Triggers = $true
$scripter.Options = $scrOpts


See MSDN for all Options .

Export all tables:

# Read all tables into an array, filter out system tables
$Objects = @()

$Objects += $db.Tables | where {$_.IsSystemObject -eq $false}
# Find dependencies
$dependencyTree = $scripter.DiscoverDependencies($Objects, $true)
# Create a list of all needed objects in correct order
$depCollection = $scripter.WalkDependencies($dependencyTree);


Unfortunatelly this did nor work the first time, because there was a trigger on a table which called a stored procedure. DiscoverDependencies also returned this procedure and all views and other objects which this procedure was dependent of. Filtering out the procedure helped. To get this done one can set a callback function which gets called by DiscoverDependencies:

function ScriptingFilter([Microsoft.SqlServer.Management.Sdk.Sfc.Urn]$urn) {
    $item = $srv.GetSmoObject($urn)
    if($item -is [Microsoft.SqlServer.Management.Smo.StoredProcedure])
    {
        Write-Host "  Filter: " $item " => " $type
        return $true
    } else {
        return $false
    }
}


$scripter.FilterCallbackFunction = get-content Function:\ScriptingFilter


Now create a script file for each object:

# Create a UrnCollection of all objects
$urns = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection;
foreach($dep in $depCollection) { $urns.add($dep.Urn) }


$onlyOne = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection;
$i = 0
$max = $urnCollection.Count

foreach($urn in $urnCollection)
{
    $onlyOne.clear()
    $item = $srv.GetSmoObject($urn)
    $ext = "SQL"
    if($
item -is [Microsoft.SqlServer.Management.Smo.Table])
    {
        $ext = "TAB"
    }
    if($
item -is [Microsoft.SqlServer.Management.Smo.View])
    {
        $ext = "VIW"
    }
    if($
item -is [Microsoft.SqlServer.Management.Smo.StoredProcedure])
    {
        $ext = "PRC"
    }
    if($
item -is [Microsoft.SqlServer.Management.Smo.UserDefinedFunction])
    {
        $ext = "UDF"
    }
    if($
item -is [Microsoft.SqlServer.Management.Smo.UserDefinedDataType])
    {
        $ext = "UDT"
    }



    # Filter out some more objects we do not need     if($item.Name.StartsWith("spt_") -or $item.IsSystemObject -eq $true)
    {
        Write-Host " Ignored: " $item.Name
     } else {
            $count = $count + 1
            $scripter.Options.FileName = $scriptPath + $count.ToString("0000") + '.' + $item.Schema + "." + $item.Name + "." + $ext
            $scripter.Script($onlyOne)
            $i = $i + 1
    }
}


Finally I added some status information using Write-Progress commandlet and created some functions so that it was possible to script first all tables, then all views and procedures.

Some helpfull links:

The full script:

#Set-ExecutionPolicy RemoteSigned

#
# Extension fuer ein bestimmtes SmoObject ermitteln
#
function GetTypeExt([Microsoft.SqlServer.Management.Smo.SqlSmoObject] $Item)
{
    $ext = "SQL"
    if($Item -is [Microsoft.SqlServer.Management.Smo.Table])
    {
        $ext = "TAB"
    }
    if($Item -is [Microsoft.SqlServer.Management.Smo.View])
    {
        $ext = "VIW"
    }
    if($Item -is [Microsoft.SqlServer.Management.Smo.StoredProcedure])
    {
        $ext = "PRC"
    }
    if($Item -is [Microsoft.SqlServer.Management.Smo.UserDefinedFunction])
    {
        $ext = "UDF"
    }
    if($Item -is [Microsoft.SqlServer.Management.Smo.UserDefinedDataType])
    {
        $ext = "UDT"
    }
    return $ext
}

#
# Function um bestimmte Elemente aus den Abhaengigkeiten auszuschliessen
#
function ScriptingFilter([Microsoft.SqlServer.Management.Sdk.Sfc.Urn]$urn) {
    $item = $srv.GetSmoObject($urn)
    $type = GetTypeExt($item)
    if(-not $Filter -eq "" -and $type.StartsWith($Filter))
    {
        Write-Host "  Filter: " $item " => " $type
        return $true
    } else {
        return $false
    }
}

#
# Generiert Scripts ohne Abhaengigkeiten fuer Objekte in $Objects
#
function GenScripts()
{
    $urns = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection;
    foreach($dep in $Objects) { $urns.add($dep.Urn) }
    GenScriptsForObjects $urns
}

#
# Generiert Scripts mit Abhaengigkeiten fuer Objekte in $Objects
#
function GenScriptsWithDependencies()
{
    $scripter.FilterCallbackFunction = get-content Function:\ScriptingFilter
    if($Objects.Length -ne 0)
    {
        $dependencyTree = $scripter.DiscoverDependencies($Objects, $true)
        $depCollection = $scripter.WalkDependencies($dependencyTree);
        $urns = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection;
        foreach($dep in $depCollection) { $urns.add($dep.Urn) }
        GenScriptsForObjects $urns
    }
}

#
# Generiert Scripts fuer eine Collection von SmoObjects
#
function GenScriptsForObjects([Microsoft.SqlServer.Management.Smo.UrnCollection]$urnCollection)
{
        $onlyOne = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection;
        $i = 0
        $max = $urnCollection.Count

        foreach($urn in $urnCollection)
        {
            $onlyOne.clear()
            $item = $srv.GetSmoObject($urn)
           
            Write-Progress -Activity "Script objects..." -status $item.Name -percentComplete ($i / $max * 100)
           
            $ext = GetTypeExt($item)
           
            if($item.Name.StartsWith("spt_") -or $item.IsSystemObject -eq $true)
            {
                Write-Host "  Ignored: " $item.Name
            } else {
                if(-not $UrnsDone.Contains($item.Urn))
                {
                    $onlyOne.add($item.Urn)
                    $global:count = $UrnsDone.Count + 1
                    $scripter.Options.FileName = $scriptPath + $count.ToString("0000") + '.' + $item.Schema + "." + $item.Name + "." + $ext
                    $scripter.Script($onlyOne)
                    $UrnsDone.add($item.Urn)
                    $i = $i + 1
                }
            }
        }
}

#
# Hauptfunktion: Stellt Verbindung mit SQLServer her, ermittelt die Objekte und Scripted diese
#
function GenerateDBScript([string]$dbServer, [string]$dbName, [string]$dbUser, [string]$dbPsw, [string]$scriptPath)
{
    if ($scriptPath[-1] -ne "\")
    {
        $scriptPath = $scriptPath + "\"
    }
    if (!(Test-Path -path $scriptPath))
    {
       New-Item $scriptPath -type directory
    }
    if (Test-Path -path ($scriptPath + "0*.*"))
    {
        $msg = "Verzeichnis """ + $scriptPath + """ enthält bereits Script-Dateien. Zuerst alle Dateien löschen."
        [System.Windows.Forms.MessageBox]::Show($msg) | out-null
        $msg
        explorer $scriptPath
    } else {
        "Generate scripts for server: " + $dbServer + " database: " + $dbName
        "  to Path: " + $scriptPath

        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
        [System.Reflection.Assembly]::LoadWithPartialName("System.Data") | out-null

        $srv = new-object "Microsoft.SqlServer.Management.SMO.Server" $dbServer
        $srv.ConnectionContext.LoginSecure = $false
        $srv.ConnectionContext.Login = $dbUser
        $srv.ConnectionContext.Password = $dbPsw
        $srv.SetDefaultInitFields([Microsoft.SqlServer.Management.SMO.View], "IsSystemObject")

        $db = New-Object "Microsoft.SqlServer.Management.SMO.Database"
        $db = $srv.Databases[$dbName]

        $scripter = New-Object "Microsoft.SqlServer.Management.Smo.Scripter"
        $scripter.Server = $srv

        $scrOpts = New-Object "Microsoft.SqlServer.Management.SMO.ScriptingOptions"
        #$scrOpts.Encoding = [System.Text.Encoding]::GetEncoding(1252)
        $scrOpts.AllowSystemObjects = $false
        $scrOpts.AnsiFile = $true
        $scrOpts.AppendToFile = $false
        $scrOpts.ClusteredIndexes = $true
        $scrOpts.Default = $true
        $scrOpts.DriAll = $true
        $scrOpts.Indexes = $true
        $scrOpts.NonClusteredIndexes = $true
        $scrOpts.ToFileOnly = $true
        $scrOpts.WithDependencies = $false
        $scrOpts.Triggers = $true
        $scripter.Options = $scrOpts

        $UrnsDone = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection;
        $Filter = ""
        $global:count = 0

        try {

            $Objects = @()
           
            Write-Progress -Activity "Script Objects..." -status "User Defined Data Types" -percentComplete 0
            $Res = $db.UserDefinedDataTypes | where {$_.IsSystemObject -eq $false}
            if($Res)
            {
                $Objects += $Res
            }
       
            Write-Progress -Activity "Script Objects..." -status "Tables" -percentComplete 0
            $Res = $db.Tables | where {$_.IsSystemObject -eq $false}
            if($Res)
            {
                $Objects += $Res
            }

            $Filter = "PRC"
            GenScriptsWithDependencies
           
           
            $Objects = @()
       
            Write-Progress -Activity "Script Objects..." -status "User Defined Functions" -percentComplete 0
            $Res = $db.UserDefinedFunctions | where {$_.IsSystemObject -eq $false}
            if($Res)
            {
                $Objects += $Res
            }

            Write-Progress -Activity "Script Objects..." -status "Views" -percentComplete 0
            $Res = $db.Views | where {$_.IsSystemObject -eq $false}
            if($Res)
            {
                $Objects += $Res
            }

            $Filter = "PRC"
            GenScriptsWithDependencies

           
            $Objects = @()
           
            Write-Progress -Activity "Script Objects..." -status "DB Triggers" -percentComplete 0
            $Res = $db.Triggers | where {$_.IsSystemObject -eq $false}
            if($Res)
            {
                $Objects += $Res
            }

            Write-Progress -Activity "Script Objects..." -status "Procedures" -percentComplete 0
            $Res = $db.StoredProcedures | where {$_.IsSystemObject -eq $false}
            if($Res)
            {
                $Objects += $Res
            }

            $Filter = ""
            GenScriptsWithDependencies

        } catch {
            "EXCEPTION: " + $_.Exception.GetBaseException().Message
            throw $_.Exception
        }

        "Done. " + $count + " Dateien erstellt in " + $scriptPath
        explorer $scriptPath
    }
}

# GenerateDBScript $args[0] $args[1] $args[2] $args[3] $args[4]
GenerateDBScript "SERVER" "DATABASE" "USER" "PASSWORD" "DIRECTORY"