Laden...

simple Datenbank Erstellung/Verwaltung

Erstellt von Tac vor 11 Jahren Letzter Beitrag vor 11 Jahren 3.500 Views
Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren
simple Datenbank Erstellung/Verwaltung

verwendetes Datenbanksystem: <SQL Database>

Hey,
Also ich habe eigentlich grade erst zu meinem derzeitigen Projekt mit C# angefangen und mich versucht so gut es geht in viele Aspekte einzulesen.
Ich möchte dafür grade ein paar Formulare eingeben lassen und in einer Datenbank speichern.
So schaffe ich es auch schon eine SQL Datenbank mit einer Tabelle zu erstellen und für die Werte Eingabe Felder usw. zu erstellen.

1.
Ich habe jetzt nur das Problem, dass Visual meine Primärschlüssel-Kombination aus Name/Wochen-Anfangsdatum/Wochentag irgendwie nicht so akzeptieren will wie ich das gerne hätte.
Eigentlich möchte ich, dass jeder Benutzer für jede Woche jeweils für 7 Wochentage verschiedene Einträge erstellen kann.
Also ein Benutzer(Name) soll beliebig viele Einträge besitzen. Ein Wochendatum soll auch häufiger vorkommen könne(für alle ihre 7 Wochentage).
Es soll also nur die Kombination von Name/Datum/Wochentag einzigartig in der Tabelle sein.

Das ganze habe ich wie im Anhang zu sehen versucht.
Wenn ich darauf hin aber zwei Einträge mit dem gleichen Datum und dem gleichen Namen erstelle bekomme ich eine > Fehlermeldung:

NotNullAllowedException

Wenn ich das ganze umgehe in dem ich das Datum aus dem Primärschlüssel nehme funktioniert es zwar aber es ist sehr unschön und ich kann das Formular nicht mehr wie beabsichtigt speichern.

2.
Des Weiteren würde ich eigentlich gerne in dem gesamten Programm eine Benutzer-Verwaltung mit verschiedenen Rollen und Zugriffsrechten auf die Datenfelder einbauen. (Also Benutzer X kann nur Einträge mit seinem Namen sehen, Benutzer Y kann alle sehen)

Ich hoffe, dass ich das Forum hier nicht zu sehr mit meinen lächerlichen Anfängerproblemen voll mülle und es jemanden gibt der mir trotzdem unter die Arme greifen mag =)

Zu Punkt 2. bin ich über Tipps/Erklärungen sehr dankbar, Links zu Erklärungen/Tutorien usw. sind aber auch vollkommen ok, ich mag mich da wohl einlesen - habe dazu nur bisher noch nicht so viel gefunden.

B
357 Beiträge seit 2010
vor 11 Jahren

Hm, wie das mit dem Designer geht, weiß ich nicht. Hast du es mal mit einem SQL-Statement probiert?

create table Tagebuch (
     Name varchar(100) not null,
     Datum datetime not null,
     Tag integer not null,
     primary key (Name, Datum, Tag)
);

Eine NotNullAllowedException sagt aber doch schon aus, was Sache ist - es wurde ein Wert nicht belegt, der belegt werden muss.

Generell ist auch das Feld "Tag" völlig überflüssig, da es sich über "Datum" ermitteln lässt. Lass das weg, Thema Normalisierung und so.

Benutzerrechte... hm, so wie ich das sehe, musst da in deinem Fall einfach die WHERE-Klausel deiner SQL-Statements entsprechend setzen, dass ein Nutzer eben nur seine Einträge ändert. Ich sehe da jetzt das Problem nicht.

106 Beiträge seit 2011
vor 11 Jahren

Hallo Tac,

als erstes würde ich dir empfehlen einen O/R-Mapper zu nutzen, die bekanntesten sind NHibernate und Entity Framework, schau einfach mal ob dich einer davon anspricht.

Ich habe jetzt nur das Problem, dass Visual meine Primärschlüssel-Kombination aus Name/Wochen-Anfangsdatum/Wochentag irgendwie nicht so akzeptieren will wie ich das gerne hätte.

Als nächstes würde ich dir davon abraten solche wilden Schlüsselkombinationen zu nutzen, benutze einfach einen int bzw. long für die ID, es sei denn es ist zwingend notwendig solche zusammen gesetzten Schlüssel zu nutzen, aber die Notwendigkeit sehe ich zumindest in deiner Beschreibung nicht.

Des Weiteren würde ich eigentlich gerne in dem gesamten Programm eine Benutzer-Verwaltung mit verschiedenen Rollen und Zugriffsrechten auf die Datenfelder einbauen. (Also Benutzer X kann nur Einträge mit seinem Namen sehen, Benutzer Y kann alle sehen)

