myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Basistechnologien und allgemeine .NET-Klassen » 2 Entities für das ViewModel aufbereiten
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

2 Entities für das ViewModel aufbereiten

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50


Moritz83 ist offline

2 Entities für das ViewModel aufbereiten

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

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)

C#-Code:
        public class DisplayEmployeeEntity : EmployeeEntity
    {
        public DepartmentEntity Department { get; set; }
    }

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

C#-Code:
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)

C#-Code:
  [Computed]
  public DepartmentEntity Department { get; set; }

(so könnte ich ein SelectedItem direkt wieder updaten da es sich um die gleiche Entity handelt)
Neuer Beitrag 18.10.2019 10:32 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.835
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Eine Id erzeugt entweder der DAL (Guid) oder die Datenbank (Int).
Prinzipiell wäre dafür der richtige Platz beim Repository Pattern eben eine solche Add Methode.

Dass Du die Logikschicht hier völlig weglässt; das kann man machen - bringt aber oft - gerade beim Hinzufügen von Elementen, Probleme in Form von Abhängigkeiten, Logik oder redundantem Code.
 [Artikel] Drei-Schichten-Architektur
Neuer Beitrag 18.10.2019 13:31 Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.197
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Moritz83,

zu dem von Abt Gesagtem: 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. Wenn du eine View hast, dann bau dir ein ViewModel dazu, mappe es entsprechend und zeige es dann an.

Gruss

Coffeebean
Neuer Beitrag 18.10.2019 13:41 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50

Themenstarter Thema begonnen von Moritz83

Moritz83 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@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 Daumen hoch
Neuer Beitrag 18.10.2019 14:11 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.835
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Moritz83:
@Gerade das Thema "ViewModel + Entity + Model" erschliesst sich mirnoch nicht ganz (Wer spricht mit wem, wer erbt von wem, mappen, etc)

Steht prinzipiell alles in  [Artikel] Drei-Schichten-Architektur :-)
Haben wir Dir ja schon mehrfach verlinkt; aber falls Du es nicht gesehen hast: Da ist auch nen Bild wer mit wem Spricht.
Neuer Beitrag 18.10.2019 14:43 Beiträge des Benutzers | zu Buddylist hinzufügen
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50

Themenstarter Thema begonnen von Moritz83

Moritz83 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

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 smile

Bin dankbar für solche Inputs eurerseits Daumen hoch
Neuer Beitrag 18.10.2019 16:04 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50

Themenstarter Thema begonnen von Moritz83

Moritz83 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Coffeebean:
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:

C#-Code:
        #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

C#-Code:
            //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

C#-Code:
EmployeeEntity

akzeptiert. Der Code dazu in verkürzter Form:

C#-Code:
    public interface IEmployeeRepository : ISqlRepository<EmployeeEntity>
    {
    }

C#-Code:
    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

C#-Code:
        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).

C#-Code:
                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.
Neuer Beitrag 18.10.2019 23:34 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50

Themenstarter Thema begonnen von Moritz83

Moritz83 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

(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

C#-Code:
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

C#-Code:
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.

C#-Code:
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

C#-Code:
    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?
Neuer Beitrag 29.10.2019 22:32 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.835
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Den Mapper erstellt man zentral; kann man hier einfach statisch machen.

C#-Code:
public static class MyMapper
{
    private static bool _init;
    public static Init()
    {
        if (!_init)
        {
            Mapper.Initialize(c =>
            {
                c.CreateMap<xxxxxx>();
            });
            _init= true;
        }
    }
}

Das wird dann beim Applikationsstart aufgerfen.

Zitat:
Das funktioniert auch, allerdings killt der 2. _test = mapper.Map logischerweise

Naja; das wundert Dich? Du überschreibst ja auch alles.

Du kannst nicht so ohne weiteres von mehreren Quellen in ein Ziel mappen.
Prinzipiell müsstest Du hier das komplette Mapping manuell konfigurieren; sprich Member für Member und jeweils die Information hinterlegen, wo AutoMapper die Werte der einzelnen Eigenschaften findet.

Combined (bescheuerter Name ;-) nenne ich ma DepartmentEmployee

C#-Code:
    var emMap = Mapper.CreateMap<EmployeeEntity,DepartmentEmployee>();
        emMap.ForAllMembers(d => d.Ignore());
        emMap.ForMember(e => e.FirstName, opt => opt.MapFrom(s => s.FirstName))
            .ForMember(e => e.LastName, opt => opt.MapFrom(s => s.LastName));

     var deMap = Mapper.CreateMap<DepartmentEntity, DepartmentEmployee>();
        deMap.ForAllMembers(d => d.Ignore());
        deMap.ForMember(d => d.Department, opt => opt.MapFrom(s => s.Department));

Soweit ich weiß musst Du dann aber doppelt mappen - manuell.

C#-Code:
var depEm = Mapper.Map<EmployeeEntity,DepartmentEmployee>(employee); // füllt Employee
depEm = Mapper.Map<DepartmentEntity,DepartmentEmployee>(employee); // füllt Department

Bin mir aber sicher, dass sich hier andere, wie AutoMapper und WPF exzessiv verwenden, evtl schlauere Lösungen oder entsprechende Extensions geschrieben haben.
Neuer Beitrag 29.10.2019 23:01 Beiträge des Benutzers | zu Buddylist hinzufügen
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50

Themenstarter Thema begonnen von Moritz83

Moritz83 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

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 smile 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 smile
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
Neuer Beitrag 30.10.2019 06:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.835
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Moritz83:
Kannst du mir verraten warum du erst "Ignore" machst und dann die Properties zuweist?

Zitat von Abt:
Prinzipiell müsstest Du hier das komplette Mapping manuell konfigurieren

Augenzwinkern
Neuer Beitrag 30.10.2019 11:51 Beiträge des Benutzers | zu Buddylist hinzufügen
Moritz83
myCSharp.de-Mitglied

Dabei seit: 27.05.2013
Beiträge: 50

Themenstarter Thema begonnen von Moritz83

Moritz83 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

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/4136...rable-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.

C#-Code:
        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)
Neuer Beitrag 30.10.2019 15:15 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.835
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Du, Du wirst nicht den perfekten Code schreiben - niemals. Den gibt es nicht.

Du solltest es erstmal so machen, dass Du es prinzipiell verstehst.
Legitim oder nicht legitim ist auch oft Anforderungssache....
Neuer Beitrag 30.10.2019 15:28 Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 7 Monate.
Der letzte Beitrag ist älter als 7 Monate.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 06.06.2020 13:30