Laden...

Forenbeiträge von Moritz83 Ingesamt 50 Beiträge

08.11.2019 - 20:50 Uhr

@FZelle
Danke für den Hinweis, die haben schicke Controls, aber irgendwie noch nicht das was ich suche. Werde mal Github durchforsten, denke da könnte ich wirklich fündig werden

05.11.2019 - 15:25 Uhr

Sowas hatte ich mir schon gedacht dass es auf etwas kostenpflichtiges hinauslaufen wird. Werde mal die gängigen Anbieter durchforsten und dort ggf. was holen wenn es der Preis zulässt.

Nicht falsch verstehen, habe absolut nix gegen gute Arbeit die entsprechend etwas kostet, nur gerade als Anfänger ist man doch eher "kostenbewusst" unterwegs 😁

Danke dir und einen schönen Abend!

05.11.2019 - 13:57 Uhr

Moin,

falls der Thread hier fehl am Platze ist bitte einfach verschieben oder löschen.

Ich möchte in einem WPF Projekt eine Art Ferienplanner realisieren ähnlich dem Beispiel im Anhang. Oben eine Zeitleiste (am liebsten Monat für Monat, drunter eine Aufteilung in Tage, eventuell halbe Tage), links eine Liste aufgeteilt nach Abteilung und darunter alle dazugehörigen Mitarbeiter und dann im Datenbereich entsprechende farbige Balken.

Hat jemand sowas schon mal gemacht resp. kann mir ein NuGet Package empfehlen? Am Liebsten natürlich unter einer GNU Lizenz, aber falls es etwas kosten sollte wäre das auch in Ordnung.

Habe bereits etwas gegoogelt und bin über diverse Gantt Sachen gestolpert, allerdings waren die Meisten entweder uralt oder absolut oversized für meine Bedürfnisse da es mir primär nur um die Darstellung geht. Die Eingabe kann und wird über ein simples Formular erfolgen, sprich Sachen wie QuickEdit und Co bräuchte ich nicht.

EDIT:
Suche nicht eine fertige Lösung, nur einen Ansatz für die Maske an sich

30.10.2019 - 15:15 Uhr

Yep, wer lesen kann ist klar im Vorteil 😃

Mir ist aber immer noch nicht alles klar (leider), vor Allem wie die Befüllung mit Daten stattfinden soll. Ich habe ein anderes Beispiel gefunden

-->https://stackoverflow.com/questions/41368164/automapper-to-join-records-from-2-tables-into-single-ienumerable-viewmodel

Er "joined" 2 Listen und gibt t3 zurück. Wenn ich das nun "Quick und Dirty" darstelle kriege ich für die IEnumerable "result" tatsächlich das Department als Klartext zurück.

        public TestViewModel(IEmployeeRepository Emp, IDepartmentRepository Dep)
        {
            _emp = Emp;
            _dep = Dep;
            var config = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<EmployeeEntity, Combined>();
                cfg.CreateMap<DepartmentEntity, Combined>();
            });
            var mapper = config.CreateMapper();
            var t1 = new ObservableCollection<EmployeeEntity>(_emp.GetAll().ToObservable());
            var t2 = new ObservableCollection<DepartmentEntity>(_dep.GetAll().ToObservable());

            var result = t1.Join(t2, t => t.DepartmentFK, t => t.Id, (EmployeeEntity, DepartmentEntity) =>
            {
                var t3 = mapper.Map<EmployeeEntity, Combined>(EmployeeEntity);
                t3 = mapper.Map(DepartmentEntity, t3);
                return t3;
            });
            foreach (var item in result)
            {
                MessageBox.Show(item.FirstName);
                MessageBox.Show(item.Department);
            }
        }

Scheint also zu funktionieren, nun stellen sich 2 Fragen:
1.) Ist das so legitim?
2.) einen Teil der Mapper Sachen in die Bootstrapper.cs Datei und den Rest im ViewModel belassen oder in eine "Logik" Datei auslagern?

PS:
Als Alternative kam mir vorhin noch in den Sinn das man ja auch eine separate SQL Abfrage machen könnte und diese dann in die ObservableCollection wirft, komplett ohne Mapping (allerdings "wurmt" mich dieser Ansatz und ich will eigentlich das Ganze per Mapping lösen)

30.10.2019 - 06:29 Uhr

Packe den Mapper, wenn denn alles funktioniert, in den Bootstrapper. Hab das aber absichtlich noch nicht gemacht damit ich im Notfall einfach die Datei löschen kann.

Ne klar wundere ich mich nicht 🙂 wichtig war mir gestern nur das zumindest das mit den Employees klappt und das Mapping funktioniert.

Ja ausser Combined fiel mir echt nix besseres ein auf die Schnelle 🙂
Kannst du mir verraten warum du erst "Ignore" machst und dann die Properties zuweist?

Muss nochmal die Doku von Automapper studieren, irgendwie muss das doch klappen

29.10.2019 - 22:32 Uhr

(Bitte den letzten Post ignorieren)
Ich hake hier nochmal nach da mir eure beiden Antworten ("vermeide Fehlerquellen mit der Logikschicht" und "bau dir ein eigenes ViewModel und mappe das entsprechend") keine Ruhe lassen. Hab jetzt soweit fast alles in anständiger Form (aus meiner Sicht) und will das nun auch noch hin kriegen!

in der Theorie habe ich folgende Ausgangslage:
Entity 1

public class EmployeeEntity : Entity
    {
        public EmployeeEntity()
        {...}
        public string FirstName
        {...}
        public string LastName
        {...}
        public int DepartmentFK  //Foreign Key zur Tabelle Departments in der SQLite Datei
        {...}
    }

Entity 2

public class DepartmentEntity : Entity
    {
        public string Department
        {...}
    }

für meine View erstelle ich nun ein neues ViewModel das die entsprechenden Properties bereit stellt die ich dann in eine ObservableCollection packen kann (keine Entity kommt hier direkt vor) und die View kann per Binding drauf zugreifen.

public class Combined : Entity
    {
        public string FirstName
        {...}
        public string LastName
        {...}
        public string Department
        {...}
    }

soweit komme ich ja noch mit, allerdings geht es jetzt ja darum die Werte aus der DB zu holen. Bis jetzt ging das via "Schmeiss die Entity in eine generische Dapper Methode und gib mir alle Zeilen raus", sollte man ja nun nicht mehr machen. Wenn ich die Automapper Tutorials richtig verstanden habe kann ich ein Mapping erstellen und dann direkt im Mapping die Resultate der neuen ObservableCollection zuweisen. Das ViewModel File sähe nun in etwa so aus

    public class TestViewModel : Screen
    {
        private readonly IEmployeeRepository _emp;
        private readonly IDepartmentRepository _dep;
        private ObservableCollection<Combined> _test;
        public TestViewModel(IEmployeeRepository Emp, IDepartmentRepository Dep)
        {
            _emp = Emp;
            _dep = Dep;
            var config = new MapperConfiguration(cfg => {
                cfg.CreateMap<EmployeeEntity, Combined>();
                cfg.CreateMap<DepartmentEntity, Combined>().ForMember(dest => dest.DepartmentFK, opt => opt.MapFrom(src => src.Id));
                });
            var mapper = config.CreateMapper();
            var employees = _emp.GetAll().ToObservable();
            _test = mapper.Map(_emp.GetAll().ToObservable(), _test);
            _test = mapper.Map(_dep.GetAll().ToObservable(), _test);
        }

        public ObservableCollection<Combined> Test
        {
            get { return _test; }
            set { _test = value; }
        }
    }
    public class Combined
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int DepartmentFK { get; set; }
        public string Department { get; set; }
    }

Das funktioniert auch, allerdings killt der 2. _test = mapper.Map logischerweise meine Combined Collection. Bin ich hier generell auf dem richtigen Weg oder völlig daneben und Ihr meint etwas ganz anderes?

24.10.2019 - 17:42 Uhr

Was würdest du mir raten? Back to Autofac oder eherwas anderes?

24.10.2019 - 10:32 Uhr

Moin,

ich habe in meinem ersten Projekt Autofac integriert:

public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<DbContext>().As<IDbContext>();
builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>();
builder.RegisterType<DepartmentRepository>().As<IDepartmentRepository>();
return builder.Build();
}

und dann so eingebunden

var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var test = scope.Resolve<IDepartmentRepository>();
dept = test.GetById(DepartmentFK);
}

dies klappt auch soweit, allerdings schaue ich mir gerade anhand meines ersten Projekts "Caliburn.Micro" an und dachte mir das ich auch gleich den dort inegrierten "SimpleContainer" nutzen könnte. Ich habe den Bootstrapper bereits erstellt und der Teil mit dem Container sieht so aus:

private SimpleContainer _container = new SimpleContainer();
protected override void Configure()
        {
            _container.Instance(_container);

_container
.Singleton<IWindowManager, WindowManager>()
.Singleton<IEventAggregator, EventAggregator>()
.Singleton<IDbContext, DbContext>()
.Singleton<IDepartmentRepository, DepartmentRepository>();

GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}

Sodele, wenn ich nun folgendes mache:

public class Test
{
private readonly IDepartmentRepository _dep;
public Test(IDepartmentRepository Dep)
{
_dep = Dep;
}

public DepartmentEntity teststuff()
{
var test = _dep.GetById(MyId);
return test;
}
}

sollte ich ja schlussendlich das Gleiche kriegen, allerdings bedingt durch das fehlende Using bleibt der Container offen (wenn ich das richtig verstanden habe). Wie kann ich das mit SimpleContainer erreichen?

