Laden...
Avatar #avatar-2880.jpg
Florian Reischl myCSharp.de - Experte
Softwarearchitekt, Technischer Projektleiter München Dabei seit 16.10.2007 1.564 Beiträge
Benutzerbeschreibung

Forenbeiträge von Florian Reischl Ingesamt 1.564 Beiträge

29.11.2009 - 11:43 Uhr

Hallo Heiko

Ich kann mir nicht wirklich vorstellen dass du einen PDA zum Service-Host machen kannst. IIS und WAS fallen ja schon mal als Provider aus. Wenn kannst du höchstens mal Self-Hosting versuchen, aber ich befürchte irgendwie dass das nix wird.

Nur so aus Neugier, warum soll eigentlich der PDA zum Host werden?
Irgendwie sehe ich den PDA auch eher als Client

Grüße
Flo

28.11.2009 - 23:55 Uhr

Hallo carom

Die Sicherheitsrichtlinien von Silverlight verbieten einen direkten Zugriff auf eine Datenbank. Den würdest du aber sicher auch nicht wirklich wollen, da die Applikation auf dem Client (also irgendwo) läuft und die Verbindungsinformationen in deiner Anwendung hinterlegt sein müssten 😉

Du kannst aber PHP verwenden um einen XML basierten Web-Service zu bauen.

Grüße
Flo

28.11.2009 - 23:39 Uhr

nein, den Pfad ohne den Dateinamen. 🙂

Autsch! 8o
Wer lesen kann ist klar im Vorteil...

Danke
Flo

28.11.2009 - 23:24 Uhr

Hallo

Blöde Frage, geht's dir einfach darum den Dateinamen aus einem Pfad zu bekommen?

Versuch mal das:

Console.WriteLine(Path.GetFileName(@"C:\Program Files\test.doc"));

Grüße
Flo

28.11.2009 - 20:22 Uhr

Ich habe recht viel mit Barcodes zu tun. Danke für den Link 😉

28.11.2009 - 09:12 Uhr

Morgen zusammen

Wenn ich mich jetzt nicht komplett täusche basiert die C++ Map auf einem Red-Black Tree (wie übrigens auch die Indizes der DataTable). Wenn's genau die gleiche Sortierung sein soll kannst du die C5 Library verwenden. Da ist ein Red-Black Tree dabei.

Grüße
Flo

28.11.2009 - 09:03 Uhr

Hallo burli

Ich würde unter Mono auch, wie winSharp93, NHibernate empfehlen. Da gibt's Doku und Beispiele ohne Ende.

@herbivore
Sehe ich genauso. Vielleicht verschiebst du den Thread in "Entwicklungs- und Laufzeitumgebung (Infrastruktur)"?

Grüße
Flo

28.11.2009 - 08:48 Uhr

Guten Morgen Peter

Ich werde jetzt, wie bereits geschrieben, mit einer SQL Express DB anfangen. Wenn ich später auf eine geeignete embedded Datenbank wechslen möchte, worauf sollte ich dann jetzt schon achten?
Geht das überhaupt ohne grundlegende Anpassungen?

Vergiss es. Rein theoretisch ist der Umstieg natürlich möglich wenn du dich auf das Minimum an SQL beschränkst.

Auf eine Embedded Datenbank wie SQL Server Compact kann aber immer nur eine einzige Verbindung geöffnet werden, also nix parallel schreiben und lesen.

Zum Thema SQL wäre theoretisch ein O/R-Mapper wie Entity Framework eine Option. Bei deiner Datendurchsatzrate bin ich mir aber nicht sicher ob du ohne Custom SQL auskommen wirst und spätestens da setzt dir SQL Compact aus.

Außerdem wirst du selbst den Schritt rückwärts einfach nicht durchziehen 😉. Da gehst du nämlich die Evolutionsstufen rückwärts. Wenn du dich erstmal an die Vorzüge des großen gewöhnt hast willst du mit dem kleinen nichts mehr zu tun haben. Das wäre als würdest du von einem BMW auf einen Lada umsteigen :evil:

Grüße
Flo

27.11.2009 - 16:23 Uhr

Hallo

Das hat nix mit dem Activator zu tun sondern ist eine generelle Sicherheitsfunktion von .NET. Kopier mal deine EXE auf einen Netzwerkordner, dann dürftest du die auch nicht starten können. Die DLLs müssen entweder lokal kopiert werden oder explizit in der .NET Sicherheitskonfiguration freigeschalten werden.

Grüße
Flo

27.11.2009 - 13:20 Uhr

Hi

Freut mich dass ich helfen konnte und danke für's Feedback!

Schönes WE
Flo

27.11.2009 - 12:13 Uhr

Aber vielleicht hat ja jemand noch eine ultimative Lösung für dieses Nachschlage-Problem?

Lass mal versuchen 🙂

Wird jetzt aber 100% SQL Server 2005/2008 spezifisch...

Da es sich potentiell ja um sehr viele Keywords handeln kann würde ich nicht für jedes Keyword einen eigenen JOIN bauen. Stattdessen würde ich mit CTEs (Common Table Expressions) und Aggregaten arbeiten.

Erstmal ein kleines Test-Setup:

