Verwenden des Repository-Modes
Mit Hilfe eines Repositories können die meisten Dateizugriffe auf ein selbst definiertes, virtuelles Dateisystem umgeleitet werden. Dies erlaubt z.B. alle Berichte und zugehörigen Dateien in einer Datenbank zu halten. Die Umsetzung einer solchen Strategie wird in den folgenden Abschnitten beschrieben. Einen Überblick über die Verwendung und Funktionsweise des Repositories kann direkt im Namespace combit.Reporting.Repository eingesehen werden. Im Folgenden soll gezeigt werden, wie das Repository in List & Label verwendet werden kann. Die mitgelieferten ASP.NET Programmierbeispiele zeigen die Verwendung in der Praxis.
Tipp
Im Folgenden werden Code-Ausschnitte gezeigt, die aufgrund der Übersichtlichkeit auf ein Minimum reduziert wurden. Etwaige gezeigten Hilfsfunktionen, können aber in den mitgelieferten ASP.NET vollständig eingesehen werden, da die nachfolgende Beschreibung auf die Implementierung des IRepository-Interfaces anhand einer SQLite-Datenbank und dessen anschließende Verwendung in der Praxis im Detail in den ASP.NET Beispielen basiert.
Schritt 1: Festlegung der Datenhaltung
Das Verwalten der Dateien für List Label wie die Projektdateien für den Designer, P-Dateien für Druckereinstellungen, Inhaltsverzeichnisse, Grafiken, Shapefiles etc. werden in der Regel dem Dateisystem überlassen. Somit arbeitet List & Label auf der einfachen Basis von Dateinamen und dessen Pfaden.
Sobald man sich nun jedoch für das Repository entscheidet, muss man sich mit dieser Frage auseinander setzen und einen geeigneten "Speicher" dafür verwenden bzw. ansteuern, da nun nicht mehr mit Dateinamen gearbeitet wird sondern nur noch mit sogenannten Repository-IDs. Über diese IDs können dann die gefragten Elemente aus dem Repository abgefragt und verwendet werden. Zahlreiche Applikationen nutzen für die Datenhaltung in der Regel eine Art Datenbanksystem, so dass auch oft SQL Datenbanken zum Einsatz kommen. Aus diesem Grund wird hier nun für einfache Demonstrationszwecke eine SQLite-Datenbank verwendet. Hierfür wird der Namespace System.Data.SQLite aus dem .NET Framework genutzt:
public class SQLiteFileRepository
{
private readonly IDbConnection _db;
public SQLiteFileRepository(string databasePath)
{
bool needsDatabaseInit = !File.Exists(databasePath);
_db = new SQLiteConnection("Data Source=" + databasePath);
_db.Open();
if (needsDatabaseInit)
DropAndCreateTables();
}
private void DropAndCreateTables()
{
_db.CreateCommand(@"
DROP TABLE IF EXISTS RepoItems;
CREATE TABLE IF NOT EXISTS RepoItems (
ItemID TEXT,
Type TEXT,
Descriptor TEXT,
TimestampUTC INT,
FileContent BLOB
);").ExecuteNonQuery();
}
}
Public Class SQLiteFileRepository
Private ReadOnly _db As IDbConnection
Public Sub New(databasePath As String)
Dim needsDatabaseInit As Boolean = Not File.Exists(databasePath)
_db = New SQLiteConnection(Convert.ToString("Data Source=") & databasePath)
_db.Open()
If needsDatabaseInit Then
DropAndCreateTables()
End If
End Sub
Private Sub DropAndCreateTables()
_db.CreateCommand("DROP TABLE IF EXISTS RepoItems; CREATE TABLE IF NOT EXISTS RepoItems (ItemID TEXT, Type TEXT, Descriptor TEXT, TimestampUTC INT, FileContent BLOB);").ExecuteNonQuery()
End Sub
End Class
Schritt 2: Notwendige Implementierung des Interfaces IRepository
Nun muss die Implementierung des Interfaces IRepository durchgeführt werden, so dass man individuell auf die Anfragen von List & Label reagieren kann:
public class SQLiteFileRepository : IRepository
{
public bool ContainsItem(string itemID)
{
// ...
}
public void CreateOrUpdateItem(RepositoryItem item, string userImportData, Stream sourceStream)
{
// ...
}
public void DeleteItem(string itemID)
{
// ...
}
public IEnumerable<RepositoryItem> GetAllItems()
{
// ...
}
public RepositoryItem GetItem(string itemID)
{
// ...
}
public void LoadItem(string itemID, Stream destinationStream, CancellationToken cancelToken)
{
// ...
}
public bool LockItem(string id)
{
// ...
}
public void UnlockItem(string id)
{
// ...
}
}
Public Class SQLiteFileRepository Implements IRepository
Public Function ContainsItem(itemID As String) As Boolean Implements IRepository.ContainsItem
' ...
End Function
Public Sub CreateOrUpdateItem(item As RepositoryItem, userImportData As String, sourceStream As Stream) Implements IRepository.CreateOrUpdateItem
' ...
End Sub
Sub DeleteItem(itemID As String) Implements IRepository.DeleteItem
' ...
End Sub
Public Function GetAllItems() As IEnumerable(Of RepositoryItem) Implements IRepository.GetAllItems
' ...
End Function
Public Function GetItem(itemID As String) As RepositoryItem Implements IRepository.GetItem
' ...
End Function
Public Sub LoadItem(itemID As String, destinationStream As Stream, cancelToken As CancellationToken) Implements IRepository.LoadItem
' ...
End Sub
Public Function LockItem(id As String) As Boolean Implements IRepository.LockItem
' ...
End Function
Public Sub UnlockItem(id As String) Implements IRepository.UnlockItem
' ...
End Sub
End Class
Gemäß der Beschreibung in IRepository.ContainsItem wird diese Funktion aufgerufen, um zu prüfen ob ein bestimmtes Element im Repository existiert. Daher muss nun in der Datenbank angefragt werden, ob die angegebene ID vorhanden ist:
public class SQLiteFileRepository : IRepository
{
// ...
public bool ContainsItem(string itemID)
{
int result = Convert.ToInt32(_db.CreateCommand(
"SELECT COUNT(*) FROM RepoItems WHERE ItemID = @ItemID")
.SetParameter("ItemID", itemID).ExecuteScalar());
return (result == 1);
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
'...
Public Function ContainsItem(itemID As String) As Boolean Implements IRepository.ContainsItem
Dim result As Integer = Convert.ToInt32(_db.CreateCommand("SELECT COUNT(*) FROM RepoItems WHERE ItemID = @ItemID").SetParameter("ItemID", itemID).ExecuteScalar())
Return (result = 1)
End Function
' ...
End Class
IRepository.CreateOrUpdateItem
Gemäß der Beschreibung in IRepository.CreateOrUpdateItem wird diese Funktion aufgerufen, wenn ein neues Element dem Repository hinzugefügt werden soll oder wenn ein vorhandenes Element aktualisiert werden soll. Jedoch wird diese Funktion auch aufgerufen, wenn sich lediglich die Metdaten des Elements aktualisieren oder diese unabhängig vom Inhalt hinzugefügt werden sollen:
public class SQLiteFileRepository : IRepository
{
// ...
public void CreateOrUpdateItem(RepositoryItem item, string userImportData, Stream sourceStream)
{
// Den Stream von List & Label in ein byte array konvertieren, um es in die DB zu schreiben.
// Hinweis: sourceStream kann null sein! Dann sollten nur die Metadaten in der Datenbank aktualisiert werden.
byte[] fileContent = null;
bool setMetadataOnly;
if (sourceStream != null)
{
using (var memStream = new MemoryStream())
{
sourceStream.CopyTo(memStream);
fileContent = memStream.ToArray();
}
setMetadataOnly = false;
}
else
{
setMetadataOnly = true;
}
// Muss ein neues Element aktualisiert werden on handelt es sich um ein neues Element?
RepositoryItem itemToInsert;
bool isUpdate = ContainsItem(item.InternalID);
if (isUpdate)
{
// Ein existierendes Repository-Item aktualisieren
itemToInsert = GetItemsFromDb(item.InternalID).First();
itemToInsert.Descriptor = item.Descriptor;
itemToInsert.LastModificationUTC = item.LastModificationUTC;
}
else
{
// Ein neues Repository-Item hinzufügen
itemToInsert = new RepositoryItem(item.InternalID, item.Descriptor, item.Type, item.LastModificationUTC);
}
// Erstelle eine passende SQL-Abfrage für INSERT / UPDATE und rufe es mit oder ohne den Dateiinhalt auf.
string sqlQuery;
if (isUpdate) // UPDATE
{
if (setMetadataOnly)
{
sqlQuery = @"UPDATE RepoItems
SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC
WHERE ItemID = @ItemID";
}
else
{
sqlQuery = @"UPDATE RepoItems
SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC, FileContent = @FileContent
WHERE ItemID = @ItemID";
}
}
else // INSERT
{
if (setMetadataOnly)
{
sqlQuery = @"INSERT INTO RepoItems (ItemID, Type, Descriptor, TimestampUTC)
VALUES (@ItemID, @Type, @Descriptor, @TimestampUTC)";
}
else
{
sqlQuery = @"INSERT INTO RepoItems (ItemID, Type, Descriptor, TimestampUTC, FileContent)
VALUES (@ItemID, @Type, @Descriptor, @TimestampUTC, @FileContent)";
}
}
_db.CreateCommand(sqlQuery)
.SetParameter("ItemID", itemToInsert.InternalID)
.SetParameter("Type", itemToInsert.Type)
.SetParameter("Descriptor", itemToInsert.Descriptor)
.SetParameter("FileContent", fileContent)
.SetParameter("TimestampUTC", itemToInsert.LastModificationUTC.ToBinary()) // Es ist immer eine UTC-Zeit (muss für UI in eine lokale Zeit konvertiert werden)
.ExecuteNonQuery();
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Sub CreateOrUpdateItem(item As RepositoryItem, userImportData As String, sourceStream As Stream) Implements IRepository.CreateOrUpdateItem
' Den Stream von List & Label in ein byte array konvertieren, um es in die DB zu schreiben.
' Hinweis: sourceStream kann null sein! Dann sollten nur die Metadaten in der Datenbank aktualisiert werden.
Dim fileContent As Byte() = Nothing
Dim setMetadataOnly As Boolean
If sourceStream IsNot Nothing Then
Using memStream = New MemoryStream()
sourceStream.CopyTo(memStream)
fileContent = memStream.ToArray()
End Using
setMetadataOnly = False
Else
setMetadataOnly = True
End If
// Muss ein neues Element aktualisiert werden on handelt es sich um ein neues Element?
Dim itemToInsert As RepostoryItem
Dim isUpdate As Boolean = ContainsItem(item.InternalID)
If isUpdate Then
' Ein existierendes Repository-Item aktualisieren
itemToInsert = GetItemsFromDb(item.InternalID).First()
itemToInsert.Descriptor = item.Descriptor
itemToInsert.LastModificationUTC = item.LastModificationUTC
Else
' Ein neues Repository-Item hinzufügen
itemToInsert = New RepostoryItem(item.InternalID, item.Descriptor, item.Type, item.LastModificationUTC)
End If
' Erstelle eine passende SQL-Abfrage für INSERT / UPDATE und rufe es mit oder ohne den Dateiinhalt auf.
Dim sqlQuery As String
If isUpdate Then
' UPDATE
If setMetadataOnly Then
sqlQuery = "UPDATE RepoItems SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC WHERE ItemID = @ItemID"
Else
sqlQuery = "UPDATE RepoItems SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC, FileContent = @FileContent WHERE ItemID = @ItemID"
End If
Else
' INSERT
If setMetadataOnly Then
sqlQuery = "INSERT INTO RepoItems (ItemID, Type, Descriptor, TimestampUTC) VALUES (@ItemID, @Type, @Descriptor, @TimestampUTC)"
Else
sqlQuery = "INSERT INTO RepoItems (ItemID, Type, Descriptor, TimestampUTC, FileContent) VALUES (@ItemID, @Type, @Descriptor, @TimestampUTC, @FileContent)"
End If
End If
' Es ist immer eine UTC-Zeit (muss für UI in eine lokale Zeit konvertiert werden)
_db.CreateCommand(sqlQuery) _
.SetParameter("ItemID", itemToInsert.InternalID) _
.SetParameter("Type", itemToInsert.Type) _
.SetParameter("Descriptor", itemToInsert.Descriptor) _
.SetParameter("FileContent", fileContent) _
.SetParameter("TimestampUTC", itemToInsert.LastModificationUTC.ToBinary()) _
.ExecuteNonQuery()
End Sub
' ...
End Class
Gemäß der Beschreibung in IRepository.DeleteItem wird diese Funktion aufgerufen, wenn ein Element aus dem Repository entfernt werden soll. Daher muss nun auch der zugehörige Datesnsatz aus der SQLite-Datenbank entfernt werden:
public class SQLiteFileRepository : IRepository
{
// ...
public void DeleteItem(string itemID)
{
_db.CreateCommand("DELETE FROM RepoItems WHERE ItemID = @ItemID")
.SetParameter("ItemID", itemID)
.ExecuteNonQuery();
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Sub DeleteItem(itemID As String) Implements IRepository.DeleteItem
_db.CreateCommand("DELETE FROM RepoItems WHERE ItemID = @ItemID").SetParameter("ItemID", itemID).ExecuteNonQuery()
End Sub
' ...
End Class
Diese Implementierung wird aufgerufen, um alle vorhandenen Elemente in Repository abzufragen (siehe auch IRepository.GetAllItems):
public class SQLiteFileRepository : IRepository
{
// ...
public IEnumerable<RepositoryItem> GetAllItems()
{
List<RepositoryItem> result = new List<RepositoryItem>();
var cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems");
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result.Add(new RepositoryItem(
/* ItemID */ reader.GetString(0),
/* Descriptor */ reader.GetString(2),
/* Type */ reader.GetString(1),
/* TimestampUTC */ DateTime.FromBinary(reader.GetInt64(3)))
{
IsEmpty = reader.IsDBNull(4) ? true : (reader.GetInt32(4) == 0 ? true : false)
});
}
}
return result;
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Function GetAllItems() As IEnumerable(Of RepositoryItem) Implements IRepository.GetAllItems
Dim result As New List(Of RepositoryItem)()
Dim cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems")
Using reader = cmd.ExecuteReader()
While reader.Read()
' ItemID
' Descriptor
' Type
' TimestampUTC
result.Add(New RepositoryItem(reader.GetString(0), reader.GetString(2), reader.GetString(1), DateTime.FromBinary(reader.GetInt64(3))) _
With {.IsEmpty = If(reader.IsDBNull(4), True, (If(reader.GetInt32(4) = 0, True, False)))})
End While
End Using
Return result
End Sub
' ...
End Class
Um ein einzelnes Element aus dem Repository zurückliefern zu können, wird die Implementierung von IRepository.GetItem mit der angeforderten ID aufgerufen:
public class SQLiteFileRepository : IRepository
{
// ...
public RepositoryItem GetItem()
{
List<RepositoryItem> result = new List<RepositoryItem>();
var cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems WHERE ItemID = @ItemId");
cmd.SetParameter("ItemId", itemId);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result.Add(new RepositoryItem(
/* ItemID */ reader.GetString(0),
/* Descriptor */ reader.GetString(2),
/* Type */ reader.GetString(1),
/* TimestampUTC */ DateTime.FromBinary(reader.GetInt64(3)))
{
IsEmpty = reader.IsDBNull(4) ? true : (reader.GetInt32(4) == 0 ? true : false)
});
}
}
return result.FirstOrDefault();
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Function GetItem(itemID As String) As RepositoryItem Implements IRepository.GetItem
Dim result As New List(Of RepositoryItem)()
Dim cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems WHERE ItemID = @ItemId")
cmd.SetParameter("ItemId", itemId)
Using reader = cmd.ExecuteReader()
While reader.Read()
' ItemID
' Descriptor
' Type
' TimestampUTC
result.Add(New RepositoryItem(reader.GetString(0), reader.GetString(2), reader.GetString(1), DateTime.FromBinary(reader.GetInt64(3))) _
With {.IsEmpty = If(reader.IsDBNull(4), True, (If(reader.GetInt32(4) = 0, True, False)))})
End While
End Using
Return result.FirstOrDefault()
End Sub
' ...
End Class
Wenn nun tatsächlich ein Element inhaltlich aus dem Repository geladen wird, um bspw. eine Projektdatei im Designer zu öffnen, wird die Implementierung von IRepository.LoadItem aufgerufen und es muss der Inhalt des angefragten Elements als Stream zurückgeliefert werden:
public class SQLiteFileRepository : IRepository
{
// ...
public void LoadItem(string itemID, Stream destinationStream, CancellationToken cancelToken)
{
byte[] content = (byte[])_db.CreateCommand(
"SELECT FileContent FROM RepoItems WHERE ItemID = @ItemID")
.SetParameter("ItemID", itemID).ExecuteScalar();
destinationStream.Write(content, 0, content.Length);
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Sub LoadItem(itemID As String, destinationStream As Stream, cancelToken As CancellationToken) Implements IRepository.LoadItem
Dim content As Byte()
content = DirectCast(_db.CreateCommand("SELECT FileContent FROM RepoItems WHERE ItemID = @ItemID").SetParameter("ItemID", itemID).ExecuteScalar(), Byte())
destinationStream.Write(content, 0, content.Length)
End Sub
' ...
End Class
Wenn die Anforderung besteht, dass ein Element nur exklusiv editiert werden kann - bspw. wenn eine Projektdatei im Designer geöffnet wird - so kann man einen exklusiven Zugriff mit Hilfe von IRepository.LockItem implementieren:
public class SQLiteFileRepository : IRepository
{
// ...
public bool LockItem(string id)
{
// WICHTIG: Es muss immer ein Fallback vorhanden sein, um etwaige Locks freizugeben (z.B. bei Netzwerk Timeouts).
// Insbesondere in Netzwerk-Anwendungen kann unter Umsänden UnlockItem() nicht mehr aufgerufen werden wg. Netzwerk Problemen.
// true zurückiefern, wenn der Lock angefordert wurde order wenn kein Lock implementiert ist.
// false zurückliefern, wenn das Element von einem anderen Benutzer bereits gelockt wurde. Der Designer zeigt dann
// einen Fehlermeldung an und öffnet das Element read-only mode.
return true;
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Function LockItem(id As String) As Boolean Implements IRepository.LockItem
' WICHTIG: Es muss immer ein Fallback vorhanden sein, um etwaige Locks freizugeben (z.B. bei Netzwerk Timeouts).
' Insbesondere in Netzwerk-Anwendungen kann unter Umsänden UnlockItem() nicht mehr aufgerufen werden wg. Netzwerk Problemen.
' true zurückiefern, wenn der Lock angefordert wurde order wenn kein Lock implementiert ist.
' false zurückliefern, wenn das Element von einem anderen Benutzer bereits gelockt wurde. Der Designer zeigt dann
' einen Fehlermeldung an und öffnet das Element read-only mode.
Return True
End Function
' ...
End Class
Dies wird benötigt, um ein zuvor über IRepository.LockItem gesperrtes Element wieder freizugeben:
public class SQLiteFileRepository : IRepository
{
// ...
public void UnlockItem(string id)
{
// ...
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Sub UnlockItem(id As String) Implements IRepository.UnlockItem
' ...
End Sub
' ...
End Class
Hilfsfunktion um Metadaten eines Repository Elements zu aktualisieren
Um einzelne Metadaten eines Elementes im Repository wie bspw. den Anzeigename im Designer einer Projektdatei modifizieren zu können, bedarf es einer kleinen Hilfsfunktion, die diese Anpassung in der SQLite-Datenbank überträgt:
public class SQLiteFileRepository : IRepository
{
// ...
public void SetItemMetadata(string itemID, string descriptor)
{
_db.CreateCommand(@"
UPDATE RepoItems
SET Descriptor = @Descriptor
WHERE ItemID = @ItemID")
.SetParameter("Descriptor", descriptor)
.SetParameter("ItemID", itemID)
.ExecuteNonQuery();
}
// ...
}
Public Class SQLiteFileRepository Implements IRepository
' ...
Public Sub SetItemMetadata(itemID As String, descriptor As String, showReportInToolbar As Boolean, originalFileName As String)
_db.CreateCommand("UPDATE RepoItems SET Descriptor = @Descriptor WHERE ItemID = @ItemID") _
.SetParameter("Descriptor", descriptor) _
.SetParameter("ItemID", itemID).ExecuteNonQuery()
End Sub
' ...
End Class
Ab nun werden alle List & Label betreffenden Dateien in einer SQLite-Datenbank verwaltet. Wie nun mit dem Repository gearbeitet werden kann wird unter "Schritt 3: Verwendung der eigenen IRepository Implementierung" beschrieben.
Schritt 3: Verwendung der eigenen IRepository Implementierung
Hilfsklasse für den einfachen Zugriff auf das Repository
Mit dieser Klasse wird der Zugriff auf das selbst implementierte Repository vereinfacht und dient auch als Grundlage für die folgenden Punkte:
// Hilfsklasse
public class RepositoryHelper
{
// Das aus Schritt 2 implementierte IRepository-Interface
private static SQLiteFileRepository _fileRepository;
public static SQLiteFileRepository GetCurrentRepository()
{
if (_fileRepository == null)
{
_fileRepository = new SQLiteFileRepository(Global.RepositoryDatabaseFile);
}
return _fileRepository;
}
// Definiert einen Anzeigenamen für ein Repository-Item.
// Dieser Anzeigename wird nur für die Dialoge des Designers verwendet!
// Das Repository-Item wird weiterhin nur über seine ID referenziert.
public static void SetRepositoryItemProperties(string itemId, string name)
{
RepostoryItem modifiedItem = GetCurrentRepository().GetItem(itemId);
// Rufe den (kodierten) Descriptor dieses Repository-Items ab (dieser enthält Metadaten wie den Anzeigenamen).
string descriptorString = modifiedItem.Descriptor;
// Dekodiere den String und setzte den gewünschten Anzeigenamen für das UI.
var descriptor = RepositoryItemDescriptor.LoadFromDescriptorString(descriptorString);
descriptor.SetUIName(0, name); // 0 = Standardsprache
descriptorString = descriptor.SerializeToString();
// Speichere den geänderten Descriptor wieder im Repository.
GetCurrentRepository().SetItemMetadata(itemId, descriptorString);
}
}
' Hilfsklasse
Public Class RepositoryHelper
' Das aus Schritt 2 implementierte IRepository-Interface
Private Shared _fileRepository As SQLiteFileRepository
Public Shared Function GetCurrentRepository() As SQLiteFileRepository
If _fileRepository Is Nothing Then
_fileRepository = New SQLiteFileRepository([Global].RepositoryDatabaseFile)
End If
Return _fileRepository
End Function
' D: Definiert einen Anzeigenamen für ein Repository-Item.
' Dieser Anzeigename wird nur für die Dialoge des Designers verwendet!
' Das Repository-Item wird weiterhin nur über seine ID referenziert.
Public Shared Sub SetRepositoryItemProperties(itemId As String, name As String)
Dim modifiedItem As RepostoryItem = GetCurrentRepository().GetItem(itemId)
' Rufe den (kodierten) Descriptor dieses Repository-Items ab (dieser enthält Metadaten wie den Anzeigenamen).
Dim descriptorString As String = modifiedItem.Descriptor
' Dekodiere den String und setzte den gewünschten Anzeigenamen für das UI.
Dim descriptor = RepositoryItemDescriptor.LoadFromDescriptorString(descriptorString)
descriptor.SetUIName(0, name) ' 0 = default language
descriptorString = descriptor.SerializeToString()
' Speichere den geänderten Descriptor wieder im Repository.
GetCurrentRepository().SetItemMetadata(itemId, descriptorString)
End Sub
End Class
Hinzufügen/Importieren von bestehenden Dateien ins Repository
Damit vorhandene Dateien wie bspw. Projektdateien, Grafiken, Shapefiles etc. dem Repository hinzugefügt werden können, kann dies mit der Hilfsklasse RepositoryImportUtil durchgeführt werden:
// Führt eine passende Importfunktion für den Dateityp aus.
private void AddFileToRepository(RepositoryItemType fileType, string file1, string file2)
{
string createdItemId1 = null;
string createdItemId2 = null;
// Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
using (RepositoryImportUtil util = new RepositoryImportUtil(RepositoryHelper.GetCurrentRepository()))
{
using (ListLabel LL = new ListLabel())
{
// Beachten Sie die Möglichkeit, eine eigene Information an die CreateOrUpdate()-Methode (in SQLiteFileRepository) zu übergeben,
// die durch den Import intern aufgerufen wird. Diese Information ist dort im Parameter "userImportData" wieder verfügbar.
string userImportData = "Some custom information for your repository";
if (RepositoryItemType.IsProjectType(fileType))
{
createdItemId1 = util.ImportProjectFile(LL, file1, userImportData /* , printerConfigFile, sketchImageFile */);
}
else if (fileType == RepositoryItemType.Image)
{
createdItemId1 = util.ImportImageFile(LL, file1, userImportData);
}
else if (fileType == RepositoryItemType.PDF)
{
createdItemId1 = util.ImportPdfFile(LL, file1, userImportData);
}
else if (fileType == RepositoryItemType.ProjectReverseSide)
{
createdItemId1 = util.ImportReverseSideFile(LL, file1, userImportData);
}
else if (fileType == RepositoryItemType.ProjectTableOfContents)
{
createdItemId1 = util.ImportTableOfContentsFile(LL, file1, userImportData);
}
else if (fileType == RepositoryItemType.ProjectIndex)
{
createdItemId1 = util.ImportIndexFile(LL, file1, userImportData);
}
else if (fileType == RepositoryItemType.Shapefile)
{
// ImportShapeFile() liefert zwei IDs zurück
// Für das Shapefile (*.shp) und die zugehörige Datenbankdatei (*.dbf)
var createdShapeFileItems = util.ImportShapefile(LL, file1, file2, userImportData);
createdItemId1 = createdShapeFileItems.Item1;
createdItemId2 = createdShapeFileItems.Item2;
}
// Lege den ursprünglichen Dateinamen (ohne Dateiendung) als Anzeigenamen des Repository-Items im UI fest.
string displayName1 = file1.FileName;
if (fileType != RepositoryItemType.Shapefile) // Behalte die Dateiendung nur bei Shapefiles, da es immer zwei Dateien mit dem gleichen Namen sind)
displayName1 = Path.GetFileNameWithoutExtension(displayName1);
RepositoryHelper.SetRepositoryItemProperties(createdItemId1, displayName1);
if (createdItemId2 != null)
RepositoryHelper.SetRepositoryItemProperties(createdItemId2, Path.GetFileNameWithoutExtension(file2));
}
}
}
' Führt eine passende Importfunktion für den Dateityp aus, und gibt die IDs der angelegten Repository-Items zurück.
Private Sub AddFileToRepository(fileType As RepositoryItemType, file1 As String, file2 As String)
Dim createdItemId1 As String = Nothing
Dim createdItemId2 As String = Nothing
' Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
Using util As New RepositoryImportUtil(RepositoryHelper.GetCurrentRepository())
Using LL As New ListLabel()
' Beachten Sie die Möglichkeit, eine eigene Information an die CreateOrUpdate()-Methode (in SQLiteFileRepository) zu übergeben,
' die durch den Import intern aufgerufen wird. Diese Information ist dort im Parameter "userImportData" wieder verfügbar.
Dim userImportData As String = "Some custom information for your repository"
If RepositoryItemType.IsProjectType(fileType) Then
createdItemId1 = util.ImportProjectFile(LL, file1, userImportData)
ElseIf fileType.Value = RepositoryItemType.Image.Value Then
createdItemId1 = util.ImportImageFile(LL, file1, userImportData)
ElseIf fileType.Value = RepositoryItemType.PDF.Value Then
createdItemId1 = util.ImportPdfFile(LL, file1, userImportData)
ElseIf fileType.Value = RepositoryItemType.ProjectReverseSide.Value Then
createdItemId1 = util.ImportReverseSideFile(LL, file1, userImportData)
ElseIf fileType.Value = RepositoryItemType.ProjectTableOfContents.Value Then
createdItemId1 = util.ImportTableOfContentsFile(LL, file1, userImportData)
ElseIf fileType.Value = RepositoryItemType.ProjectIndex.Value Then
createdItemId1 = util.ImportIndexFile(LL, file1, userImportData)
ElseIf fileType.Value = RepositoryItemType.Shapefile.Value Then
// ImportShapeFile() liefert zwei IDs zurück
// Für das Shapefile (*.shp) und die zugehörige Datenbankdatei (*.dbf)
Dim createdShapeFileItems = util.ImportShapefile(LL, file1, file2, userImportData)
createdItemId1 = createdShapeFileItems.Item1
createdItemId2 = createdShapeFileItems.Item2
End If
' Lege den ursprünglichen Dateinamen (ohne Dateiendung) als Anzeigenamen des Repository-Items im UI fest.
Dim displayName1 As String = FileUploadItem.FileName
If fileType.Value <> RepositoryItemType.Shapefile.Value Then
' Behalte die Dateiendung nur bei Shapefiles, da es immer zwei Dateien mit dem gleichen Namen sind)
displayName1 = Path.GetFileNameWithoutExtension(file1)
End If
RepositoryHelper.SetRepositoryItemProperties(createdItemId1, displayName1)
If createdItemId2 IsNot Nothing Then
RepositoryHelper.SetRepositoryItemProperties(createdItemId2, Path.GetFileNameWithoutExtension(file2))
End If
End Using
End Using
End Sub
Erstellen neuer Elemente (neuer Projektdateien) im Repository
Soll eine neue Projektdatei erstellt und im Designer bearbeitet werden, so bietet auch hier die Hilfsklasse RepositoryImportUtil die geeignete Funktion CreateNewProject an:
private void CreateNewRepositoryItem(LlProject projectType, string name)
{
// Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
string createdItemID;
using (RepositoryImportUtil util = new RepositoryImportUtil(RepositoryHelper.GetCurrentRepository()))
{
createdItemID = util.CreateNewProject(projectType, name);
}
// Wenn nicht nur die ID des Repository-Items angezeigt werden soll, muss ein Anzeigename für das UI festgelegt werden.
RepositoryHelper.SetRepositoryItemProperties(createdItemID, name);
}
private Sub CreateNewRepositoryItem(projectType As LlProject, name As String)
' Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
Dim createdItemID As String
Using util As New RepositoryImportUtil(RepositoryHelper.GetCurrentRepository())
createdItemID = util.CreateNewProject(projectType, name)
End Using
' Wenn nicht nur die ID des Repository-Items angezeigt werden soll, muss ein Anzeigename für das UI festgelegt werden.
RepositoryHelper.SetRepositoryItemProperties(createdItemID, name)
End Sub
Entfernen von Elementen aus dem Repository
Um Elemente anhand seiner ID aus dem Repository zu entfernen, kann direkt die dafür vorgesehene Implementierung von IRepository.DeleteItem aufgerufen werden:
private void DeleteRepositoryItem(String itemID)
{
RepositoryHelper.GetCurrentRepository().DeleteItem(itemID);
}
private Sub DeleteRepositoryItem(itemID As String)
RepositoryHelper.GetCurrentRepository().DeleteItem(itemID)
End Sub
Die mitgelieferten ASP.NET Programmierbeispiele zeigen die Implementierung des IRepository-Interfaces anhand einer SQLite-Datenbank und dessen anschließende Verwendung in der Praxis im Detail.