18.10.2019 - 23:34 Uhr

Ich würde mir genau für solche Fälle immer ein ViewModel bereitlegen und das entsprechend Mappen. Ein "xyzEntity" sollte nicht in einer ObservableCollection vorkommen.

Habe jetzt die letzten Stunden versucht raus zu kriegen wie das funktionieren soll, bin aber gescheitert. Die Idee hinter dem Mapping ist es ja die Properties von einem Objekt auf ein anderes zu kriegen OHNE jedesmal den ganzen Code bei Änderungen absuchen zu müssen.

Ich habe mal versucht was zu schustern, aber beim Versuch etwas vom ViewModel via Entity in die DB zu werfen bin ich kläglich gescheitert gescheitert. Mein Versuch sieht so aus:
die definerten Properties:

        #region Properties
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int DepartmentFK { get; set; }
        #endregion

und das Mapping

            //Mapping
            var config = new MapperConfiguration(cfg => cfg.CreateMap<EmployeeViewModelNew, EmployeeEntity>());
            var mapper = config.CreateMapper();
            //Source
            EmployeeViewModelNew EmployeeMapped = new EmployeeViewModelNew
            {
                FirstName = "Test",
                LastName = "User",
                DepartmentFK = 1
            };
            //Target
            EmployeeEntity newEmployee = new EmployeeEntity();
            //Sending to SQLite
            var container = ContainerConfig.Configure();
            using (var scope = container.BeginLifetimeScope())
            {
                var Sending = scope.Resolve<IEmployeeRepository>();
                Sending.Add(EmployeeMapped);
            }

Sodele, das Target hatte ich nur mal vorsorglich definiert aber dann nicht gebraucht. Den "EmployeeMapped" konnte ich auch nicht adden da mein "Add" Befehl nur eine

EmployeeEntity

akzeptiert. Der Code dazu in verkürzter Form:

    public interface IEmployeeRepository : ISqlRepository<EmployeeEntity>
    {
    }
    public class EmployeeRepository : SqlRepository<EmployeeEntity>, IEmployeeRepository
    {
        public EmployeeRepository(IDbContext dbContext) : base(dbContext)
        {
        }
        protected override string TableName { get; } = "Employees";
        protected override string IdField { get; } = "Id";
    }

und der entsprechende Add Befehl aus dem Repository

        public long Add(TEntity entity)
        {
            return DbContext.Connection.Insert(entity);
        }

--> Was funktionieren würde wäre sowas (irgendwie macht das aber keinen Sinn denn dann wäre das Mapping völlig umsonst - zumindest aus meiner Sicht).

                newEmployee.FirstName = EmployeeMapped.FirstName;
                newEmployee.LastName = EmployeeMapped.LastName;
                newEmployee.DepartmentFK = EmployeeMapped.DepartmentFK;
                Sending.Add(newEmployee);

Das Thema Mapping und aus der SQLIte Datei in eine Liste für die View packen habe ich komplett net geschnallt. Dazu habe ich nicht ein schlaues Beispiel gefunden, geschweige denn eins mit SQLite oder so.

Wäre für Hilfe echt dankbar damit ich das Thema zumindest etwas kapiere.

18.10.2019 - 16:04 Uhr

ne das Bild kenne ich natürlich, auch den Artikel habe ich gelesen. Bei mir liegt das Problem eher darin die Theorie in die Praxis umzusetzen. Aber das wird schon, Rom ist auch nicht an einem Tag erbaut worden 🙂

Bin dankbar für solche Inputs eurerseits 👍

18.10.2019 - 14:11 Uhr

@Abt
Das mit der ID war mir klar, hab nur deshalb übergeben damit ich beim zurückspielen eine ID zum updaten habe (generische Dapper Methode, übergebe eine EmployeeEntity). Hat halt auf diese Weise geklappt auch wenn es ziemlich wild aussieht.

@Coffeebean
Werde dazunochmals über die Bücher, habe das Zusammenspiel der einzelnen Elemente noch nicht begriffen. Gerade das Thema "ViewModel + Entity + Model" erschliesst sich mirnoch nicht ganz (Wer spricht mit wem, wer erbt von wem, mappen, etc)

Danke für euren Input 👍

18.10.2019 - 10:32 Uhr

Moin,

ich habe in meinem Projekt 2 Entities, einmal als Employee (Id, FirstName, LastName und DepartmentFK) und einmals als Departments (Id, Department). In der SQLite Datei sind DepartmentFK und Id (von Departments) verknüpft.

Um die Daten im UI nutzen zu können kann ich ja je eine ObservableCollection erstellen. Soweit ist mir das auch klar, nun will ich aber in (beispielsweise) einer ListView etwas darstellen was wie folgt aussieht:

FirstName - LastName - Department (sprich entsprechend DepartmentFK in der Employees Tabelle hole ich den Namen aus der Departmentstabelle)

Ich habe das nun wie folgt gelöst (funktioniert - bin mir aber nicht sicher ob das der "richtige" Weg ist:

1.) Definition einer neuen Entity CS Datei (abgeleitet von EmployeeEntity, aber halt mit dem Department)

        public class DisplayEmployeeEntity : EmployeeEntity
    {
        public DepartmentEntity Department { get; set; }
    }

2.) Im ViewModel eine neue ObservableCollection von "DisplayEmployeeEntity" erstellen

 public ObservableCollection<DisplayEmployeeEntity> DisplayEmployees { get; }
        public EmployeeViewModel()
        {
            DisplayEmployees = new ObservableCollection<DisplayEmployeeEntity>();
            foreach (var emp in Employees)
            {
                DisplayEmployees.Add(
                new DisplayEmployeeEntity()
                {
                    Id = emp.Id,
                    FirstName = emp.FirstName,
                    LastName = emp.LastName,
                    DepartmentFK = emp.DepartmentFK,
                    Department = Departments.Where(x => x.Id == emp.DepartmentFK).FirstOrDefault()
                });
            }
        }

3.) DisplayEmployees an die Listview in der View binden

Kann man das so lösen oder sollte ich einen anderen Weg gehen?
Mein anderer Ansatz war ein Property direkt in der Employee Entity zu erstellen (Derpartment) und das dann auszuschliessen (arbeite mit Dapper)

  [Computed]
  public DepartmentEntity Department { get; set; }

(so könnte ich ein SelectedItem direkt wieder updaten da es sich um die gleiche Entity handelt)

15.10.2019 - 10:24 Uhr

… habe vergessen der Klasse das "INotifyPropertyChanged" mitzugeben, aber das Event hatte ich bereits drin

Ist nicht mein Tag heute, danke dir für den Wink mit dem Zaunpfahl 👍

15.10.2019 - 09:54 Uhr

Moin,

ich habe ein kleines Problem mit meiner GUI. Meine WPF Anwendung besteht aus einer Listview, 2 TextBoxen und einem Button. Die Idee ist folgende:

Ich klicke auf einen Eintrag in der Listview, die beiden TextBoxen kriegen die entsprechenden Werte, ich ändere einen oder beide Werte, klicke auf den Button, werfe die Änderungen in die Datenbank und falls das klappt will ich die GUI aktualisieren. Irgendwie klappt das aber nicht so ganz 🤔

Listview:

        <ListView Name="Employees_Listview" ItemsSource="{Binding Employees, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}" Height="86">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}"/>
                    <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding LastName}"/>
                </GridView>
            </ListView.View>
        </ListView>

beide TextBoxen (diese sind absichtlich nicht direkt an das SelectedItem "YourSelectedItem" gekoppelt da ich ansonsten im ViewModel den aktualisierten Text nicht auslesen konnte -- habs net hingekriegt)

<TextBox Name="FirstNameTextBox" Width="136" Text="{Binding FirstName, Mode=TwoWay}" />
<TextBox Name="LastNameTextBox" Width="136" Text="{Binding LastName, Mode=TwoWay}" />

mein UpdateCommand

        private void UpdateEmployee()
        {
            var container = ContainerConfig.Configure();
            using (var scope = container.BeginLifetimeScope())
            {
                var SelectedEmployee = scope.Resolve<IEmployeeRepository>();
                YourSelectedItem.FirstName = FirstName;
                YourSelectedItem.LastName = LastName;
                if (!SelectedEmployee.Update(YourSelectedItem))
                {
                    MessageBox.Show("Datensatz konnte nicht aktualisiert werden!" + "\n" + "Bitte den Administrator verständigen!");
                    return;
                };
            }
        }

und meine 3 Properties

        //TEST
        private string lastname;
        public string LastName
        {
            get { return lastname; }
            set
            {
                lastname = value;
                NotifyPropertyChanged("LastName");
            }
        }
        private string firstname;
        public string FirstName
        {
            get { return firstname; }
            set
            {
                firstname = value;
                NotifyPropertyChanged("FirstName");
            }
        }
        //TEST
        public EmployeeEntity YourSelectedItem
        {
            get
            {
                return _yourSelectedItem;
            }
            set
            {
                if (value != null) { 
                FirstName = value.FirstName;
                LastName = value.LastName;
                }
                _yourSelectedItem = value;
                NotifyPropertyChanged("YourSelectedItem");
            }
        }

Sodele, wenn ich nun einen Eintrag anklicke, ändern sich die TextBoxen, ich kann etwas verändern, den Button drücken und die SQLite Datei wird aktualisiert. Wenn ich dann in der ListView andere Einträge anklicke und wieder zu meinem ursprünglichen zurück komme sehe ich in der TextBox den aktualisierten Wert, aber die ListView ist noch unverändert.