Sowas löst man über die Programmlogik und nicht über die Datenbank. In der DB exisitiert normalerweise nur ein Benutzer der alle schreibrechte hat und dein Programm meldet sich dann als dieser Nutzer an. Es macht nur selten Sinn alle Benutzer deines Programms als eigenständige Benutzer der Db anzulegen.

Die Exception die du bekommst, sagt etwas anderes als deine Aussage, ich würde die Exception auch so interpretieren das du vergessen hast ein bestimmtes Feld zu füllen. Schau dir mal die ExceptionDetails an, da sollten weiterführende Infos drin stehen.

MfG
Rabban

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

@bredator
Danke schon einmal für die schnelle Antwort.
Mit SQL kenne ich mich eigentlich einiger Maßen aus,** wo kann ich in Visual bei meinem C# Projekt denn direkt die SQL Statements eingeben** 😃?
Die Exception flog obwohl alle Werte eingegeben wurden, nach einigem rumprobieren bin ich der Meinung, dass das so ist, weil er den Wert "Datum" per Se nicht mehrfach vorkommen haben wollte.

Mit den Benutzern wollte ich eigentlich das z.B. ein Admin alle ausgefüllten Formulare durch gehen und sehen kann, ein Benutzer aber nur seine eigenen.
Also über den "Bindingnavigator" der Admin alle Formulare durchlaufen kann.(Siehe Anhang)

@Rabban
Auch dir danke!

Zu der Schlüsselkombination, ich habe das ganze jetzt noch einmal überdacht und werde denke ich deinen Tipp mit der ID aufnehmen.
Das Formular soll eigentlich eine Art Wochen "Tagebuch" darstellen, d.h. hatte ich erst gedacht ich speichere die einzelnen Tage mit gemeinsamen "Wochen-Datum"(Erster Tag der Woche) aber dann werde ich wohl jedes Formular gesamt speichern mit den Werten:
FormularID
Name
Datum
Tag1Wert1
Tag1Wert2
Tag1Wert3
Tag2Wert1
Tag2Wert2
...
Tag7Wert1
Tag7Wert2
Tag7Wert3
(Es sollen täglich 3 Werte eingetragen werden)

Zur Nutzerverwaltung in der Programmlogik, dann kann ich die Formular Aufrufe ja sicher nicht über den "Bindingnavigator"(Siehe Anhang) abwickeln, wo/wie kann ich denn in der Programmlogik meine Datenbank Werte abrufen?(auch hier versuche ich mich mal selber schlau zu machen)

106 Beiträge seit 2011
vor 11 Jahren

Zur Nutzerverwaltung in der Programmlogik, dann kann ich die Formular Aufrufe ja sicher nicht über den "Bindingnavigator"(Siehe Anhang) abwickeln, wo/wie kann ich denn in der Programmlogik meine Datenbank Werte abrufen?(auch hier versuche ich mich mal selber schlau zu machen)

Ich persönliche arbeite nicht mit solchen Bindungen, die nehmen dir zwar viel arbeit ab, aber auch viel Kontrolle, deshalb nutze ich NHibernate. Dort kannst du dir explizit die benötigen Rechte deines angemeldeten Benutzers einfach aus der DB laden und dann entsprechend handeln.

Falls du nocht nicht weißt was ein O/R-Mapper ist, würde ich mich an deiner Stelle auf jedenfall mal reinlesen.

Das Formular soll eigentlich eine Art Wochen "Tagebuch" darstellen, d.h. hatte ich erst gedacht ich speichere die einzelnen Tage mit gemeinsamen "Wochen-Datum"(Erster Tag der Woche) aber dann werde ich wohl jedes Formular gesamt speichern mit den Werten:

Ich würde nur jeden einzelnen Termin speichern und nicht komplette Tage. Das ganze würde als Poco in etwa so aussehen:


public class Termin
{
	/// <summary>
	/// Jede Entität braucht eine Id
	/// </summary>
	public long Id { get; set; }

	/// <summary>
	/// Der Zeitpunkt des Termins mit Uhrzeit.
	/// </summary>
	public DateTime DateTime { get; set; }

	/// <summary>
	/// Eine Beschreibung des Termins
	/// </summary>
	public string Beschreibung { get; set; }
}

Natürlich gehören da noch der Benutzer rein und alle anderen Infos die du für den Termin brauchst, aber im groben und ganzen reichen die 3 Informationen aus um einen Termin zu erstellen.

MfG
Rabban

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

Ja gut klar, ich könnte Objektorientiert vorgehen und

Klasse Termin
Attribute:
ID
Datum