SET NOCOUNT ON;
GO
---==================================================================
-- cleanup previous tests
IF (OBJECT_ID('tempdb..#Albums') IS NOT NULL)
   DROP TABLE #Albums;
IF (OBJECT_ID('tempdb..#Keywords') IS NOT NULL)
   DROP TABLE #Keywords;
IF (OBJECT_ID('tempdb..#AlbumKeywords') IS NOT NULL)
   DROP TABLE #AlbumKeywords;
GO
---==================================================================
-- tables
CREATE TABLE #Albums (
   Id INT NOT NULL IDENTITY(1,1)
      PRIMARY KEY CLUSTERED
   ,Name VARCHAR(100)
);

CREATE TABLE #Keywords (
   Id INT NOT NULL IDENTITY(1,1)
      PRIMARY KEY CLUSTERED
   ,Value VARCHAR(100)
);

CREATE TABLE #AlbumKeywords (
   AlbumId INT
   ,KeywordId INT
   ,PRIMARY KEY CLUSTERED (AlbumId, KeywordId)
   ,UNIQUE (KeywordId, AlbumId)
);
GO

---==================================================================
-- some test data
INSERT INTO #Albums
             SELECT 'Album 1'
   UNION ALL SELECT 'Album 2'
   ;

INSERT INTO #Keywords
             SELECT 'Key1'
   UNION ALL SELECT 'Key2'
   UNION ALL SELECT 'Key3'
   ;

INSERT INTO #AlbumKeywords
             SELECT 1, 1
   UNION ALL SELECT 1, 2
   UNION ALL SELECT 2, 1
   UNION ALL SELECT 2, 3
   ;
GO

Ein Album kann also ein oder mehr Keywords haben und ein Keyword kann (natürlich) mehreren Alben zugeordnet werden. Zuordnung über die Tabelle #AlbumKeywords.
Album 1 hat die Keywords Key1 und Key2
Album 2 hat die Keywords Key1 und Key3

Als nächstes werde die aktuell gesuchten Keywords in Table-Variablen gesteckt.

---==================================================================
-- init search
DECLARE @MatchAll TABLE (Value VARCHAR(100));
DECLARE @MatchAny TABLE (Value VARCHAR(100));
DECLARE @MatchNone TABLE (Value VARCHAR(100));
DECLARE @AllCount INT;
DECLARE @AnyCount INT;

-------------------------------------------------------------
---- test match all
--INSERT INTO @MatchAll
--             SELECT 'Key1'
--   UNION ALL SELECT 'Key2';

-------------------------------------------------------------
---- test match any
--INSERT INTO @MatchAny
--             SELECT 'Key2'
--   UNION ALL SELECT 'Key3';

-------------------------------------------------------------
---- test match none
--INSERT INTO @MatchNone
--             SELECT 'Key3'

-----------------------------------------------------------
-- test combinations
INSERT INTO @MatchAll SELECT 'Key1';
INSERT INTO @MatchNone SELECT 'Key2';

Ich habe jetzt mal ein paar Kombinationen die ich getestet habe drinnen gelassen. Können einzeln oder kombiniert ausgeführt werden. (Habe aber jetzt nicht 100% alle Kombinationen versuch.)

Die Suche (unten eine kurze Beschreibung)


-----------------------------------------------------------
-- Get count information for all and any queries
SELECT @AllCount = COUNT(*) FROM @MatchAll;
SELECT @AnyCount = COUNT(*) FROM @MatchAny;

-----------------------------------------------------------
-- select albums
WITH 
m_all (AlbumId, MatchCount) AS
(  -- match all criteria
   SELECT
      ak.AlbumId
      ,COUNT(*)
   FROM #AlbumKeywords ak
      JOIN #Keywords k ON ak.KeywordId = k.Id
      JOIN @MatchAll m ON k.Value = m.Value
   GROUP BY ak.AlbumId
   -- if no ALL criteria specified return all albums
   UNION ALL
   SELECT
      Id
      ,@AllCount
   FROM #Albums
   WHERE @AllCount = 0
),
m_any (AlbumId) AS
(  -- match any criteria
   SELECT DISTINCT
      ak.AlbumId
   FROM #AlbumKeywords ak
      JOIN #Keywords k ON ak.KeywordId = k.Id
      JOIN @MatchAny m ON k.Value = m.Value
   -- if no ANY criteria specified return all albums
   UNION ALL
   SELECT
      Id
   FROM #Albums
   WHERE @AnyCount = 0
),
m_none (AlbumId) AS
(  -- match excluded criteria
   SELECT DISTINCT
      ak.AlbumId
   FROM #AlbumKeywords ak
      JOIN #Keywords k ON ak.KeywordId = k.Id
      JOIN @MatchNone m ON k.Value = m.Value
)
SELECT
      a.*
   FROM #Albums a
      JOIN m_all ON a.Id = m_all.AlbumId 
                    AND m_all.MatchCount = @AllCount
      JOIN m_any ON a.Id = m_any.AlbumId
      LEFT JOIN m_none ON a.Id = m_none.AlbumId
   WHERE m_none.AlbumId IS NULL
   ;

Ich benötige hierfür erstmal die Anzahl der gewünschten ALL und ANY (=OR) Kriterien um entscheiden zu können ob alle Kriterien erfüllt wurden, bzw. Teile der Suche komplett auszuschließen wenn gar keine Kriterien angegeben wurden.