Hab rausgefunden das meine ObservableCollection so nur auf "Add" oder "Remove" reagiert, nicht aber wenn ich die Properties eines Items ändere.

Was kann ich hier tun?

PS: Ich habe diesen Ansatz bewusst gewählt da ich ja bei verknüpften (zum ausgewählten Element)TextBoxen das Problem hätte bei einem Fail zur Datenbank, die "alten" Werte wieder holen zu müssen (und keine Datenbank = keine alten Werte). Muss natürlich es nicht zwingen diese Lösung sein, aber es war für mich die "sinnvollste" Variante

11.10.2019 - 18:25 Uhr

@MarsStein
Super, danke für den Wink wie man so etwas angehen könnte. Auf die Idee mit der integrierten Fehlerbehandlung bin ich gar nicht gekommen, macht aber mehr als Sinn.

Klar, nun verstehe ich auch was Abt gemeint hat (wenn gleich das im ersten Augenblick vielleicht mehr als verwirrend war).

Merci für deine Hilfe und hilfreichen Tipps!

@Th69
Ja die Benennung ist wirklich schlecht in meinem Projekt. Hab da noch kein Händchen für. Meine Idee war erstmal ein funktionierendes "Etwas" auf die Beine zu stellen und mir dann Schritt für Schritt den Code durcharbeiten um eben sowas, als auch die von Abt und MarsStein angesprochenen Sachen zu ändern.

Bin definitiv noch ein Anfänger und habe tatsächlich nur Erfahrung mit Visual Basic (aber da auch eher Quick & Dirty als Ordentlich & Strukturiert). Ich habe natürlich die Methode gemeint.

Danke dir für deinen Input!

@Abt
auch dir vielen Dank (auch wenn ich Hilfe gebraucht habe um die Message zu verstehen 😉 )

11.10.2019 - 12:56 Uhr

entweder verstehe ich nicht was du mir sagen willst oder wir reden komplett aneinander vorbei. Hier ganz vereinfacht die Ausgangslage:

MainWindow.xaml

<ListView Name="MuhKuh" ItemsSource="{Binding EmployeeListView, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}" Height="86">
//Code
<Button Command="{Binding DeleteEmployee}" CommandParameter="{Binding EmployeesListView}" Margin="0,0,10,0">Delete</Button>
//Code
</ListView>

MainWindow.xaml.cs

        public MainWindow()
        {
            InitializeComponent();
            DataContext = new EmployeeViewModel();
        }

----> Binding ---->

EmployeeViewModel.cs (RelayCommand ist eine eigene Klasse in einer eigenen Datei)

       
//Definition der ObservableCollection für die View
//Code
//RelayCommand Definition
public RelayCommand DeleteEmployee
        {
            get
            {
                command = new RelayCommand(Delete);
                return command;
            }
        }

meine Frage wäre nun ob die Delete Void die ich aufrufe
a.) Das Löschen des Eintrags aus der ObservableCollection (die View kriegt durch das Binding ja automatisch ein Feedback) UND das Löschen des Eintrags aus der DB beinhaltet.

oder

b.) Das Löschen des Eintrags aus der ObservableCollection beinhaltet und die Funktion zum löschen des DB Eintrags in der SQLite Datei im besagten CollectionChanged Event steht

Sorry wenn ich mich vielleicht nicht klar ausgedrückt habe

11.10.2019 - 12:17 Uhr

Der Code steht auch in meinem ViewModel und net im Code Behind. Im Code Behind von MainWindow.xaml steht nur

        public MainWindow()
        {
            InitializeComponent();
            DataContext = new EmployeeViewModel();
        }
11.10.2019 - 12:13 Uhr

Moin,

ich habe folgenden Ausgangssituation (WPF MVVM):
Auf einer View habe ich eine Listview und einen Button mit dem ich einen Eintrag aus der Listview löschen und danach die SQLite Datei aktualisieren möchte.

Das mit dem Binding des Buttons klappt soweit, allerdings habe ich ein Verständnis Problem wie der Hintergrund aussehen sollte. Meiner Meinung nach habe ich 2 Möglichkeiten:

        public RelayCommand DeleteEmployee
        {
            get
            {
                command = new RelayCommand(Delete);
                return command;
            }
        }
  1. Möglichkeit
        public void Delete()
        {
            //aus Listview löschen
            //aus SQLite löschen
        }

oder ich gehe über die CollectionChanged Variante (eine ObservableCollection dient als Datenlieferant für die Listview) als

  1. Möglichkeit
EmployeeListView.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler
(CollectionChangedMethod);
        public void Delete()
        {
            //aus Listview löschen und CollectionChanged triggern
        }
        private void CollectionChangedMethod(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                //Aus SQLite löschen
            }
        }

Würde jetzt die 1. Möglichkeit nehmen da sie mir "sauberer" erscheint, wie "sollte" man das lösen?

07.10.2019 - 21:10 Uhr

Habs nun umgedreht, View -> ViewModel -> neuer "Employee" (anstatt neue EmployeeEntity) -> Validation funktioniert -> Mapper -> Entity -> neuer Eintrag in der DB

Danke für den dezenten Hinweis bzgl. Modellvalidierung <-> Entitätsvalidierung

Thread kann zu

07.10.2019 - 19:58 Uhr

Nabend,

ich habe folgende Ausgangssituation:

Employee.cs (Model inkl. IDaraErrorInfo)

 using System;
using System.ComponentModel;

namespace Calendar.Models
{
    public class Employee : IDataErrorInfo, INotifyPropertyChanged
    {
        //Employee ID -- Auto Increment (Database)
        public int Id { get; set; }
        private string _firstName;
        public string FirstName
        {
            get => _firstName;
            set
            {
                if (_firstName != null && _firstName != value)
                {
                    _firstName = value;
                }
                NotifyPropertyChanged("FirstName");
            }
        }
        //more code
        #region IDataErorInfo Members
        string IDataErrorInfo.Error
        {
            get
            {
                return null;
            }
        }
        string IDataErrorInfo.this[string propertyName]
        {
            get
            {
                return GetValidationError(propertyName);
            }
        }
        #endregion

        #region Validation
        //more code
        #endregion

        #region INotifyPropertyChanged Members
        //more code
        #endregion
    }
}

EmployeeEntity (Entity Klasse)

namespace Calendar.Database.Entities
{
    [Dapper.Contrib.Extensions.Table("Employee")]
    public class EmployeeEntity : Entity
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

und mein ViewModel

namespace Calendar.ViewModels
{
    internal class EmployeeViewModel : INotifyPropertyChanged
    {
        private RelayCommand command;
        private EmployeeEntity employee;
        /// <summary>
        /// Initializes a new instance of the EmployeeViewModel class.
        /// </summary>
        public EmployeeViewModel()
        {
            employee = new EmployeeEntity();
        }
        public EmployeeEntity Employee
        {
            get
            {
                return employee;
            }         
        }
        public RelayCommand AddNewEmployee
        {
            get
            {
                this.command = new RelayCommand(SaveChanges);
                return this.command;
            }
        }
        public void SaveChanges()
        {
            var container = ContainerConfig.Configure();
            using (var scope = container.BeginLifetimeScope())
            {
                var test = scope.Resolve<IEmployeeRepository>();
                employee.FirstName = this.Employee.FirstName;
                employee.LastName = this.Employee.LastName;
                test.Add(employee);
            }
        }
    }
}

Sodele, mein Problem ist dass das Model keinerlei Bezug zur Entity (und vice versa) hat und natürlich so auch keine Validation (die im Model definiert wurde) stattfinden kann. Gemäss Internet gibt es AutoMapper und der Code den ich bräuchte sähe etwa so aus

            var config = new MapperConfiguration(cfg => cfg.CreateMap<Employee, EmployeeEntity>());
            var mapper = config.CreateMapper();

Leider habe ich keinen Plan wie ich das einbauen kann, resp. beim letzten Versuch kam die Fehlermeldung das eine Konvertierung von EmployeeEntity in Employee nicht möglich sei. Der Vollständigkeitshalber noch ein Beispiel einer Textbox:

<TextBox Width="136" Text="{Binding Employee.FirstName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

In einem anderen Beispiel das ich gemacht habe ging das einwandfrei, allerdings ohne Entity dazwischen.

PS: Sollten mehr Infos notwendig sein einfach melden

06.10.2019 - 21:22 Uhr

ach so, ne den Connection String habe ich auch im 2. Beispiel bereits nicht mehr hard im Code. Hab den in der App.config verstaut (kopiere im Moment bei jedem Debug eine Datei in den bin Ordner zum ausprobieren)

 <connectionStrings>
    <add name="SQLite" connectionString="Data Source=Database\\DatabaseFile\\Database.db;Version=3;" />
  </connectionStrings>

Also gehört eine Entity gemäss Link zu Datenzugriffsschicht da damit ja eigentlich nur verwaltet werden, richtig? (Es wird ja nix verarbeitet sondern nur "verwaltet", resp. aus der DB ausgelesen - das SqlRepository gehört demnach auch dazu, oder?)

Was sich mir nicht ganz ausgeht:
Wenn ich nun in der Entity Definition

public string FirstName { get; set; }

schreibe und gleichzeitig in der Employee Model Datei