Klasse Tag
Attribute:
ID
Datum
Morgentermin
Mittagtermin
Abendtermin

Klasse Formular
Attribute:
ID
Tag[7]

erstellen. Ich hatte aber bisher nur etwas zur Nutzung der SQL DB gefunden, wo ich eine Tabelle in einer DB mit den entsprechenden Attributen die ich brauche erstelle und danach dann aus der Datenquelle die einzelnen Attribute auf mein Formular herausziehe, damit diese mit der DB synchronisiert werden.

Für die objektorientierte Variante müsste ich dann wissen wie ich anders herum aus einer meiner Klassen den Wert in der DB speichere 😄 mal gucken.

Ich bin mit den vorherigen Tipps aber auch schon so weit gekommen das ich die Daten jetzt eingeben und speichern kann, es würde also nur noch eine Rechte-Verwaltung fehlen:

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

Das Formular sieht dann übrigens (zur besseren Vorstellung und zum Verständnis) so aus:

(es fehlt also nur noch die Benutzerverwaltung, von den Dingen von denen ich keine Ahnung habe)

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

Da ich denke das folgendes auch in die Datentechnologie gehört:

Ich kämpfe derzeit noch damit, wie ich den BindingNavigator (den ich hier schon benutzen möchte) programmiere um darin (z.B. der MoveNext_Click Methode) das abgespeicherte Formular abzufragen und den dort enthaltenen Namen mit dem Namen des derzeitigen Programm Benutzers abzugleichen.

Ich muss also irgendwie in meinem Form Code den aktuellen Tabelleneintrag nach dem Zeilenwert "Name" abfragen um ihn vergleichen zu können.

Kann ich das einfach mit SQL-Statements im normalen Code machen?
Ich denke, dass das nicht so schwer sein wird oder?

Hatte mir ungefähr so etwas gedacht:


private void bindingNavigatorMoveNextItem_Click(object sender, EventArgs e) {
            if (this.formularBindingSource.Position + 1 < this.formularBindingSource.Count) {
                this.formularBindingSource.MoveNext();
                this.formularBindingNavigator.Update();
            } 
        }

Hier dann quasi vor der "Update()" Methode checken wie der Name in dem Formular lautet und ihn mit dem aktuellen Benutzer Namen abgleichen.

Achja die MoveNextItem_Click Methode wie sie in dem obigen Codeblock steht funktioniert übrigens auch nicht richtig, wobei ich nicht weiß wieso.
Sie springt immer genau 2 Einträge weiter statt 1em. Obwohl ja nur einmal .MoveNext() aufgerufen wird - also wenn hier auch jemand eine Ahnung hat.

B
357 Beiträge seit 2010
vor 11 Jahren

Nein, das wäre ziemlich unsauber. Zuerst via SQL die betroffenen Datensätze auswählen und die zurückgegebenen Daten als BindingSource auswählen. Dadurch lassen sich auch Ressourcen sparen, da du sonst immer alle Daten vorhalten musst, die du aber gar nicht brauchst.

F
10.010 Beiträge seit 2004
vor 11 Jahren

@Tac:
Statt irgendwas herumzuprobieren würde es dir eher helfen mal zu verstehen was Du überhaupt machst.

  1. Der BindingNavigator springt schon einen weiter, die Routine ist zum aufräumen, nicht zum durchführen.
  2. Wenn du ein wechseln des Datensatzes erkennen willst, benutze BindingSource.PositionChange.
  3. Und über die BindingSource kannst Du natürlich auch den aktuellen Datensatz erfragen ( Current ).
  4. [Hinweis] Wie poste ich richtig? Punkt 1.1
Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

@Tac:
Statt irgendwas herumzuprobieren würde es dir eher helfen mal zu verstehen was Du überhaupt machst.

Total im Recht! Ich habe auch endlich einen Guide gefunden, der mich in genau dem Punkt den ich gesucht habe weiter bringt und das ganze -wie ich finde- hervorragend erklärt:
Free C#Net Course

  1. Der BindingNavigator springt schon einen weiter, die Routine ist zum aufräumen, nicht zum durchführen.

Mir ist auch schon aufgefallen, dass die Methode oder vor der Methode wohl so oder so (egal was ich in die Routine schreibe) einen vor springt.
Aber ich denke ich werde das ganze jetzt wie in dem Guide beschrieben selber ansprechen und über Buttons steuern (Siehe Code unten) 😁


  1. >
    Punkt 1.1

Danke für den Hinweiß, habe mir jetzt Mühe gegeben die Forenposts noch mehr durch zu wälzen und online nach Guides/Erklärungen zu suchen.