CTE "m_all" liefert die AlbumId und die Anzahl der Treffer zurück. Damit auch nur die Alben mit allen Kriterien zurückgeliefert werden ist die Treffer-Anzahl ganz unten Kriterium der JOIN-Klausel. Wurden gar keine ALL Kriterien angegeben so werden alle Alben zurückgeliefert.

CTE "m_any" liefert die AlbumId zurück wenn irgendein ANY Kriterium erfüllt wurde. Gab's gar keine ANY Kriterien wieder einfach alles.

CTE "m_none" ist ähnlich "m_any" nur ohne den "Kein Argument"-Teil.

Am Ende noch die eigentliche Abfrage auf die Alben die alle CTEs vereint.

Achtung:
Da die UNION ALLs in "m_all" und "m_any" eine Art Catch-All Query darstellen sollte man unbedingt ein Auge auf den Execution Plan haben um sicherzustellen dass SQL Server hier keinen Schrott macht und in einem Full Table Scan endet. Sicherer würde hier die Abfrage dynamisch zu erstellen. Dass schließt einen FTS aus und verhindert zusätzlich Abfragen die nicht nötig sind.

Grüße
Flo

27.11.2009 - 11:22 Uhr

Hallo akswift

MSDN zu DefaultIfEmpty

Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty.

Lass dein DefaultIfEmpty einfach weg und du erhältst eine leere Liste.

Grüße
Flo

27.11.2009 - 11:12 Uhr

Hi Khalid!

Die gute alte EAV/CR Struktur 👅 . Habe das Problem auch (noch) manchmal.

Es gibt zwei unterschiedliche Wege das zu lösen. Die PIVOT Funktion oder ein konditionaler Split mit einer Cross-Tab Query.

Mal kurz ein paar Test-Daten (ich habe deine Kunden-Tabelle jetzt weggelassen da die ja auf die Query keine wirkliche Auswirkung hat):

DECLARE @ContactInfo TABLE (
   Id INT NOT NULL IDENTITY(1,1)
      PRIMARY KEY CLUSTERED
   ,ContactId INT
   ,Type INT
   ,Value VARCHAR(100)
);

INSERT INTO @ContactInfo (ContactId, Type, Value) 
VALUES
   (1, 1, 'Vorname 1')
  ,(1, 2, 'Nachname 1')
  ,(1, 3, 'Email 1')
  ,(2, 2, 'Nachname 2')
  ,(2, 3, 'Email 2')
  ,(2, 4, 'Phone 2');

Über PIVOT musst du immer eine Subquery (oder eine CTE) verwenden um deine Identity-Spalte rauszuschmeißen da diese sonst als Anker für die Zeilen verwendet wird und somit nix aggregiert wird:

SELECT
      ContactId
      ,[1] Vorname
      ,[2] Nachname
      ,[3] Email
      ,[4] Phone
   FROM (
      SELECT ContactId, Type, Value FROM @ContactInfo
      ) ci
   PIVOT (
      MAX(Value)
      FOR [Type] IN ([1], [2], [3], [4])
      ) AS pvt

Ich würde jedoch einen Cross-Tab Split empfehlen. Ist erstens mehr Straight-Forward, besser lesbar (finde ich) und vor allem deutlich schneller.

SELECT
      ContactId
      ,MAX(CASE WHEN Type = 1 THEN Value ELSE '' END) Vorname
      ,MAX(CASE WHEN Type = 2 THEN Value ELSE '' END) Nachname
      ,MAX(CASE WHEN Type = 3 THEN Value ELSE '' END) Email
      ,MAX(CASE WHEN Type = 4 THEN Value ELSE '' END) Phone
   FROM @ContactInfo
   GROUP BY ContactId

Sowohl zur Performance im Vergleich zu PIVOT als auch zu vielen anderen Beispielen gibt's eine großartige Artikel-Serie von Jeff Moden (ganz großartiger Mensch!) bei SQLServerCentral.com:
Cross Tabs and Pivots, Part 1 – Converting Rows to Columns
Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs

Grüße
Flo

27.11.2009 - 10:49 Uhr

edit: ich sollte besser suchen das nächste Mal

>

Bitte beachte, dass der Blog-Post unten in den Kommentaren ziemlich zerfetzt wird 😉

27.11.2009 - 10:43 Uhr

Hi ThaMubber

Argumente hat herbivore ja schon einige gebracht.

Mir fällt spontan nur die Datenverdopplung ein. Was ist wenn man sagt, dass die Performanz der Anwendung nebensächlich ist ?

Meine Erfahrung hat mir eigentlich immer gezeigt, Performance ist nie nebensächlich 8) .

Was sind eigentlich die Argumente die gegen das Kopieren sind?

Gegenfrage: Was spricht denn dafür? 😉
Ob ich jetzt eine Clone-Methode baue oder ein Read-Only-Interface schreibe (geht ja fast automatisiert) ist eigentlich kein Mehraufwand, aber halt wirklich sauber. Nebenbei bist du mit Interfaces immer komplett flexibel weil du, wenn nötig, auch unterschiedliche auf eine Klasse klatschen kannst., Clone-Methode geht nur eine.

Grüße
Flo