        public string FirstName
        {
            get => _firstName;
            set
            {
                //blub blub
            }
        }

steht so ist das doch doppelt gemoppelt, oder nicht? Ich mein ich definiere 2x das es einen String namens "FirstName" gibt.

Viel wichtiger ist, wo wird denn die Model Datei eingeklinkt? In der Entity steht ja

public class EmployeeEntity : Entity

sprich "FirstName" und "LastName" hole ich aus der EmployeeEntity und die "Id" aus der "normalen" Entity. Eigentlich müsste sich doch das Employee Model auf die Entität stützen, oder?

--> Habe nur folgendes Beispiel gefunden, hab aber keine Ahnung ob das so korrekt ist:
https://stackoverflow.com/questions/30319584/how-to-map-entity-framework-model-classes-with-business-layer-class-in-n-tier-ar

06.10.2019 - 20:19 Uhr

Sodele, mittlerweile habe ich die "Add" und die "Update" Variante auch eingebaut und es klappt wunderbar!

Add (hier musste ich allerdings "long" anstatt "int" nehmen - int wurde in der Dapper Doku genannt - warum kann ich nicht sagen, aber long funktioniert)

        public long Add(TEntity entity)
        {
            return DbContext.Connection.Insert(entity);
        }

Update

        public bool Update(TEntity entity)
        {
            return DbContext.Connection.Update<TEntity>(entity);
        }

Das 2. Beispiel für den Inhalt der DbContext habe ich mal angeguckt:
"noch besser weil die Connection zuerst geprüft wird (sprich ist bereits eine offene Verbindung vorhanden?) und weil es nun als SqLite Verbindungsdatei erkennbar ist?

Mir ist noch aufgefallen das nun die Datei "Employee.cs" (mein ursprüngliches Model) gar nicht mehr eingebunden ist. Gehe ich richtig in der Annahme das nun die Definition (als Beispiel)

        public string FirstName
        {
            get => _firstName;
            set
            {
                if (_firstName != null && _firstName != value)
                {
                    _firstName = value;
                }
                OnPropertyChanged("FirstName");
            }
        }

direkt in der EmployeeEntity gemacht wird und ich die "normale" Model Datei nicht mehr brauche?

04.10.2019 - 15:13 Uhr

Das mit dem Debugging hatte ich mittels Einzelschritt versucht (und eben auch um heraus zu finden, wann er wohin springt) aber wenn natürlich der Code an sich zwar ok aber trotzdem Blödsinn ist dann bringt dir das nicht viel.

Deine 2. Variante erinnert mich stark an mein Dependency Injection Beispiel von gestern, guck es mir daheim mal an.

A propos, den Teil mit "GetAll()" in SqlRepository.cs musste ich deaktivieren weil GetAll ein IEnumerable zurück gibt und daher

        public IList<TEntity> GetAll()
        {
            return DbContext.Connection.GetAll<TEntity>();
        }

nicht funktionieren kann (wenn ich mich nicht komplett täusche).

Nochmals vielen Dank für die tatkräftige Unterstützung!

Eine kleine Randnotiz noch:
Der Code funktioniert soweit, allerdings habe ich die lustige Eigenschaft dass die Tabelle im db File auf Entity enden muss, sprich "EmployeeEntity". Das finde ich aber raus warum das so ist. 😉

04.10.2019 - 14:03 Uhr

Die Return Zeile des folgenden Codes wirft die Exception aus (in der Dateil SqlRepository.cs):

        public TEntity GetById(int id)
        {
            return DbContext.Connection.Get<TEntity>(id);
        }

Ich habe die DbContext um folgenden Code ergänzt:

using System.Data.SQLite;

namespace Calendar.Database.DbContext
{
    public class DbContext : IDbContext
    {
        public SQLiteConnection Connection
        {
            get
            {
                return null;
            }
            set
            {
                using (var connection = new SQLiteConnection("Data Source=C:\\Database\\Database.db;Version=3;"))
                {
                    connection.Open();
                }
            }
        }
    }
}

Eine Datei namens ContainerConfig.cs beinhaltet das NuGet Paket Autofac für die Dependency Injection und der Code sieht so aus:

using Autofac;
using Calendar.Database.DbContext;
using Calendar.Database.Repositories;

namespace Calendar
{
    public static class ContainerConfig
    {
        public static IContainer Configure()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<DbContext>().As<IDbContext>();
            builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>();
            return builder.Build();
        }
    }
}

Ich war nach dem Tutorial der Meinung das der Code (vor Allem der Teil in der Klammer)

        public EmployeeRepository(IDbContext dbContext) : base(dbContext)
        {
        }

automatisch die Injection triggert aber ich bin da wohl auf dem Holzweg und geh da heute Abend nochmal über die Bücher.

Der genaue Fehlercode lautet übrigends:

System.NullReferenceException
  HResult=0x80004003
  Nachricht = Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
  Quelle = Dapper
  Stapelüberwachung:
   bei Dapper.SqlMapper.<QueryImpl>d__140`1.MoveNext() in C:\projects\dapper\Dapper\SqlMapper.cs: Zeile1066
   bei System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   bei System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   bei Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in C:\projects\dapper\Dapper\SqlMapper.cs: Zeile721
   bei DapperExtensions.DapperImplementor.GetList[T](IDbConnection connection, IClassMapper classMap, IPredicate predicate, IList`1 sort, IDbTransaction transaction, Nullable`1 commandTimeout, Boolean buffered)
   bei DapperExtensions.DapperImplementor.Get[T](IDbConnection connection, Object id, IDbTransaction transaction, Nullable`1 commandTimeout)
   bei System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
   bei DapperExtensions.DapperExtensions.Get[T](IDbConnection connection, Object id, IDbTransaction transaction, Nullable`1 commandTimeout)
   bei Calendar.Database.Repositories.SqlRepository`1.GetById(Int32 id) in C:\Users\mj\Source\Repos\Calendar_Azure\Calendar\Calendar\Database\Repositories\SqlRepository.cs: Zeile27
   bei Calendar.MainWindow..ctor() in C:\Users\mj\Source\Repos\Calendar_Azure\Calendar\Calendar\ViewModels\MainWindow.xaml.cs: Zeile24

04.10.2019 - 12:48 Uhr

hab versucht anhand von einem Tutorial die "Dependency Injection" einzubauen, allerdings stimmt da etwas noch nicht.

Wenn ich es start wirft die Datei "SqlRepository" einen "System.NullReferenceException: "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt." Fehler aus. Hab da wohl etwas falsch eingebaut 😭

02.10.2019 - 10:51 Uhr

Entschuldige die verspätete Antwort, krankheitsbedingt ist dies leider völlig in Vergessenheit geraten 😠

Ich habe die letzten Wochen versucht mittels des gekauften Buches die Grundlagen zu erlangen und mittlerweile leuchtet mir einiges ein was vorher unklar war. Habe mir jetzt dein OOP Beispiel vorgenommen und versuche im Moment jeden Schritt zu dokumentieren (warum und wieso) sowie nachzuvollziehen, was da genau abläuft, sprich wer gibt wem die Anweisung oder wer erbt von wem und was erbt er (und so weiter)

Mein jetziger Stand ist folgender:
Die Grundsatz Idee hinter deinem Ansatz ist die Definition einer generischen Struktur, sprich ich definiere die Funktionen (Löschen, Auslesen, etc) und je nachdem ob es ein Team oder ein Mitarbeiter ist wird immer dieselbe Funktion aufgerufen, nur die "Parameter" (wie zum Beispiel TableName) werden entsprechend geändert.

Um zu Prüfen ob alles funktioniert wollte ich im CodeBehind der XAML Datei eine Messagebox einbinden welche den Vor- und Nachnamen des Mitarbeiters mit der Id 1 ausgibt. Wäre jetzt über das EmployeeRepository eingestiegen aber ich kriege den Befehl net hin. (Vermute der Schluss heisst dann irgendwas a la GetById(1).FirstName😉

PS: der Kram im CodeBehind dient nur zum Testen, habe miich noch nicht mit XAML beschäftigt

PSS:
Hab das ganze Projekt neu hochgeladen, ist einfacher da was nachzugucken
--> https://moritzjuergensen.visualstudio.com/_git/Calendar?path=%2FCalendar&version=GBmaster
Den Connection String habe ich unter "Projekteigenschaften - Ressourcen" definiert. Die DbContext Datei habe ich erstellt, aber das "Set" fehlt mir noch, vielleicht kannste mir hier auch noch auf die Sprünge helfen damit ich anhand eines funktionierenden Modells das ganze mittels Buch nochmals rekapitulieren kann...Falls du mal in der Gegend um Lindau oder der Ostschweiz bist lade ich dich für die Hilfe mal auf ein Bier ein 👍

14.05.2019 - 22:12 Uhr

Aber wie bereits gesagt: pragmatisch bleiben ist legitim.

Ja ich muss irgendwo ne Grenze ziehen, ansonsten habe ich zu viel drin was ich nicht wirklich verstehe. Habe jetzt schon Schwierigkeiten den ganzen Sachen zu folgen.

Du musst Dich wirklich ein wenig mit den Themen beschäftigen...

und damit wären wir bei Problem Nummer 2: Ich für meinen Teil finde es extrem schwierig anhand von abstrakten Erklärungen daraus die richtigen Schlussfolgerungen zu ziehen, zumal mir hier im RL auch niemand wirklich helfen kann. Beispielsweise dein Link zu den generischen Klassen.
Klar steht dort sehr viel und vermutlich auch sehr gut erklärt aber für mich als Laien doch schwer zu interpretieren und dann in ein funktionierendes Beispiel einzusetzen.

Wenn ich ein konkretes Beispiel habe (wie jetzt in meinem Fall die aus deiner Sicht sicherlich sehr simple Anwendung) dann kann ich anhand meines Codes die Theorie nachvollziehen, resp. ich kann es vermutlich besser als andersrum.

Die Idee mit "GetByID" greife ich auf, zumal ich in meiner Anwendung in jeder Tabelle eine eindeutige ID habe und auch haben werde.

Vielen Dank für deine Geduld! 😃 Ich denke ich werde jetzt erstmal wieder über die Bücher und versuche deinen umfangreichen Input irgendwie zu interpretieren und dann umzusetzen bevor ich auch nur daran denken sollte weiter zu machen. Werde auch im Buch weiter machen und parallel die Grundlagen nochmals durchlesen und vertiefen.

14.05.2019 - 18:25 Uhr

irgendwas geht sich bei mir net aus.

Versuche mal aufzuzeigen wo meine Denkblockade ist:

In der Datei "SqlRepository.cs" sind die Datenbankaugaben wie "alle Einträge holen" oder "Eintrag updaten" abgelegt. Der Code

    class SqlRepository<TEntity> : ISqlRepository<TEntity> where TEntity : class, Entity
    {
        //BLIBLABLUB
    }

besagt das es sich um eine Klasse "SqlRepository" handelt die als Parameter eine TEntity erfordert (stellt in diesem Fall Mitarbeiter oder Team dar), die vom Interface "ISqlRepository" abhängig ist und der Parameter nur gültig ist wenn es eine Klasse ist die von Entity erbt -- in deinem Beispiel

public class EmployeeEntity: Entity {}

Die Klasse "EmployeeEntity" erbt von Entity und sie wie folgt aus:

public class EmployeeEntity : Entity
{
    public int Id {get;set;}
    public string Vorname {get;set;}
    public string Nachname {get;set;}
    // ...
}

Sprich hier ist definiert welche Eigenschaften vorhanden sind

Widerspruch 1 für mich: Wieso brauche ich denn ein Model "Employee.cs" in dem fast das Gleiche steht? Kann es ja net doppelt definieren --> Annahme: Employee.cs fällt weg

Widerspruch 2 für mich: Das mit dem " : Entity" Zusatz verstehe ich gar nicht. Was soll denn da drin stehen?--> Annahme: Gemäss folgendem Link (abstrakte Klasse) vermute ich das diese Klasse Sachen beinhaltet die für beide Entities (Team oder Mitarbeiter) relevant sind, wobei sich mir hier net erschliesst was das sein soll

Die ganz ganz groben Züge erschliessen sich mir langsam aber irgendwie bleibt jedesmal ein Fragezeichen, daher vielen Dank das Ihr meine "blöden" Fragen so umfassend beantwortet 😃
Hoffe das ich mir eurer Hilfe bald eine funktionierende Klassenbibliothek habe mit der ich das ganze nochmals durchgehen kann; sprich den kompletten Workflow innerhalb der Klasse abarbeiten kann.

14.05.2019 - 15:59 Uhr

Woher kommt denn das ": Entity"?

public class EmployeeEntity : Entity

Müsste ich das nicht auf das Employee.cs File im Model Ordner referenzieren?

14.05.2019 - 14:23 Uhr

Ich habe die letzten Tage versucht das Beispiel von Abt (OOP) in das von ihm netterweise zur Verfügung gestellte Rahmenprojekt einzubauen. Mir erschliessen sich noch einige Sachen nicht so wirklich, darum brauche ich (mal wieder) euren Rat.

Die Struktur hat sich leicht verändert im Gegensatz zum Originalprojekt (siehe Bild im Anhang)

Der Inhalt der einzelnen Dateien sieht nun wie folgt aus:
EmployeeEntity.cs

namespace Toolbox.Employee.Database.Entities
{
    class EmployeeEntity
    {
    }
}

EmployeeRepository.cs

using System.Data;
using Toolbox.Employee.Database.Entities;

namespace Toolbox.Employee.Database.Sqlite.Repositories
{
    class EmployeeRepository : SqlRepository<EmployeeEntity>
    {
        public EmployeeRepository(IDbConnection connection) : base(connection)
        {
            GetAll();  //BEISPIEL
        }
    }
}

IEmployeeRepository.cs

namespace Toolbox.Employee.Database.Sqlite.Repositories
{
    class IEmployeeRepository
    {
    }
}

ISqlRepository.cs

namespace Toolbox.Employee.Database.Sqlite.Repositories
{
    internal interface ISqlRepository<TEntity>
    {
    }
}

SqlRepository.cs

using Dapper.Contrib.Extensions;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace Toolbox.Employee.Database.Sqlite.Repositories
{
    class SqlRepository<TEntity> : ISqlRepository<TEntity> where TEntity : class
    {
        IDbConnection _connection;

        public SqlRepository(IDbConnection connection)
        {
            _connection = connection;
        }

        public IList<TEntity> GetAll()
        {
            return _connection.GetAll<TEntity>().ToList(); // GetAll = Dapper
        }

        public bool Update(TEntity e)
        {
            return _connection.Update(e);
        }
    }

}

DbSqliteContext.cs

namespace Toolbox.Employee.Database.Sqlite
{
    class DbSqliteContext
    {
    }
}

IDbContext.cs

namespace Toolbox.Employee.Database
{
    class IDbContext
    {
    }
}

Employee.cs

using System.ComponentModel;

namespace Toolbox.Employee.Models
{
    public class Employee
    {
        //Employee ID -- Auto Increment (Database)
        public int Id { get; set; }
        private string _firstName;
        public string FirstName
        {
            get => _firstName;
            set
            {
                if (_firstName != null && _firstName != value)
                {
                    _firstName = value;
                }
            OnPropertyChanged("FirstName");
            }
        }
        private string _lastName;
        public string LastName
        {
            get => _lastName;
            set
            {
                if (_lastName != null && _lastName != value)
                { 
                    _lastName = value;
                }
                OnPropertyChanged("LastName");
            }
        }
        //TeamId -- Foreign Key
        private int _teamId;
        public int TeamId
        {
            get => _teamId;
            set
            {
                if (_teamId != value)
                    { 
                    _teamId = value;
                    }
                else
                {
                    return;
                }
                OnPropertyChanged("TeamId");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Team.cs

namespace Toolbox.Employee.Models
{
}

EmployeeService.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace Toolbox.Employee.Services
{
    class EmployeeService
    {
    }
}

Soweit die Theorie, nun habe ich mir dazu ein paar Gedanken gemacht, recherchiert und mir sind einige Dinge unklar und bräuchte weitere Hilfe:

  • um eine generische Methode nutzen zu können muss ich ja eine Entity definieren. Ist es richtig das ich in der Datei "EmployeeEntity.cs" definiere aus was diese Entity besteht (beispielsweise ID, Vorname, Nachname) und dann in der Datei "Employee.cs" beschreibe das der Vorname ein String ist und wie get/set definiert sind?

  • damit ich die IDbConnection "connection" nutzen kann muss ich ja irgendwie definieren wohin sie connecten soll. Wo wird das definiert (in welcher Datei) und vor Allem, die Verbindung sollte ja nach erfolgtem Update oder was auch immer wieder geschlossen werden.

  • wenn ich in der Datei "EmployeeRepository.cs" "GetAll" (gekennzeichnet mit Beispiel) aufrufen will, warum ist dort keine Angabe der Entity notwendig? (oder ist es so wie ich vermute und ich starte von dort gar nicht den Aufruf?)

  • ausgehend von der MVVM Idee, entspricht "EmployeeService.cs" dem ViewModel für die Employee Sachen?

07.05.2019 - 14:14 Uhr

Danke für die Erklärung 😃

@Abt
vielen Dank für den Github Link!

07.05.2019 - 11:25 Uhr

Vorab:

Vielen lieben Dank für das mehr als ausführliche Feedback!!
Die Quintessenz daraus ist für mich ganz klar: Nochmals über die Bücher und ganz von vorne anfangen. Irgendwie sind viele Sachen zwar in den Kopf rein aber leider auch wieder raus; hast du mit deinem letzten Satz auch wunderbar getroffen

Aber ja, man muss sich diese Dinge durchlesen, im Kopf sortieren und abstrahieren. Kommt alles mit der Zeit.

Nun zum detaillierten Feedback:

Das mit dem Projektnamen habe ich ehrlich gesagt ignoriert weil ich erst mit Dapper gespielt hatte. Wird natürlich so auch nicht mehr gemacht. Mit meiner Interpretation von DAL war ich wohl auf dem Holzweg, hatte das so in einem Lernvideo gesehen und dachte das es sich so gehört.

Mit dem Einchecken muss ich mich explizit nochmals befassen, hatte einfach den Hauptordner angeklickt und dann veröffentlicht.

(Zum Thema Guidelines durchlesen: Witzig ist das ich mein Accessprogramm erst auch kreuz und quer aufgebaut hatte und erst später eine "organisierte und aufgeräumte" Version erstellt habe .... anstatt direkt von Anfang an ... neige zur Chaostheorie)

Das mit der DAL Klasse ist simpel (wenngleich auch total falsch): Ich dachte ich müsse sämtliche Datenbankzugriffe (den SQL Code) in eine eigene Datei auslagern, deshalb dieser "Murks".

Ich muss mir nochmals ganz in Ruhe die Grundlagen durchlesen und dann deinen Code neu interpretieren.

Der Teil mit den Listen holen und die Entity updaten ist logisch (auch wenn ich den Codesyntax noch nicht 100% verstehe) aber bei "public EmployeeRepository (IDbConnection connection) : base(connection)" ist Schluss. Vermute mit ":base(Connection)" implementierst du etwas wo die Verbindungsdaten geregelt sind, liege ich damit richtig?

Auf alle Fälle, vielen vielen Dank für deine Bemühungen ... weiss es echt zu schätzen 🙂

06.05.2019 - 22:38 Uhr

Würdest du mir anhand meiner Update Employee Methode zeigen wie es aussehen sollte? Dann könnte ich analog die anderen Methoden entsprechend ändern.

Werde den 3 Schichten Artikel nochmals lesen und mir überlegen wie ich das in grösserem Ausmasse umsetzen könnte

06.05.2019 - 19:56 Uhr

Habe die letzten Tagen viel gelesen und versucht so viel als möglich umzusetzen und das ganze Projektlein etwas "seriöser" umzusetzen. (DAL, etc) Dapper wurde (entgegen des Titels) noch nicht implementiert, das mache ich erst wenn die Methoden und Co so passen.

Hier der Azurelink --> https://moritzjuergensen.visualstudio.com/SQL_V1/SQL_V1%20Team/_git/SQL_V1

Ich habe dennoch einige offenen Punkte die mir nicht einleuchten und bei denen mir vielleicht jemand helfen kann:

  • die Methoden oder Funktionen im Inferface geben ja etwas zurück, wie bestimme ich ob es beispielsweise ein Bool oder ein Wert ist? (Beispiel: Mitarbeiterupdate ... eigentlich will ich ja nur wissen ob es geklappt hat oder net, sprich ein Bool würde mir genügen oder sehe ich das falsch?)
  • wenn jetzt noch mehr Klassen dazu kommen (Beispiel: Projektarten, Projektphasen, etc), macht man dann ein neues Projekt oder "sollte" man Sachen die logisch zusammen passen (Beispiel: Alles was im Konfigurationsmenu ist) in einem Projekt vereinen?

Wäre echt froh wenn Verbesserungen anhand meines Codes dargestellt würden, dann kann ich gleich vergleichen wo ich Fehler gemacht habe.

Vielen Dank an Alle die mir helfen 😃

28.04.2019 - 16:03 Uhr

Danke dir für das Feedback.

Habe heute nochmal alles über den Haufen geworfen und wieder von vorne angefangen da ich nicht zufrieden bin .... Macht 0 Sinn weiter zu machen wenn die grundlegendsten Sachen nicht "stimmen".
Habe jetzt ne Möglichkeit gefunden mit Dapper einigermassen ne ObersavbleCollection raus zu kriegen und damit kann ich wieder die Inotify Schnittstelle nutzen.

  • das mit dem "*" werde ich so ändern wie du sagst, genau die Sachen raus holen die ich brauche
  • das mit der 3 Schichten Architektur schnalle ich nur im Ansatz ein klein wenig aber im Grossen und Ganzen ist das nur ein Fragezeichen für mich (habe schon genug mit MVVM zu tun damit da im Ansatz alles sauber bleibt) aber ich lese mir das mal in aller Ruhe durch und vielleicht kann ich ja einen Teil davon umsetzen

PS: Ich glaube je länger je mehr das so ein Projekt anfangs vielleicht doch 2-3 Nummern zu gross ist...

27.04.2019 - 22:44 Uhr

Habs nach langem Würgen hingekriegt, allerdings glaube ich das es net so "gut" ist.

Hier nur die 3 aktualisierten Dateien:

ViewModel.cs

using SQLiteDapper.Model;
using System;
using System.Collections.Generic;
using System.Windows.Input;

namespace SQLiteDapper.ViewModels
{
    public partial class ViewModel
    {
        public RelayCommand Update { get; set; }
        public List<Employee> Employees { get; set; }
        public Employee MySelectedEmployee { get; set; }
        public ViewModel ()
        {
            // Get data for View Datagrid
            IEmployeeSQLiteRepository employeeRepository = new EmployeeSQLiteRepository();
            Employees = employeeRepository.GetAll(); //per Binding ins XAML 
            this.Update = new RelayCommand(_ => UpdateRecord());
        }
        void UpdateRecord()
        {
            IEmployeeSQLiteRepository employeeRepository = new EmployeeSQLiteRepository();
            Employee emp = employeeRepository.GetById(MySelectedEmployee.ID);
            emp.FirstName = MySelectedEmployee.FirstName;
            emp.LastName = MySelectedEmployee.LastName;
            emp.TeamID = MySelectedEmployee.TeamID;
            employeeRepository.Update(emp);
        }
    }

    public class RelayCommand : ICommand
    {
        private Action<object> action;
        public RelayCommand(Action<object> action)
        {
            this.action = action;
        }
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            action(parameter);
        }

        public event EventHandler CanExecuteChanged;
    }
}

IEmployeeSQLiteRepository.cs

using System.Collections.Generic;

namespace SQLiteDapper.Model
{
    public interface IEmployeeSQLiteRepository
    {
        List<Employee> GetAll();
        //bool Add(Employee employee);
        Employee GetById(int id);
        Employee Update(Employee employee);
    }
}

EmployeeSQLiteRepository.cs

using System;
using Dapper;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using Dapper.Contrib.Extensions;

namespace SQLiteDapper.Model
{
    class EmployeeSQLiteRepository : IEmployeeSQLiteRepository
    {
        private SQLiteConnection db = new SQLiteConnection(Properties.Settings.Default.connString);
        // Get Employee record by Id
        public Employee GetById(int id)
        {
            try
            {
                return this.db.Query<Employee>("SELECT * FROM employees WHERE ID=@Id", new { Id = id }).FirstOrDefault();
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
                return null;
            }
        }
        // Retrieves the data from the table.
        public List<Employee> GetAll()
        {
        try
            { 
                return this.db.Query<Employee>("SELECT * FROM employees LIMIT 20").ToList();
            }
        catch (Exception exception)
            {
                Console.WriteLine(exception);
                return null;
            }
        }
        // Update the employee record
        void Update(Employee employee)
        {
        try
            {
                SqlMapperExtensions.Update(db, new Employee { ID = employee.ID, FirstName = employee.FirstName, LastName = employee.LastName, TeamID = employee.TeamID });

            }
         catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }

        Employee IEmployeeSQLiteRepository.Update(Employee employee)
        {
            throw new NotImplementedException();
        }
    }
}

So oder so habe ich hierzu ein paar Sachen die mir nicht klar sind:

  • Ist es "sinnvoll" die von Dapper genutzte Liste zu verwenden oder sollte man diese in eine ObservableCollection umwandeln da man dann "INotifyPropertyChanged" nutzen kann? (Würde aber irgendwie Dapper ad absurdum führen in meinen Augen) --> Falls ja, gibt es da eine einfache Möglichkeit?

  • Ich führe jetzt das Update eines Mitarbeiters via Button im WPF Formular aus, ist der Code so korrekt mit der ICommand Schnittstelle?

  • Generell, ist der Code und der Aufbau so in Ordnung oder gibt es irgendwas "schwerwiegendes" was ich noch ändern sollte?

24.04.2019 - 22:54 Uhr

stehe jetzt komplett aufm Schlauch 😦

Kann mir jemand anhand des neuen Codes zeigen wie mein Code aussehen muss damit bei Änderungen im View (nehmen wir an ich ändere bei einem Mitarbeiter den Nachnamen direkt im Datagrid) die Änderung via OOP in die Datenbank zurück gespeichert werden?

24.04.2019 - 21:49 Uhr

Ne das mit den 2000 war eher auf das Thema Performance bezogen. In der heutigen Lösung werden alle 2000 in einer scrollbaren Liste dargestellt, würde ich so nie wiede realisieren wollen 😃

Über das Contrib Projekt bin ich beim googlen auch gestolpert, hatte aber bzgl. der Aktivität ein ähnliches Gefühl wie du … aber bei den paar Eigenschaften spielt es imho noch keine Rolle.

Das Beispiel war eher auf die einzelnen SQL Eigenschaften bezogen, der Rest ist selbst aus meiner Sicht suboptimal gelöst.

Die UI hatte ich nur ins Spiel gebracht weil ja beim vorherigen Beispiel die Inotify Schnittstelle zusammen mit der ObservableCollection alles "selbst" gemacht hat. Ohne die Schnittstelle habe ich ja das Problem das beispielsweise die Daten die dem Team hinterlegt sind nicht mehr automatisch aktualisiert werden sondern ich ja eigentlich bei einer Änderung die Initialisierung erneut anstossen muss (zumindest bei dem was ich bisher zur List gelesen habe)

24.04.2019 - 17:37 Uhr

Danke für die fixe Antwort!

Das mit der Liste, ID_Mitarbeiter, TeamID und der OOP Benennung habe ich bereits umgesetzt, danke dafür!

Dependency Injection muss ich später mal nachgucken, denke das ist aber imho zweitrangig. Mein grösstes Problem ist das Thema "Update":

In einem Beispiel habe ich folgendes gefunden:

        private int UpdateStudent(Student student)    
        {    
            using (var connection = new SqlConnection(sqlConnectionString))    
            {    
                connection.Open();    
                var affectedRows = connection.Execute("Update Student set Name = @Name, Marks = @Marks Where Id = @Id", new { Id = studentId, Name = txtName.Text, Marks = txtMarks.Text });    
                connection.Close();    
                return affectedRows;    
            }    
        } 

(Originalpost

Sehe ich das richtig das ich jede Eigenschaft überspielen soll? (Beispiel: Ich ändere "LastName" und überspiele dann trotzdem "FirstName" und "TeamID" mit?) Könnte ja rechts das Datagrid einblenden und links dann 2 Textboxen und Eine Combobox mit den Daten füllen. Nach dem Aktualisieren könnte ich via Button den Update Prozess starten.

Oder könnte man nach dem Editieren eines Feldes im Datagrid den Updateprozes starten?

PS:
die grösste Tabelle hat im Moment (nach 2 Jahren) ca. 2000 Einträge, denke GetAll() dürfte da noch funktionieren, oder?

24.04.2019 - 16:56 Uhr

Habe nun 2 Dinge gemacht:

1.) Ich hab mir endlich n C# Buch (Schrödinger programmiert…) gekauft (und werde nach dem Abschluss dieses MiniProjekts nochmal von 0 anfangen mit dem Buch)
und
2.) Ich habe versucht den Teil mit den Mitarbeitern mit Dapper umzusetzen. Hier erstmal der Code dazu (komplett neu aufgebaut):

Employee.cs

namespace SQLiteDapper.Model
{
    public class Employee
    {
        public int ID_Mitarbeiter { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Team { get; set; }
    }
}

IEmployeeRepository.cs

using System.Collections.Generic;

namespace SQLiteDapper.Model
{
    public interface IEmployeeRepository
    {
        List<Employee> GetAll();
        Employee GetById(int id);
        bool Update(Employee employee);
    }
}

EmployeeRepository.cs

using Dapper;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;

namespace SQLiteDapper.Model
{
    class EmployeeRepository : IEmployeeRepository
    {
        private SQLiteConnection db = new SQLiteConnection(Properties.Settings.Default.connString);
        // Get Employee record by Id
        public Employee GetById(int id)
        {
            return this.db.Query<Employee>("SELECT * FROM Mitarbeiter WHERE ID_Mitarbeiter=@Id", new { Id = id }).FirstOrDefault();
        }
        // Retreives the data from the table.
        public List<Employee> GetAll()
        {
            return this.db.Query<Employee>("SELECT * FROM Mitarbeiter").ToList();
        }
        // Update the employee record
        public bool Update(Employee employee)
        {
            string query = "UPDATE Mitarbeiter SET LastName = @LastName WHERE ID_Mitarbeiter = @ID_Mitarbeiter";
            var count = this.db.Execute(query, employee);
            return count > 0;
        }
    }
}

ViewModel.cs

using SQLiteDapper.Model;
using System.Collections.Generic;

namespace SQLiteDapper.ViewModels
{
    public partial class ViewModel
    {
        public IEnumerable<Employee> Employees { get; set; }
        public ViewModel ()
            {
            // Get data for View Datagrid
            IEmployeeRepository employeeRepository = new EmployeeRepository();
            Employees = employeeRepository.GetAll(); //per Binding ins XAML File
            Employee emp = employeeRepository.GetById(1);
            emp.LastName = "Blödsinn";
            employeeRepository.Update(emp);
        }
    }
}

So, es funktioniert soweitber nun stehe ich aber vor einem Problem das ich ohne Hilfe def. net sauber lösen kann:

Für eine Liste funktioniert onpropertychanged ja nicht. Lohnt es sich die Liste in eine ObservableCollection umzuwandeln (Dapper unterstützt soweit ich weiss OC nicht direkt) oder soll ich diese sehr statischen Daten einfach als "Liste" belassen? (Meinen Datagrid kann ich ja trotzdem ändern und dann per Button oder so den Update Prozess anstossen) --> Falls Ja, wie mache ich das am "Besten" habs versucht aber das klappt bei mir nicht 😦 )?

22.04.2019 - 22:03 Uhr

erstmal danke euch Beiden!

@KroaX
Muss ich mir anschauen wie das mit dem ORM funktioniert, lese das hier zum ersten Mal. Yep habe einfach mal "alles" ins ViewModel gepackt, funktioniert aber def. keine optimale Lösung. Hier dran muss ich defitiniv arbeiten

@MrSparkle
denke du sprichst von dieser Methode, oder?

        private void CreateNewRow(object param)
        {
            string Parameter = (string) param;
            if (Parameter == "Mitarbeiter")
            {
                OC_Mitarbeiter.Add(new Mitarbeiter(99999, "?", "?", 1));
            }
            else if (Parameter == "Team")
            {
                OC_Team.Add(new Team(99999, "?"));
            }
            else
            {
                MessageBox.Show("falscher Parameter");
            }
        }

Die Methode hängt ja am Menu, sollte ich die beiden "Create" Methoden als einzelne Methoden definieren? (Hatte mir das irgendwie über die Zeit unter VBA angewöhnt alles zu verschachteln wenn es ähnliche Sachen sind --- hier wäre in dem Fall eine "CreateTeam" und Eine "CreateMitarbeiter" Methode sinnvoll?)

Zu den Links, Nr. 1 und 4 sind toller Lesestoff aber für 2 und 3 wäre es echt net wenn du mir praktische Beispiele anhand meines Beispiels geben würdest. Denke das würde mir helfen Theorie und Praxis besser miteinander zu verknüpfen. (Natürlich nur wenns net all zu grosse Mühe macht 😃 )

21.04.2019 - 21:32 Uhr

au weia, hoffe ich habe das nun richtig gemacht mit GitHub … falls net bitte melden (musste mir erstmal n Account anlegen)

--> https://github.com/MoritzJuergensen/SQLite (Datenbank befindet sich im Database Ordner --- Pfad muss wie oben angegeben angepasst warden)

21.04.2019 - 20:02 Uhr

Moin,

wie in meinen beiden anderen Threads bereits angesprochen beschäftige ich mich im Moment mit C#, MVVM, WPF und SQLite (Ich selber habe nur ein wenig VBA (Excel) und Access Erfahrung, sprich dies ist mein erstes Projekt mit C# überhaupt)

Zu meinem "Projekt":
Als Backend dient mir eine SQLite Datei mit 2 Tabellen (Mitarbeiter und Team) und ich möchte jedem Mitarbeiter (ID, Vorname, Nachname) ein Team zuordnen. Dieses Team soll mir im späteren Verlauf eine mandantenfähige Lösung dienlich sein (dazu weiter unten mehr). Das Anlegen ieines neuen Mitarbeiters oder Teams kann via Menu oben vorgenommen warden, das Löschen funktioniert via "Delete". De Änderungen warden allesamt in die SQLite Datei zurück gespielt.
Bitte unter "settings.settings" den Link zur Test.db anpassen bevor das Projekt debuggt wird 😉

Was ich mir wünsche:
Wie bereits gesagt stehe ich ganz am Anfang mit C# und würde mich freuen wenn ihr euch mein kleines Projekt ansehen könntet. Denke da ist eine ganze Menge an Verbesserungspotential vorhanden. Würde mir natürlich auch wünschen das Verbesserungsvorschläge ev. direct an meinem Code aufgezeigt würden.

Zum weiteren Verlauf:
Ich möchte dieses "Miniprojekt" nutzen um eine bestehende Accesslösung abzulösen. Es wird noch 2-3 weitere solche Projekte geben die dann schlussendlich in ein grosses Ganzes übernommen warden … davon bin ich aber 1.) noch weit weit entfernt und 2.) möchte ich so gut als mir möglich Best-Practice betreiben (klar, ein Profi würde das ganz anders angehen)

Link:
<Link entfernt>

Sonstiges:
An dieser Stelle schon mal vielen Dank an alle die meinen Post gelesen haben 😃

13.04.2019 - 09:48 Uhr

X( au weia, habs ja gesagt .... den Wald vor lauter Bäumen nicht mehr sehen.

Danke dir, klappt alles wunderbar!

13.04.2019 - 09:11 Uhr

Moin,

ich versuche im Moment mich in C# und MVVM einzuarbeiten, allerdings scheitere ich am Databinding. Hoffe jemand hat Lust und Zeit mir kurz unter die Arme zu greifen, wäre echt dankbar!

Die Idee ist (wie hier angesprochen -> SQLite (Mehrere User updaten denselben Eintrag - Was passiert?)) aus einer SQLite Datei Name und Vorname auszulesen und in einem Datagrid darzustellen. Habe den Code von Code-Behind auf MVVM (oder zumindest im Ansatz) umgebaut aber nun bleibt mein Datagrid leer. Da die FOR Schleife im ViewModel allerdings Daten ausspuckt gehe ich davon aus, das etwas mit der Rückmeldung an die View nicht passt (inotifypropertychanged fehlt noch komplett, damit stehe ich noch auf Kriegsfuss).

Was ich bis jetzt habe:

Mitarbeiter.cs (Model)

namespace SQLite.Models
{
    public class Mitarbeiter
    {
        public Mitarbeiter(string vorname, string nachname)
        {
            Vorname = vorname;
            Nachname = nachname;
        }

        public string Vorname { get; set; }
        public string Nachname { get; set; }
    }
}

MainWindowViewModel.cs (ViewModel)

using System;
using System.Collections.ObjectModel;
using System.Data.SQLite;
using SQLite.Models;

namespace SQLite.ViewModels
{
    class MainWindowViewModel
    {
        public ObservableCollection<Mitarbeiter> Test;

        public MainWindowViewModel ()
        {
            FetchData();
        }

        void FetchData()
        {
            string connectionString = "Data Source=C:\\Users\\Moritz\\Desktop\\Test.db" + ";Version=3;";
            SQLiteConnection connection = new SQLiteConnection(connectionString);
            SQLiteCommand cmd = new SQLiteCommand("select * from Mitarbeiter", connection);
            Test = new ObservableCollection<Mitarbeiter>();
            connection.Open();          
            SQLiteDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                string Vorname = reader.GetString(1);
                string Nachname = reader.GetString(2);
                Test.Add(new Mitarbeiter(Vorname, Nachname));
            }

            //TEST
            for (int i = 0; i < Test.Count; i++)
            {
                Console.WriteLine(string.Concat(Test[i].Vorname, "---", Test[i].Nachname));
            }
            //TEST
        }
    }
}

MainWindow.xaml.cs (View)

using System.Windows;
using SQLite.ViewModels;

namespace SQLite.Views
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();

            ///////////////////LÖSCHEN
            //string connectionString = "Data Source=C:\\Users\\Moritz\\Desktop\\Test.db" + ";Version=3;";
            //SQLiteConnection connection = new SQLiteConnection(connectionString);
            //SQLiteCommand cmd = new SQLiteCommand("select * from Mitarbeiter", connection);
            //ObservableCollection<Mitarbeiter> Test = new ObservableCollection<Mitarbeiter>();
            //connection.Open();
            //SQLiteDataReader reader = cmd.ExecuteReader();
            //while (reader.Read())
            //{
            //    string Vorname = reader.GetString(1);
            //    string Nachname = reader.GetString(2);
            //    Test.Add(new Mitarbeiter(Vorname, Nachname));
            //}
            //dt1.ItemsSource = Test;
            ///////////////////LÖSCHEN

        }
    }
}