namespace Employees_Database {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        System.Data.SqlServerCe.SqlCeConnection con;
        DataSet ds1;
        System.Data.SqlServerCe.SqlCeDataAdapter da;
        int maxRows = 0;
        int inc = 0;

        private void Form1_Load(object sender, EventArgs e) {
            con = new System.Data.SqlServerCe.SqlCeConnection();
            con.ConnectionString = "Data Source=C:\\Users\\Tac\\Documents\\Databases\\Visual 2012\\Database1.sdf";
            con.Open();
            MessageBox.Show("Connection open");
            ds1 = new DataSet();
            String sql = "SELECT * FROM tbl_employees";
            da = new System.Data.SqlServerCe.SqlCeDataAdapter(sql,con);
            da.Fill(ds1,"Workers");
            NavigateRecords();
            maxRows = ds1.Tables["Workers"].Rows.Count;

            con.Close();

        }

        private void NavigateRecords() {
            DataRow dRow = ds1.Tables["Workers"].Rows[inc];
            textBox1.Text = dRow.ItemArray.GetValue(1).ToString();
            textBox2.Text = dRow.ItemArray.GetValue(2).ToString();
            textBox3.Text = dRow.ItemArray.GetValue(3).ToString();
            textBox4.Text = dRow.ItemArray.GetValue(4).ToString();
        }

        private void btnNext_Click(object sender, EventArgs e) {
            if (inc < maxRows - 1) {
                inc++;
                NavigateRecords();
            }
            else {
                MessageBox.Show("No more Rows","End of Datatable");
            }
        }

        private void btnPrevious_Click(object sender, EventArgs e) {
            if (inc > 0) {
                inc--;
                NavigateRecords();
            }
            else {
                MessageBox.Show("Reached first Record","Begin of Datatable");
            }
        }

        private void btnLast_Click(object sender, EventArgs e) {
            if (inc != maxRows-1) {
                inc = maxRows-1;
                NavigateRecords();
            }
        }

        private void btnFirst_Click(object sender, EventArgs e) {
            if (inc != 0) {
                inc = 0;
                NavigateRecords();  
            }
        }
    }
}

Ich denke, ich werde das ganze in einer Art wie in dem Codeblock hier umsetzen.
Mein einziges Problem ist nur, dass ich das Projekt am ende mit eine exe.-Datei veröffentlichen muss, ich in diesem Fall aber eine lokale DB auf meinem Rechner anspreche.


con.ConnectionString = "Data Source=C:\\Users\\Tac\\Documents\\Databases\\Visual 2012\\Database1.sdf";

Kann mir vielleicht wenigstens jemand sagen** wie ich auf eine DB in dem veröffentlichen exe.-Package zugreife?**

F
10.010 Beiträge seit 2004
vor 11 Jahren

Kann mir vielleicht wenigstens jemand sagen wie ich auf eine DB in dem veröffentlichen exe.-Package zugreife?

Garnicht.

Eine Veränderbare Datei sollte niemals im Programmverzeichnis stehen, da dort unter vernünftigen Betriebssystemen kein Schreibzugriff erlaubt ist.

Also musst du den Pfad auf Environment.GetFolderPath() mit entsprechenden werten für (Common)ApplicationData umstellen.

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

@FZelle:
Danke für die schnelle Antwort, ich versteh bei dem " mit entsprechenden werten für (Common)ApplicationData umstellen." leider nur Bahnhof, könntest du vielleicht eine kurze Beispiel Zeile posten oder mir kurz erklären wodrum es sich bei (Common)ApplicationData handelt?
Also wodrauf ich achten muss oder wie ich halt weiß was da in meinem Fall rein muss?

Edit: Ah wenn ich es richtig verstehe müsstest du das hier gemeint haben:

String databaseFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)+"gesuchteDB.sdf";

Dürfte das so klappen 😄?

Also ich bin jetzt so weit:


connection = new System.Data.SqlServerCe.SqlCeConnection();
String connectionString = "Data Source=" + System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\dataBase1.sdf";
connection.ConnectionString = connectionString;
connection.Open();
connection.Close();

Der connectionString bekommt jetzt auch den richtigen Ordner Path rein:
"f:\C:\User\Name\Documents\Visual Studio 2012\Projects\Form1\Form1\bin\debug\dataBase1.sdf"

Dann wird aber bei dem Befehl

connection.Open()

die ArgumentException "URI Formate werden nicht unterstützt" ausgegeben.
Dabei handelt es sich hierbei doch um kein URI Format sondern einen richtigen FolderPath?

Weiterhin funktioniert die .Open() Methode, wenn ich die DB in einen eigenen Ordner kopiere und den Pfad manuell angebe.