26.11.2009 - 17:55 Uhr

Hallo CurdledMilk

Zur Zeit war / ist es so geplant das sogar die Datenbank wiederverwendbar ist. Wenn man also in einer Anwendung eine Nutzerverwaltung braucht kann man die Datenbank und die C# Anwendung "drüberlegen" oder integrieren ohne evt. seine eigene Datenstruktur anpassen zu müssen.

Kannst du durch eine Entkopplung zwischen Business Logic Layer und Data Access Layer erreichen. So kann man den BLL bei Bedarf von der Datenbank trennen und stattdessen auf einen Data-Service zugreifen lassen. Lässt sich über Interfaces machen. (Ist nebenbei auch praktisch für Unit Tests.)

Ein anderer Ansatz wäre noch den BLL über Interfaces zu abstrahieren. So kann die ganze Sache als Authentication Service zur Verfügung gestellt werden und von beliebigen Applikationen konsumiert werden.

Den Pfeil mit A User() und A UserGroup() versteh ich nicht so richtig, könntest du das evt. noch mal genauer erklären

"A User":
Der Client fragt vom BLL einen User an (über ein Repository, eine Query, ...). Der BLL prüft ob der User bereits geladen ist. Wenn nicht holt er die entsprechenden Daten über den DAL (und der aus der Datenbank). Mit den Daten kann der BLL einen User erzeugen welcher danach zurückgeliefert wird.

"A UserGroup":
Der Client ruft auf dem User eine Methode/Property "UserGroup" auf. Das "User"-Objekt holt sich die entsprechende UserGroup aus dem BLL. Wenn der diese geladen hat liefert er die zurück, ansonsten gleiches Spiel wie vorher. DAL anfragen, Daten holen, UserGroup Objekt erzeugen.

Grüße
Flo

Die POCOs im allgemeinen habe ich jetzt denk ich verstanden - Sie sind quasi Bestandteil der Wiederverwendbarkeit und dienen dazu Die Anwendung im allgemeinen unabhängig von komplexen (System) Klassen zu halten?

26.11.2009 - 11:06 Uhr

Hi Th69

@Florian: der Expression Parser hat aber einige Schwächen (s. "Current Shortcomings"). Die Menge an unausgereiften Parsern im WWW war ja extra ein Grund, meinen Parser zu veröffentlichen -)

Ich muss gestehen, dass ich den Expression Parser noch nicht verwendet habe. War der erstbeste Treffer den ich gefunden habe und der sich passend angehört hat solange keine genaueren Infos vorhanden sind. Danke für den Hinweis!

Grüße
Flo

26.11.2009 - 11:02 Uhr

POCOs dürfen/sollen durchaus Funktionalitäten enthalten. Es geht nur darum, dass sie unabhängig von anderen Technologien sein sollen. Z.B. kann/soll dein "Benutzer"-Objekt durchaus eine Referenz auf seine Benutzergruppe(n) haben.

Mit dem DataSet machst du deine komplette Library jedoch abhängig von System.Data und von den enthaltenen Komponenten. Wenn du den DataAdapter erzwingst kann man auch keine andere Datenquelle als eine Datenbank verwenden. Vielleicht macht es mal Sinn statt einer Datenbank die Benutzer-Informationen über einen (Web-)Service zur Verfügung zu stellen. Bei wiederverwendbaren Komponenten/Artefakten sollte die Daten-Schicht (die Datenbank in deinem Fall) unter der der Business-Schicht (deine Benutzerverwaltungslogik) austauschbar bleiben.

Habe mal ein grobes Diagramm angehängt das einen möglichen, grundsätzlichen Ablauf der darstellt.

Grüße
Flo

26.11.2009 - 10:35 Uhr

@JAck30lena: Guter Punkt, dass mit dem "darstellen" hatte ich irgendwie überlesen. Ich gehe mal davon aus, dass es entweder um einen Serverprozess zur Auswertung oder um die Aufbereitung der Daten im DAL/BLL geht.

26.11.2009 - 10:32 Uhr

Guten Morgen Peter!

Danke für die Infos, die sind wichtig! 😃

Ich muss gestehen ich habe noch keine großen Performance Tests mit SQL Compact gemacht, aber ich würde bei deinen Eckdaten eher SQL Server Express empfehlen. Hier hast du - wenn nötig - wesentlich mehr Optimierungsmöglichkeiten. So kannst du z.B. Daten in Mengenoperationen schreiben und mehrere Abfragen in einem Round-Trip verarbeiten, bzw. die Sache(n) parallelisieren. Das sind alles Features die bei SQL Compact by-Design ausfallen. Außerdem ist der Umstieg von Express auf Standard (falls mal nötig) abgesehen von der Installation aufwandsfrei, weil 100% kompatibel.

Bei nur 200,000 Datensätzen die in einem Durchlauf verarbeitet werden kannst du mit clientseitigem Query-Caching auch viele Round-Trips vermeiden.

Die Menge der Transaktionen dürften bei SQL Server auch kein Problem sein. Wir fahren da deutlich höhere Mengen (mit der Standard Edition).

Grüße
Flo

26.11.2009 - 09:44 Uhr

Guten Morgen Jan

Die msbuild.exe dürfte das sein was du suchst. Kannst du über ein Batch-File und/oder "Send To" im Explorer einbinden.