MainWindow.xaml (View)

<Window x:Class="SQLite.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <DataGrid ItemsSource="{Binding Test}" AutoGenerateColumns="True" Name="dt1" HorizontalAlignment="Left" Height="235" Margin="59,60,0,0" VerticalAlignment="Top" Width="663"/>
    </Grid>

</Window>

Ich weiss das der Code alles andere als "ideal" ist (using für die SQLite Connection fehlt, etc) aber ich wollte Step By Step vorgehen um dafür das Ganze besser zu verstehen. Normalerweise frage ich nicht nach "Gesamtlösungen" aber ich probiere hier seit Stunden rum und vermute das ich einen fatalen Denkfehler mache.

12.04.2019 - 18:54 Uhr

das mit dem Overengineering kenne ich von der Access Lösung her, wollte viel mehr als ich "packen" kann.

Naja dachte mir halt das ich (eben weill die DB mein zentrales Element ist) gleich auf etwas setzen sollte das mehr "wumms" hat. Ich guck mir das Thema Repository mal an, danke dir

12.04.2019 - 17:47 Uhr

Danke für den Link zu den Layern, macht absolut Sinn die Aufteilung!

Habe heute ein wenig gelesen und das Beispiel studiert. Wollte dann mich mit dem Thema "ObservableCollection" befassen, allerdings finde ich dazu in Zusammenhang mit SQLite kaum Beispiele und die meisten lesen die Daten aus SQLite in ein Datatable und wandeln das dann um ... kommt mir seltsam vor diesen Zwischenschritt zu machen! (Oder ist das wirklich die Vorgehensweise?)
Hat jemand hierzu ein "besseres" Beispiel?