Jemand eine Ahnung? Ich bin echt überfragt

16.842 Beiträge seit 2008
vor 11 Jahren

Eigentlich wäre jetzt der Zeitpunkt, dass man diesen Thread schließt.
So ziemlich jeder Beitrag von Dir kratzt an einer Grundlagenfrage oder dessen Inhalt ist mit einem simplen 10 Sekunden Blick in die Dokumenation zu lösen.
Egal ob es Dein MoveNext der BindungSource war, was CommonApplicationData ist oder eine Frage wie "klappt das so"? Du wurdest nun mehrere Male von verschiedenen Leuten auf die Forenregeln hingewiesen.

Wie wärs wenn Du einfach mal die Dokumentation in die Hand nimmst und etwas ausprobierst?

"c# assembly codebase uri" spuckt Google How do I get the path of the assembly the code is in? als ersten Treffer aus und welch ein Wunder: die ersten beiden Antworten zeigen, dass das was Du tust in diesem Fall einfach falsch ist.
Natürlich ist die Rückgabe ein Uri, da der Pfad mit file:// beginnt (Dein F:/ ist wahrscheinlich schlecht kopiert). Und noch eine phänomenale Entdeckung dieses Links: es geht auch einfacher, mit Location!

Alles zusammen gesehen ist nun doch der Punkt erreicht, dass Du Dir dringend mal ein wenig mehr Eigenintiative aneignen, die Dokumentation und Google endlich verwenden solltest.
Ich lass den Thread noch offen, in der Hoffnung, dass Du es endlich verstanden hast.

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

@Abt

Tut mir leid, ich recherchiere schon -wie ich finde- recht viel.
Ich kenne mich nur einfach mit C# garnicht aus und allgemein bin ich auch kein Programmier-Crack wie die meisten anderen hier wahrscheinlich.
Wäre ja schön wenn das noch einmal kommen könnte .)
Die C# Dokumentation kannte ich bisher noch nicht, für einzelne Klassen werde ich mir die jetzt aufjedenfall vornehmen =)

"c# assembly codebase uri" spuckt Google How do I get the path of the assembly the code is in? als ersten Treffer aus und welch ein Wunder: die ersten beiden Antworten zeigen, dass das was Du tust in diesem Fall einfach falsch ist.

Darauf nach "assembly codebase" zu suchen wäre ich z.B. nicht einmal gekommen.

Verärgern wollte ich niemanden, ich habe wohl einfach ein bischen Streß und Zeitdruck momentan und das gepaart mit großer Unerfahrenheit.
Wenn ich, wie ich bei dir den Eindruck habe, euch verärgere, dann lösch den Thread ruhig.

Habe das mit location jetzt eingebaut:


            String connectionString = "Data Source=|DataDirectory|\\Database1.sdf";
            String executable = System.Reflection.Assembly.GetExecutingAssembly().Location;
            String path = (System.IO.Path.GetDirectoryName(executable));
            AppDomain.CurrentDomain.SetData("DataDirectory", path);

So läuft es jetzt auch im Editor, nur nach der Veröffentlichung/Installation findet er in dem "C:\Users\Name\AppData\Local\Apps\2.0\9E0GCT8K.WVE\KYYL0ERB.678\Program" Ordner keine Datenbank mehr.
Ich suche mal weiter ob/wie man so etwas beheben kann, sollte ich es finden poste ich dann hier die Lösung und damit wäre der Thread dann auch sowieso beendet denke ich.

1.696 Beiträge seit 2006
vor 11 Jahren

@Abt

Tut mir leid, ich recherchiere schon -wie ich finde- recht viel.
Ich kenne mich nur einfach mit C# garnicht aus

Das Forum setzt aber Grundlagenkenntnisse voraus. Denn ohne Grundlagenkenntnisse ist es meist so, dass der Fragende nicht versteht, was man ihm mitteilt, was bei dir anscheinend auch der Fall ist, und so kommt man nicht weiter.

Ich bin verantwortlich für das, was ich sage, nicht für das, was du verstehst.

**:::

Tac Themenstarter:in
11 Beiträge seit 2013
vor 11 Jahren

@vbprogger:
Ja dann hast du wohl recht und ich bin hier leider falsch 😉
Dachte halt als Anfänger könnte man hier auch wohl etwas posten, dass die Leute einfach drüber weg lesen würden wenn es sie nicht interessiert/eher nervt.

Werde mir in den kommenden Wochen, wenn ich Zeit habe, mal richtig die Grundlagen vornehmen. Dann kann ich hier ja vielleicht nochmal sinnvoll ein Thema posten 🙂