Grüße
Flo

26.11.2009 - 09:35 Uhr

Hallo DevHB,

Dass hier schon versucht:
How do I enable MSTDC on SqlServer

Grüße
Flo

26.11.2009 - 09:24 Uhr

Ich muss sagen, dass ich mir ein const wie von ThaMubber beschrieben auch schon ein paar mal gewünscht hätte. Das hätte mir einige Proxies/Wrapper/DTOs und Interfaces erspart. Ich arbeite u.a. an den Basislibraries unserer Firma und kann aus Erfahrung sagen, was man machen darf wird gemacht. Die Observable-Collection ist eine schicke Sache, reicht aber nicht wenn es um Objekt-Bäume geht.

Grüße
Flo

26.11.2009 - 09:09 Uhr

Hallo CurdledMilk

POCOs sind Plain Old C# Objects. Der Begriff kommt ursprünglich aus dem Java Umfeld (da heißen die POJOs). Dabei geht's meistens um Objekte der Business-Schicht einer Applikation. POCOs sind Objekte die keine speziellen Interfaces/Basisklassen erwarten oder mitbringen.

Wenn ich eine bestehende Benutzerverwaltung in meiner Anwendung verwenden sollte würde ich erwarten, dass ein Benutzer-Objekt ein Benutzer-Objekt ist und nichts anderes - wie eine DataTable oder eine DataRow.

Du kannst innerhalb deiner/eurer Library ja mit DataSet/DataTable/DataAdapter arbeiten. Nach außen solltest du die Daten jedoch in Objekte mappen welche nichts anderes sind als das was sie sein sollen.

Grüße
Flo

26.11.2009 - 08:41 Uhr

Hallo Peter

Für meine Anwendung ist Geschwindigkeit aber oberste Maxime!

Um wie viele Abfragen pro Sekunde geht's dir denn? Wie viele Datensätze enthalten die Tabellen bei 1 GB Datenbank?
Nur damit wir wissen von welcher Größenordnung von Geschwindigkeit wir sprechen.

Grüße
Flo

25.11.2009 - 23:25 Uhr

edit: ja als bytes würds auch gehen, allerdings ist das dann genauso aufwändig

Den check isch net.

         Image img = null;
         MemoryStream stream = new MemoryStream();
         img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
         byte[] data = new byte[stream.Length];
         stream.Write(data, 0, data.Length);

und wenn ichs einzeln speichere, kann ichs denk ich mal auch besser debuggen 😄

Und den check isch auch net 👅

25.11.2009 - 23:19 Uhr

... oder als byte[]
😃

Grüße
Flo

25.11.2009 - 23:07 Uhr

Jain 😃