Das Thema mit dem "using" bei SQL Verbindungen und das verwalten des Connectionstrings habe ich Gott sei Dank etwas besser kapiert.

11.04.2019 - 22:38 Uhr

Erstmal danke euch Beiden! Vielleicht noch als Info, ich bin kein Informatiker sondern möchte mir C# in "Eigenregie" aneignen um dann diese Kenntnisse bei uns im Büro ev. umsetzen zu können. Wir haben leider keine eigene IT und unsere Geschäftsleitung ist der Meinung das Excel genügt

Wir brauchen mittelfristig def. eine professionelle Lösung (also nicht meine Variante) aber solche Prozesse dauern bei uns Jahre. Da die Access Lösung läuft tu ich dies in erster Linie für Mich und meinen Spieltrieb, sprich so schnell wird da nix in den laufenden Betrieb eingebaut.

@Abt
MVVM steht ganz oben auf meiner ToDo Liste, allerdings übe ich im Moment noch mit den grundlegenden C# Befehlen und wollte da noch ein wenig mehr üben (meine Logik: Wenn ich den Befehl beisammen habe und es funktioniert kann ich es im 2. Schritt entsprechend den MVVM Regeln umsetzen).

Wollte erstmal gucken wie das mit dem Binding funktioniert und der nächste Schritt wäre dann die Umsetzung dieses Miniprojekts mit dem MVVM Pattern. So möchte ich Step-by-Step mein Wissen erweitern.