Im FX ist mir keines bekannt, habe ich aber selbst schon mal gebraucht:

   /// <summary>
   /// Represents a read-only dictionary
   /// </summary>
   /// <typeparam name="TKey">The generic key type for the dictionary.</typeparam>
   /// <typeparam name="TValue">The generic value type for the dictionary.</typeparam>
   public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
   {
      #region Constructor

      /// <summary>
      /// Creates a new read-only dictionary for a specified dictionary.
      /// </summary>
      /// <param name="dictionary">The dictionary to create a read-only wrapper for.</param>
      public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
      {
         _dict = dictionary;
      }

      #endregion

      #region Private Fields

      private IDictionary<TKey, TValue> _dict;

      #endregion

      #region Public Indexer

      /// <summary>
      /// Gets a value at a specified key.
      /// </summary>
      /// <param name="key">The key to get a value for.</param>
      /// <returns>The value for the specified key.</returns>
      public TValue this[TKey key]
      {
         get { return _dict[key]; }
      }

      #endregion

      #region Public Properties

      /// <summary>
      /// Gets the count of items within the dicionary.
      /// </summary>
      public int Count
      {
         get { return _dict.Count; }
      }

      /// <summary>
      /// Gets the keys of the dictionary.
      /// </summary>
      public ICollection<TKey> Keys
      {
         get { return _dict.Keys; }
      }

      /// <summary>
      /// Gets a collection of the values within the dictionary.
      /// </summary>
      public ICollection<TValue> Values
      {
         get { return _dict.Values; }
      }

      #endregion

      #region Public Methods

      /// <summary>
      /// Determines if a specified item exists within the dictionary.
      /// </summary>
      /// <param name="item">The item to search for.</param>
      /// <returns>True if the specified item was present within the dicionary; otherwise false.</returns>
      public bool Contains(KeyValuePair<TKey, TValue> item)
      {
         return this.ContainsKey(item.Key);
      }

      /// <summary>
      /// Determines if a specified key is present within the dictionary.
      /// </summary>
      /// <param name="key">The key to determine if it is present within the dicionary.</param>
      /// <returns>True if the key was present within the dictionary; otherwise false.</returns>
      public bool ContainsKey(TKey key)
      {
         return _dict.ContainsKey(key);
      }

      /// <summary>
      /// Copies the items within the dictionary to a specified array.
      /// </summary>
      /// <param name="array">The array to copy the items to.</param>
      /// <param name="arrayIndex">The start position to copy the values to.</param>
      public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
      {
         ((IDictionary<TKey, TValue>)_dict).CopyTo(array, arrayIndex);
      }

      /// <summary>
      /// Gets an key value enumerator for the dictionary.
      /// </summary>
      /// <returns>The enumerator over all items within the dictionary.</returns>
      public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
      {
         return _dict.GetEnumerator();
      }

      /// <summary>
      /// Tries to get a value for a specified key.
      /// </summary>
      /// <param name="key">The key to try to get a value for.</param>
      /// <param name="value">The value reference to get a value to.</param>
      /// <returns>True if the value could be taken; otherwise false.</returns>
      public bool TryGetValue(TKey key, out TValue value)
      {
         return _dict.TryGetValue(key, out value);
      }

      #endregion

      #region IDictionary<TKey,TValue> Members

      void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
      {
         throw new InvalidOperationException("Dictionary is read-only");
      }

      bool IDictionary<TKey, TValue>.ContainsKey(TKey key)
      {
         return this.ContainsKey(key);
      }

      ICollection<TKey> IDictionary<TKey, TValue>.Keys
      {
         get { return this.Keys; }
      }

      bool IDictionary<TKey, TValue>.Remove(TKey key)
      {
         throw new InvalidOperationException("Dictionary is read-only");
      }

      bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)
      {
         return this.TryGetValue(key, out value);
      }

      ICollection<TValue> IDictionary<TKey, TValue>.Values
      {
         get { return this.Values; }
      }

      TValue IDictionary<TKey, TValue>.this[TKey key]
      {
         get { return this[key]; }
         set { throw new InvalidOperationException("Dictionary is read-only"); }
      }

      #endregion

      #region ICollection<KeyValuePair<TKey,TValue>> Members

      void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
      {
         throw new InvalidOperationException("Dictionary is read-only");
      }

      void ICollection<KeyValuePair<TKey, TValue>>.Clear()
      {
         throw new InvalidOperationException("Dictionary is read-only");
      }

      bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
      {
         return this.Contains(item);
      }

      void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
      {
         this.CopyTo(array, arrayIndex);
      }

      int ICollection<KeyValuePair<TKey, TValue>>.Count
      {
         get { return this.Count; }
      }

      bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
      {
         get { return true; }
      }

      bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
      {
         throw new InvalidOperationException("Dictionary is read-only");
      }

      #endregion

      #region IEnumerable<KeyValuePair<TKey,TValue>> Members

      IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
      {
         return this.GetEnumerator();
      }

      #endregion

      #region IEnumerable Members

      System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
      {
         return this.GetEnumerator();
      }

      #endregion
   }

Grüße
Flo

25.11.2009 - 22:53 Uhr

Ne, da wird intern keine neue Liste erzeugt. Das ist nur ein Proxy für deine übergebene Liste der die manipulierenden Methoden nicht unterstützt.

Grüße
Flo

25.11.2009 - 22:42 Uhr

Hallo Innocentus

Wenn ich mich jetzt nicht täusche musst du einen Array statt Array übergeben:
Statt:
public Array MyData { get; }
Das:
public object[] MyData { get; }

... wobei du statt "object" auch einen anderen Basistypen oder in in deinem Contract definierten Objekttypen übergeben kannst.

Grüße
Flo

25.11.2009 - 22:35 Uhr

Hi ThaMubber

Wenn's dir "nur" um eine Liste geht übergib einfach eine System.Collections.ObjectModel.ReadOnlyCollection<string>. Der kannst du im ctor deine Liste übergeben.

Grüße
Flo

25.11.2009 - 21:54 Uhr

???
Du solltest schon wissen wie du mit einer Datenbank kommunizierst. Bitte suche mal im Web nach einem Tutorial. Von BerndFfm gibt's z.B. hier eines.

Grüße
Flo

25.11.2009 - 21:44 Uhr

Na wenn nur ein Anwender auf die Datenbank zugreift, dann passt SQL Compact. Wenn alle Benutzer auf die gleiche Datenbank zugreifen musst du eine andere Datenbank verwenden.

25.11.2009 - 21:34 Uhr

In SQL Compact gibt's NTEXT. Dass ist ein Unicode-Text. Ich weiß nicht wie groß der werden kann, denke aber dass man sich da am großen Bruder orientiert hat.

SQL Express benötigst du wenn's eine Multi-User Anwendung werden soll (oder du T-SQL verwenden willst).

Grüße
Flo

25.11.2009 - 19:15 Uhr

Hallo CurdledMilk

Ich würde erstmal die Verbindung und die Transaktion in eine Hilfsklasse auslagern. So vermeidet man recht gut die Connection und/oder die Transaktion zu disposen:

class DbExecutionContext : IDisposable {
   public DbExecutionContext() {
      string conStr = "connection string aus der configuration holen";
      _con = new SqlConnection(conStr);
      _con.Open();
      _tran = _con.BeginTransaction();
   }
   bool _disposed;
   SqlConnection _con;
   SqlTransaction _tran;

   public SqlCommand CreateProcedureCommand(string procedureName) {
      SqlCommand cmd = new SqlCommand(procedureName);
      cmd.Connection = _con;
      cmd.Transaction = _tran;
      return cmd;
   }

   public void Commit() {
      _tran.Commit();
      Dispose();
   }

   public void Dispose() {
      if (_disposed)
         return;
      _tran.Dispose();
      _con.Close();
      _con.Dispose();
      _disposed = true;
   }
}

Die Klasse kann so in einen using-Block eingebettet werden und stellt sicher, dass alle innerhalb des Blocks ausgeführten Aktionen entweder funktionieren oder nicht. Wird kein Commit aufgerufen so wird beim Dispose implizit die komplette Transaktion zurückgerollt.

Außerdem verwendet auch das SqlCommand unmanaged Ressourcen, weswegen es ebenfalls in einen using-Block gehört:


using (DbExecutionContext ctx = new DbExecutionContext())
using (SqlCommand cmd = ctx.CreateProcedureCommand("myProcName")) {
   SqlParameter p1 = cmd.Parameters.Add("@anyParam", SqlDbType.VarChar, 10);
   SqlParameter p2 = cmd.Parameters.Add("@otherParam", SqlDbType.Int);

   p1.Value = "bla";
   p2.Value = 123;

   cmd.ExecuteNonQuery();
}

Übrigens solltest du bei Datentypen mit variabler Länge (VARCHAR/NVARCHAR/VARBINARY) immer die Länge der Parameter angeben um eine optimale Cached Plan Reusage zu erzielen.

Ansonsten hänge ich mich an frisch's Aussage, ich bin auch kein Fan von DataSets. Vor allem wenn die Sache wiederverwendbar sein soll würde ich dringend POCO's und ein eigenes Mapping empfehlen. Bei einer Benutzerverwaltung sollte die Menge der Klassen ja auch verhältnismäßig übersichtlich bleiben.

Grüße
Flo

25.11.2009 - 18:53 Uhr

bis 1GB im worst-case Fall

Nimm ein CSV File 😁 😁

Okay, zum eigentlichen Thema. Da JAck30lena hat ja eigentlich schon alle Antworten gegeben hat komm ich mit 'ner neuen Frage. Hast du mal den Microsoft LogParser in Erwägung gezogen? Der kann unterschiedlichste Arten von Dateien parsen/abfragen/konvertieren und - bei Bedarf - auch in eine Datenbank importieren.

(Performance ist auch okay. Ein Bekannter von mir importiert damit mehrmals die Woche einige Terrabyte in eine Datenbank und ist recht zufrieden.)

Grüße
Flo

23.11.2009 - 19:44 Uhr

Hi

Nein, das kann EF (soviel ich weiß auch in .NET 4.0) nicht. Sobald die Tabelle zusätzliche Attribute enthält wird sie zu einem komplexen Objekt und EF kann sie nicht mehr als reine Mapping-Tabelle betrachten da es nicht weiß wie es mit den zusätzlichen Spalten umgehen soll.

Du könntest höchstens die Objekte im Modell als Internal definieren und in deinen Entitäten die entsprechenden Eigenschaften selbst einbauen.

(Mir fällt auf die schnelle gar kein ORM ein der das unterstützt.)

Grüße
Flo

23.11.2009 - 19:29 Uhr

Wie gesagt, solche Sachen gehören in's Frontend.

Erklär doch mal wofür du die fortlaufende Nummer brauchst, dann kann man vielleicht eine Lösung finden.

23.11.2009 - 19:27 Uhr

Hallo Abt

Ja EF kann auch N:M Relationen. Werden eigentlich automatisch korrekt gemappt sobald du zwei Entities in's Diagramm packst die über eine Mapping-Tabelle verbunden sind.

Grüße
Flo

23.11.2009 - 19:15 Uhr

Hallo -Hades-

Weit verbreitetes Verständnisproblem. Ein Datenbank Primary Key (meistens die IDENTITY Spalte) ist keine Information die irgendwo angezeigt werden soll. Die wird nicht angepasst eine Identität einer Zeile ist unveränderbar und das muss so sein.

Stell dir vor du hast 1000 Kunden und löschst den ersten. Danach müssten 999 Kunden aktualisiert werden. Zusätzlich müssten alle Bestellungen aller Kunden angepasst werden usw...

Wenn du eine vorlaufende Nummer in deiner Anwendung anzeigen willst, dann mach sowas im Client. (Oder - wenn wirklich nötig - über ROW_NUMBER() .)

Grüße
Flo

23.11.2009 - 10:57 Uhr

Hi WienX

Ich muss gestehen ich habe von Word so gut wie gar keine Ahnung. Ändere doch mal deinen Thread-Titel von "Latebinding neue Zeile?" in irgendwas wie "MS-Word neue Zeile einfügen". Dann erkennen mehr Leute von außen um was es geht. Dann bleibt zwar das Forum noch falsch, aber in der "Aktive Themen" Liste werden mehr drauf aufmerksam.

Grüße
Flo

23.11.2009 - 10:32 Uhr

Hallo WienX

Versucht mal \r\n

Grüße
Flo

PS: Und versuch beim nächsten Thread einen besseren Titel und ein besseres Forum zu finden. Die Leute die sich mit deinem Problem wirklich auskennen dürftest du nämlich erstmal ausgeschlossen haben. Dafür darfst du dich jetzt hier mit einem Office-Noob wie mir rumschlagen. 😉

22.11.2009 - 11:48 Uhr

Hallo Fabian