@T-Virus
Vielleicht zur Erklärung zum Thema "Zeiterfassung":
Wir generieren für jeden Auftrag in unserer Abteilung eine eindeutige Nummer (K + Laufnummer), erfassen diverse Schritte wie "Genehmigungszeichnung, Freigabe oder Änderung" mit jeweils In / Out Datum und zu jedem Schritt werden Arbeitszeiten erfasst (Beispiel: K1500 / Freigabe / 10.04.2019 + 11.04.2019 / 3h) und das wars. Wir reden hier von ca. 3-6 Nummern pro Tag, mehr auf keinen Fall und es arbeiten ca. 15 Leute damit wobei die Chance das 2 Personen denselben Auftrag bearbeiten bei max. 1% liegt.

Das MVVM Pattern ist, wie oben geschrieben, ganz oben auf meiner ToDo Liste, aber ich "kämpfe" im Moment noch mit C# selber (Befehle, Aufbau, etc).

Nehme aber eure Punkte sehr gerne auf und werde mich def. damit in Kürze beschäftigen.

11.04.2019 - 21:57 Uhr

Hallo,

ich habe vor einiger Zeit eine MS Access Lösung für unsere Abteilung erstellt (Zeiterfassung, ganz simples Projektmanagement mit IN / OUT Kennzahlen) und würde diese gerne in C# und SQLite umsetzen. Fange gerade erst wieder an mit C# und bin im Moment am Lesen und ausprobieren.

Momentan habe ich folgendes Beispiel erstellt (das Ganze ist an ein WPF Datagrid angebunden) was auch wunderbar funktioniert.

using System.Data;
using System.Windows;
using System.Data.SQLite;
using System;

namespace WPF_MySQL_Connection
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public DataTable Dt;
        public MainWindow()
        {
            InitializeComponent();
            string connectionString = "Data Source=" + AppDomain.CurrentDomain.BaseDirectory + "Test.db" + ";Version=3;";
            SQLiteConnection connection = new SQLiteConnection(connectionString);
            SQLiteCommand cmd = new SQLiteCommand("select * from Test", connection);
            connection.Open();
            Dt = new DataTable();
            Dt.Load(cmd.ExecuteReader());
            //connection.Close();
            dtGrid.DataContext = Dt;
        }

        public void SaveChanges(object sender, RoutedEventArgs e)
        {
            string connectionString = "Data Source=" + AppDomain.CurrentDomain.BaseDirectory + "Test.db" + ";Version=3;";
            SQLiteConnection connection = new SQLiteConnection(connectionString);
            var abc = new SQLiteDataAdapter("select * from Test", connection);
            SQLiteCommandBuilder builder = new SQLiteCommandBuilder(abc);
            abc.UpdateCommand = builder.GetUpdateCommand();
            abc.Update(Dt);

        }
    }
}

Aus der Tabelle "Test" lese ich im Moment den Wert "Name" aus und stelle ihn im Datagrid dar. Wenn ich den nun editiere und via Button (Click Method "SaveChanges") wieder speichere taucht die Änderung im db File auf. Hiezu nun 2 Fragen:

1.) Gehe ich Recht in der Annahme dass wenn ein 2. User zur gleichen Zeit denselben Eintrag ändert das erst eine und danach die andere Änderung vorgenommen wird? (Sprich die Tabelle wird für kurze Zeit gesperrt bis der Updateprozess abgeschlossen ist und dann wird das nächste Update vollzogen)
2.) Es sind mehr oder weniger statische Daten mit sehr wenig Änderungen pro Stunde, wie könnte man ein Update des UI vornehmen? (Gibt es eine Möglichkeit das die SQLite Datei ein "Signal" sendet wenn eine Tabellle geändert wurde so das alle die zu dem Zeitpunkt ein Datagrid der entsprechenden Tabelle sehen ein Update kriegen?) (Hoffe das ist verständlich ausgedrückt 🙁 )

Danke euch im Vorraus!

Gruss
Moritz

PS: Wie gesagt, blutiger Anfänger ... der Code oben ist alles andere als "regelkonform" 😉

27.05.2013 - 15:19 Uhr

Moin,

ich habe vor einigen Wochen mit VB.Net angefangen und bin aber jetzt zu C# gewechselt weil es mir irgendwie besser liegt, sofern man das nach der kurzen Zeit beurteilen kann. Da ich mit Visual Studio 2012 Express arbeite bin ich natürlich über WPF und dementsprechend auch MVVM gestossen. Hab mir mal die Grundprinzipien durchgelesen und ich denke im Grossen und Ganzen habe ich die Idee dahinter verstanden.

Ich habe mittlerweile eine kleine Anwendung (Personendatenbank) mit WPF realisiert, allerdings klebt alles im Code-Behind was ja nicht so ganz den "Richtlinien" der MVVM Pattern entspricht. Wie sollte ich nun am Besten vorgehen um meine Anwendung MVVM konform zu kriegen? Ich habe mal die Anwendung angehängt, wäre schick wenn die mal jemand anschauen könnte und mir basierend darauf Tipps gibt (mal von kleinen Schreibfehlern abgesehen), natürlich bin ich auch sonst für alle Tipps dankbar 😃

Vorallem ist es ratsam als C# Anfänger gleich mit WPF und dessen Eigenheiten in MVVM zu starten?

Gruss
Moritz