Der ObjectContext sollte - meiner Meinung nach - eine Business-Transaktion (AKA Unit of Work) umschließen. Es macht normalerweise keinen Sinn für jede Änderung eines Feldwertes einen Context zu erzeugen und diese Änderung in die Datenbank zu schreiben. Wenn dein WPF-Fenster also beispielsweise ein Bestellformular darstellt sollte im Hintergrund ein Context für die komplette Aktion existieren. Es sollte jedoch nicht einen Context für die komplette Applikation geben.

Grüße
Flo

22.11.2009 - 10:54 Uhr

Hallo Tobias

Man kann bei NVARCHAR eine maximale länge von 4000 Zeichen genau definieren (8000 bei VARCHAR) und das sollte man auch machen wenn's geht.

NVARCHAR(MAX), VARCHAR(MAX) und VARBINARY(MAX) sind ein BLOB Datentypen und werden intern komplett anders verarbeitet. Normalerweise werden alle Werte einer Zeile in einer Daten-Page gespeichert, bei BLOB Typen ist das anders. Hier verweisen die Daten-Pages nur auf eine andere Position an der diese BLOBs abgelegt sind. Die Performance der BLOBs ist gegenüber der normalen Datentypen grottig, also nur verwenden wenn man sie benötigt.

Grüße
Flo

22.11.2009 - 10:23 Uhr

Hi

Dann ist bei 4GB (IMHO) Schluss.

Sind "nur" 2GB, sollte aber für die meisten Anforderungen gut reichen. Wenn das wirklich nicht reicht geht noch FILESTREAM, der hat gar keine Begrenzung.

Grüße
Flo

22.11.2009 - 10:20 Uhr

Hi

Habe noch nicht wirklich viel mit EF2 gemacht, aber wenn's dir um die Grundsätzliche Frage geht ob PostgreSQL funktioniert:
http://blogs.msdn.com/adonet/archive/2008/10/14/npgsql-s-ado-net-provider-for-postgresql-supports-the-ado-net-entity-framework.aspx
(Den Blog kann ich allgemein empfehlen 😉 )

Grüße
Flo

22.11.2009 - 10:09 Uhr

Offtopic, weil T-SQL. Fällt aber wohl auch unter die Kategorie "nicht schön aber selten" 👅


DECLARE 
   @t VARCHAR(100) = 'Hallo Welt'
   ,@splt NVARCHAR(MAX),@unpvt NVARCHAR(MAX),@sql NVARCHAR(MAX);

WITH n (n, c) AS (
   SELECT number, CONVERT(VARCHAR(10), number)
   FROM master.dbo.spt_values
   WHERE type = 'P' AND number > 0
)
SELECT 
   @splt = STUFF((
         SELECT ',MAX(CASE WHEN n1 = ' + c + ' THEN SUBSTRING(@t, n1, 1) ELSE '''' END) c' + c + CHAR(10)
         FROM n WHERE n <= LEN(@t) FOR XML PATH('')
         ),1,1,'')
   ,@unpvt = STUFF((
         SELECT ',c' + c FROM n WHERE n <= LEN(@t) 
         FOR XML PATH('')
         ),1,1,'');

SELECT
   @sql = N'WITH n (n1) AS (
      SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1))
      FROM master.sys.all_columns c1 CROSS JOIN master.sys.all_columns c2
   ),
   line AS (
      SELECT ' + @splt + CHAR(10)
      + 'FROM n WHERE n1 <= LEN(@t)
   )
   SELECT chars
   FROM line UNPIVOT (chars FOR c IN (' + @unpvt + ')) AS unpvt'
   ;

--PRINT @sql;
EXECUTE sp_executesql @sql, N'@t VARCHAR(100)', @t = @t;

Grüße
Flo

21.11.2009 - 18:49 Uhr

Hallo Verena,

Wie FZelle schon gesagt hat, am besten kannst du mit dem Profiler sehen was passiert.

Wenn dein PRINT allerdings im SSMS nicht ankommt stimmt was nicht. Die Ausgabe sollte schon anknommen.

Kleines Beispiel:

CREATE TABLE TestTrigger
(
   Id INT NOT NULL IDENTITY(1,1)
      PRIMARY KEY CLUSTERED
   ,SomeInt INT
);
GO
CREATE TRIGGER TR_InsertTestTrigger ON TestTrigger
   AFTER INSERT
AS
BEGIN
   PRINT('Hello from trigger!');
END;
GO

INSERT INTO TestTrigger (SomeInt)
   VALUES (123);

Grüße
Flo

21.11.2009 - 17:16 Uhr

Hallo Atomroflman

Ziel des ganzen war dass ich an jeder Zeile sehen kann wer / wann zuletzt an daran geändert hat.

Das Prinzip habe ich erkannt, Trigger haben halt immer den großen Nachteil dass man nicht sieht was passiert. Zum Hinzufügen von User/Zeitstempel Informationen kann man sie schon verargumentieren (wobei ich eine Prozedur trotzdem besser verständlich finde).

Woran ich mich eher aufgehängt habe ist einmal der leere CATCH-Block und dein DELETE Trigger. Mach' doch eine History-Tabelle und schieb' die gelöschten Daten da rein, das ist wesentlich verständlicher.

Grüße
Flo