Laden...

Klassen in Ordner strukturieren, wie drauf zugreifen?

Erstellt von Seiyaru vor 7 Jahren Letzter Beitrag vor 7 Jahren 15.052 Views
S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren
Klassen in Ordner strukturieren, wie drauf zugreifen?

Hallo Jungs,

bedingt durch das Feedback aus meiner letzten Frage, möchte ich meine Projekte noch etwas besser Strukturen, da ich immer mehr Klassen schreibe und verwende.

Ich würde sehr gerne Ordner verwenden, jetzt allerdings stellt sich mir die Frage, wie kann ich darauf dann zugreifen da die Klassen ja den NameSpace des Ordner haben?

Ich habe mal aus Spaß eine using Direktive geschrieben, aber ich habe festgestellt, dass ich dann jede Klasse einzeln ansprechen muss, und mir erscheint das jetzt nicht als Königsweg.....

Ich hoffe Ihr könnt mir helfen =)

PS: Mir wurde von Palin noch der Tipp gegeben, das ich auch eine DLL schreiben kann, soweit ich das aber recherchieren konnte, müsste ich diese in c++ schreiben wovon ich habe absolut keine Ahnung habe.?

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo Seiyaru,

du musst eine DLL nicht in C++ schreiben. Im Visual Studio kannst du einfach eine erstellen. Ist halt kein KonsolenProjekt oder eine WinForms-Anwendung. Sondern eine Klassenbibliothek/Class Library (je nach Sprache):)

Ordner zu verwenden ist ne gute Sache. Den Namespace kannst du einfach direkt verwenden oder besser per using einbinden.

Gruss

Coffeebean

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

Danke das werde ich gleich mal probieren =)

Wenn ich in einem neuen Projekt ein Ordner anlege und dann direkt in diesem die Klasse erstelle bekommt die neue Klasse direkt den Namespace des Ordners.

Wenn ich allerdings nachträglich Klassen in den Ordner schieben will, bleibt mir nichts anderes übrig in jeder Klasse den Namespace per Hand anzupassen oder?

PS: Macht die Strukturierung, auch bei Forms sinn?

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo Seiyaru,

oder du nimmst den ReSharper: Rechtsklick --> "Refactor" --> "Adjust Namepaces". So mache ichs zumindest immer. Der "Move"-Befehl könnte das aber auch können.

Gruss

Coffeebean

16.835 Beiträge seit 2008
vor 7 Jahren

Namespacing ist ein Thema für sich; und Namespaces sind prinzipiell unabhängig der Ordner.
Man orientiert sich aber anhand des Ordners, weil es den Überblick erleichtert. Ist einfach ein Best Practise.

DLLs werden erzeugt, wenn Du in Visual Studio eine "Klassenbibliothek" als Projekttyp wählst.
Im Gegensatz zB zu Konsolenanwendungen wird am Ende dann eben keine Exe, sondern eine DLL erzeugt.

Du kannst Dir auch mal NuGet anschauen.
NuGet ist die "Paketverwaltung" in der .NET Welt. Darüber werden gemeinsame Ressourcen - wie zum Beispiel DLLs - ausgetauscht.
DLLs auf eine Dateiebene zu legen ist die altertümliche Lösung; NuGet das moderne Konzept.

NuGet Pakete können sich sowohl auf Servern befinden, wie auch einfach bei Dir in einem Ordner.

3.003 Beiträge seit 2006
vor 7 Jahren

PS: Macht die Strukturierung, auch bei Forms sinn?

Ja. Im Prinzip kannst du Namespaces verteilen, wie du möchtest. In einem Projekt mit 50 Namespaces und genauso vielen Unterordnern wird es dann aber schwierig, anhand der logischen Unterteilung der Klassen (-> namespaces) herauszufinden, im welchem Unterordner die Datei, die die Klasse enthält, denn nun eigentlich zu finden ist. Das ist auch der Grund, wieso man besser jede Klasse - auch wenn sie nur zwei Zeilen hat - in eine separate Datei tut. Auch, wenn man das nicht müsste.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

PS: Macht die Strukturierung, auch bei Forms sinn?

Das ist auch der Grund, wieso man besser jede Klasse - auch wenn sie nur zwei Zeilen hat - in eine separate Datei tut. Auch, wenn man das nicht müsste.

Sorry da komme ich grade nicht mit, meinst du mit Datei DLL?

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo Seiyaru,

nein. Im Prinzip: Sobald du ein "public/private/whatever class Classname" machst --> eigene Klasse, also eine eigene *.cs-Datei (Die möglichst so heisst, wie die Klasse auch heisst).

Gruss

Coffeebean

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

Ah okay versteh so mach ich das derzeit schon.

Macht es Sinn die Ordner Hierarchisch zu strukturieren?

Sprich

-Projektklassen
--Kunden
----Kundendaten
----Kundenprüfung usw.

--Personal
---Personaldaten usw.

Oder ist das kein guter ansatz?

P
1.090 Beiträge seit 2011
vor 7 Jahren

Schau dir mal den [Artikel] Drei-Schichten-Architektur an.

Meistens geht man hin und macht für die Layer, einzelen Projekte. Bei der Bennenung kannst du die hier orientiern MSDN:Namen von Assemblys und DLLs

Wäre dann grob.

<Company>.PL (Bzw. hier wird auch gerne der Anwendungsname Verwendet).dll/.exe
---Kunde (Oder andere Passende Gruppierung als Ordner, z.B. Personen)
----KundenView.cs (KundenAnzeige)

<Company>.BL.dll
---Kunde
----KundenService.cs (KundenPruefung)

<Company>.DAL.dll
---Kunde
----KundenReposetory.cs (KundenDaten)

Grundlegen ist es auch nich schlecht bei den Public Klassen Interfaces Anzubieten.
Kann man im gleichen Projekt oder auch einem eigenen machen.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.835 Beiträge seit 2008
vor 7 Jahren

Namespace sollten niemals Bezeichner von Schichten enthalten!!!!! Ist ein Anti-Pattern, wird leider aber oft gemacht.
Das verringert nämlich die Wiederverwendbarkeit bzw. hat nicht jede Anwendung die gleichen Schichten und verwirrt dann oft.

Es wird folgender Namespace-Aufbau prinzipiell empfohlen bzw. findest Du diesen oft genannt als Anhangspunkt:
<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

Dieser wird u.a. direkt von Microsoft so empfohlen.

Das kann man aber beliebig anpassen und erweitern.
Man sollte jedoch Empfehlungen wenigstens beachten, denn dafür sind sie da.
Es ist einfach eine Orientierung, dass das Öksystem ".NET" relativ einheitlich am Ende aussieht. Das erleichtert vieles ungemein.

Beispiel, was ich als Grundlage verwende und das aber je nach Anwendung bzw Kunde anpasse:

`Company.ConsoleApplicationName
Company.DeskopApplicationName
Company.WebApplicationName

Company.Product.Database
Company.Product.Database.Core
Company.Product.Database.Entities
Company.Product.Database.MsSql
Company.Product.Database.PostgreSql

Company.Product.Repositories
Company.Product.Repositories.Core
Company.Product.Repositories.MsSql
Company.Product.Repositories.MySql

Company.Product.Services
Company.Product.Services.Core
Company.Product.Services.Mappers
Company.Product.Services.CustomerService
Company.Product.Services.UserService`

Erklärung:

  • Ebene Company.Product.Services: Elemente für alle Services, keine Interfaces oder Contracts
  • Ebene Company.Product.Services.Core: Interfaces und Contracts, zB IUserService
  • Ebene Company.Product.Services.UserService: konkrete Implementierung default.

Vorteil solcher Dinge:
ich kann sehr einfach sogenannte User exit umsetzen, wenn zB. für einen Kunde ein Service etwas anders sein soll.
Company.Product.Services.UserService.SpecialUserServiceForCustomer
Dann muss ich nur diese eine DLL austauschen; und habe keine Verletzung meiner Benamsungen.

Wie gesagt: das ist nur meine Vorlage und änder ich ab.
Es kann auch sein, dass ich das um eine sogenannte Enterprise-Stufe erweitere.

<Company>.<CompanyBusinessLine>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

Manchmal arbeite ich auch mit
Company.Applications.ConsoleApplicationName Company.Applications.WebApplicationName

Das liegt daran, dass es zB. in einer Firma mehrere zB "UserServices" geben kann, die man dann einfach durch eine höhere Ebene unterscheiden kann/soll/muss.

In eine gute Grundstruktur sollte man jedenfalls ein wenig Hirnschmalz investieren.
Denn das ist oft eine langfristige Sache. Microsofts bisherige Benamsungen sind jedenfalls ein guter Anhaltspunkt, wie man sowas elegant gestalten kann.

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

@abt

Company.Product.Database.MsSql

da gehe ich davon aus das du auch auf eine Datenbank zugreifen wirst oder?

ich habe heute etwas rum getestet, und mir wurde als Fehler angezeigt, dass ich bei meiner FBConnection mit Using gearbeitet habe anstatt mit disposel.

Jetzt kann es nur zwei Möglichkeiten geben,entweder ich habe ein großen Fehler gemacht, indem ich in einer Klasse eine Methode habe die eine Datenbank-connection aufbaut, oder ich muss mir merken, dass man bei der DLL Erstellung ausschließlich mit dispose anstatt einer Using Direktive arbeitet?

Ich hoffe du/ihr könnt mir da noch einen Denkanstoß geben?

16.835 Beiträge seit 2008
vor 7 Jahren

@abt

Company.Product.Database.MsSql

da gehe ich davon aus das du auch auf eine Datenbank zugreifen wirst oder?

Nein, dafür sind die Repositories da.

Beispiel:

Eine Applikation soll mehrere Datenbanken unterstützen; eben zB MySql, MsSql, Sqlite, Oracle.

Dann würde das im Prinzip so aussehen:
`Company.Product.Database.MySql

  • MySqlDatabaseContext
    Company.Product.Database.MsSql
  • MsSqlDatabaseContext
    Company.Product.Database.Sqlite
  • SqliteDatabaseContext
    Company.Product.Database.Oracle
  • OracleDatabaseContext`

zB OracleDatabaseContext ist nun wirklich die Klasse, die die Verbindung zur - in diesem Fall - Oracle Datenbank aufnimmt.

All diese DatabaseContext Implementierungen haben aber ein gemeinsames Interface, das eben in dem Core-Projekt ist:
`Company.Product.Database.Core

  • IMyDatabaseContext`

Das sieht dann zum Beispiel so aus:


public class OracleDatabaseContext : IMyDatabaseContext

Die Repositories (siehe auch Repository Pattern, so ist das ganze nämlich aufgebaut) sind nun die Klassen, in denen nun wirklich der Datenbankzugriff erfolgt.
Repositories sind dabei nach Entitäten benannt, zB UserEntity wird durch das UserRepository geladen.

Das UserRepository muss gar nicht die Datenbank kennen; es muss nur das Interface der (in diesem Fall relationalen) Datenbank kennen, also:

public class UserRepository
{
  public UserRepository (IMyDatabaseContext dbContext)

Es ist in diesem Fall dem Repository egal, ob das nun eine Oracle Database ist oder MySQL.

Schau mal nach Repository Pattern mit UnitOfWork.
Der zB OracleDatabaseContext stellt dabei den UnitOfWork Container dar.

Das macht es Dir sehr einfach, die Datenbankzugriffe bei Software-Tests zu mocken, und eben - das wichtigste - dich nicht zu sehr auf eine Datenbank zu fixieren.
Du gewinnst Flexibilität durch erhöhte Modularität.

Bei .NET funktioniert die Datenbankkomunikation i.d.R. über ADO.NET.
Das sind Interfaces, die Provider (MySql, MsSql, ..) implementieren.

Man muss da diese Abstraktion nicht mehr unbedingt vornehmen, sondern kann zB. einfach das Entity Framework nutzen.
So spart man sich die Separierung, da das Entity Framework alle ADO.NET Datenbanken bzw. Provider unterstützt.

Deswegen hab ich betont: diese Namespaces sind Orientierungsbeispiele.

Das geht hier nun auch richtung Software Architektur. Und Namespace Design basiert auf viel Verständnis für Architektur.
Meiner Erfahrung nach - auch, wenn es immer pragmatische Ansätze gibt und ich es durchaus bei kleineren Anwendungen auch pragmatisch halte:
Man erkennt am Aufbau von Namespaces einer Anwendung oder einer Landschaft sehr schnell, ob der Kopf dahinter wirklich Architekturverständnis hat und/oder Wert auf eine gute Architektur gelegt hat.
Auch meine ich (dazu muss man wissen, dass ich viel Code Reviews mach), dass man schnell erkenn, ob jemand wirklich strukturiert an ein Projekt ran gegangen ist, oder ob er direkt ohne überlegen drauf losgelegt hat.


ich habe heute etwas rum getestet, und mir wurde als Fehler angezeigt, dass ich bei meiner FBConnection mit Using gearbeitet habe anstatt mit disposel.

using(var dbConntextion = GetVerbindungIgrendwoher...)
{
}

ein using() funktioniert folgendermaßen:

try
{
  //mach irgendwas mit dbConntextion
}
finally
{
    dbConntextion.Dispose().
}

Deswegen funktioniert ein using() auch immer nur mit Klassen, die IDisposable implementieren.
Der Satz macht also inhaltlich keinen Sinn. Verwendest Du ein using(), dann verwendest Du indirekt auch ein Dispose.

Das ist aber ein anderes Thema; mach dafür bitte einen extra Thread auf.

P
1.090 Beiträge seit 2011
vor 7 Jahren

Namespace sollten niemals Bezeichner von Schichten enthalten!!!!! Ist ein Anti-Pattern, wird leider aber oft gemacht.

Drei 3 Ausrufezeichen gelten ja manch mal schon schreien, was sind dann 5 Ausrufezeichen, wildes ausrasten? 😉

Auf die Namespaces hab ich hingewiesen (Ist die gleiche Seite nur auf Deutsch).
Und hab einen Vorschlag für eine dll/Ordner Struktur gemacht.

Das die Namespaces so zu benennen, ein Antipattern ist. Wusste ich jetzt nicht (wäre schon hier einen Link zu bekommen, damit ich mich in das Thema einlesen kann), da es ja auch sehr häufig von Entwicklern verwendet wird. So auch z.B. von Raffaele Garofalo in seinem Buch „Building Enterprise Applications with WPF and the MVVM Pattern“.

@Seiyaru
Bekommst du bei dem Using einen Compiler Fehler oder einen hinweiß von z.B. Codeanalysis, das das Objekt gegebenen falls mehrfach Disposed wird?

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

Der Satz macht also inhaltlich keinen Sinn. Verwendest Du ein using(), dann verwendest Du indirekt auch ein Dispose.

Das ist aber ein anderes Thema; mach dafür bitte einen extra Thread auf.

Ich habe mich sicher undeutlich ausgedrückt. Das Prinzip von Using habe ich denke ich verstanden.=)

Danke für die ausführliche Erklärung. =)

@Palin

Ich habe mich heute früh nochmal versucht und nun funktioniert es problemlos.
Wie es manchmal so ist, war ich wohl die exception 😄

Für mich als "Anfänger" wäre es da noch interessant wie du/ihr vorgeht bei der DLL Erstellung?
Schreibt ihr erstmal die Anwendung mit den dazugehörigen Klassen und wenn, das Grundgerüst steht, erstellt ihr die einzelnen DLL´s?

Ich würde da gerne den Ablauf besser verstehen.

16.835 Beiträge seit 2008
vor 7 Jahren

[
Schreibt ihr erstmal die Anwendung mit den dazugehörigen Klassen und wenn, das Grundgerüst steht, erstellt ihr die einzelnen DLL´s?

Das wäre doppelte Arbeit.
Der Sinn dieser Separierung ist die Modularisierun und die Wiederverwendbarkeit. Im optimalen Fall hast Du ja bereits bei Projektbeginn gewisse Funktionalitäten durch vorhandene DLLs (zB. Datenbank).

Zudem plant man vor der ersten Zeile Code eine gewisse Grundarchitektur.

2.207 Beiträge seit 2011
vor 7 Jahren

Für mich als "Anfänger" wäre es da noch interessant wie du/ihr vorgeht bei der DLL Erstellung?
Schreibt ihr erstmal die Anwendung mit den dazugehörigen Klassen und wenn, das Grundgerüst steht, erstellt ihr die einzelnen DLL´s?

Ich würde da gerne den Ablauf besser verstehen.

Hallo Seiyaru,

irgendwann bekommst halt auch so eine Art Grundgerüst, wie Abt das oben schon gut beschrieben hat, was du "nur" noch abänderst. Das hilft dir dann meist und du hast du die Struktur schon.

Beachte aber, dass mit dem Code auch die Architektur wächst und sich verändert. Mal mehr, mal weniger. Wenn du also heute riesige Projekte siehst, dann ist die Architektur nicht von Anfang an so da gewesen. Da ist immer was dazu und weggekommen. Dafür gibts u.a. Architekten 😃

Gruss

Coffeebean

P
1.090 Beiträge seit 2011
vor 7 Jahren

Für mich als "Anfänger" wäre es da noch interessant wie du/ihr vorgeht bei der DLL Erstellung?
Schreibt ihr erstmal die Anwendung mit den dazugehörigen Klassen und wenn, das Grundgerüst steht, erstellt ihr die einzelnen DLL´s?

Ich würde da gerne den Ablauf besser verstehen.

Hi Seiyaru,

grundlegend hängt erst mal viel von der Art und dem Umfang der Software ab die du schreiben möchtest.

Bei um mal zwei Beispiel zu nennen. Man kann Software Horizontal oder auch Vertikal aufteilen (beides kann man auch mischen und es gibt noch andere Ansätze).
Bei einer Horizontalen Aufteilung Orientiere ich mich eher an dem Schichten (PL, BL, DAL). Mal vereinfacht. Bei einer Web Anwendung, kann ich Datenbanken haben, einen Service Layer der Funktionen bereitstellt und ein Web Oberfläche. Hier könnte man die 3 Aspekte (Schichten) Trennen.

Ein Beispiel für die Vertikale Aufteilung sind z.B. Plugins. Hier wird nach den Funktionalitäten aufgeteilt.

Wenn du weißt was für eine Anwendung du schreiben möchtest, Ergeben sich daraus schon meist Grundlegende Anforderungen an die Software Architektur, die du Erfüllen solltest.

Die Herangehensweise ist auch von Entwickler zu Entwickler unterschiedlich. Um mal 2 Extreme zu nennen. Es gibt die die erst mal eine Woche plane bevor sie eine Zeile Code schreiben und Gute Software damit schreiben. Und es gibt Leute, die sich hinsetzen Code schreiben, und wärmend des Programmierens ihre Klassen hin und herschieben und in der selben Zeit genau so guten Quellcode schreiben.
Ich denke bei den Meisten liegt es irgendwo dazwischen.

Dazu können natürlich noch Zwänge von Außen kommen. Z.B. vom Kunden oder der Firma, möchte der/die erst mal ein genaues Konzept haben bevor an dem Projekt gearbeitet wird oder möchten die lieber möglichst schnell einen Prototypen haben um die Funktionalitäten zu sehen.

Du wirst da mit der Zeit, wohl einfach deine Eigenart zu Programmieren finden. Und ja bis dahin sicher auch viele Fehler machen. Ich kenne jetzt keinen guten Programmierer, der keine Fehler gemacht hat. Ich kenne nur schlechte Programmierer die Behaupten nie Fehler gemacht zu haben.

Was die Aufteilung in Dlls angeht. Es bietet natürlich die Möglichkeit der wieder Verwendung in anderen Projekten. Dazu kommt aber auch mit zusätzlichen Dlls (Projekten in einer Solution) ein gewisser Overhead. Viele Projekte in einer Solution kann die Ladezeit deutlich erhören und die Builddauer deutlich verlängern.

Probier es da vielleicht erst mal mit einer logischen Aufteilung, also Namespaces. Bevor du eine physikalische Aufteilung vornimmst (DLLs).

MFG
Palin

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.835 Beiträge seit 2008
vor 7 Jahren

Viele Projekte in einer Solution kann die Ladezeit deutlich erhören und die Builddauer deutlich verlängern.

Mein Rat:Niemals diesen gedanklichen Ansatz fahren.

Ja, die Build-dauer dauert länger. Ja die Ladezeiten von DLLs steigern sich; aber niemals in dem Bereich, dass sie den Sinn von DLLs und der physikalischen Separierung aufheben.
Es ist immer noch alles im Millisekundenbereich.
Ob das jetzt 0.001ms braucht oder 0.005: ja, das ist eine deutliche Steigerung in Summe: merkt aber in der Totalen kein Mensch.
Das merkt man dann wenn man komplexe Anwendungen auf nem Mobiltelefon ohne entsprechende Leistung hat; aber auch da zieht man andere Optionen vor.
Sowas ist Micro Performance.... bitte wieder vergessen an dieser Stelle.

Das Aufteilen in DLLs hat den netten Nebeneffekt - gerade, wenn man am Anfang mit Architekturen nicht so vertraut ist - dass man Fehler sehr schnell erkennt.

Beispiel:
Man soll von UI auf BL, und von BL auf DAL zugreifen - nicht umgekehrt.
Machst Du das innerhalb eines Projektes (ausversehen): Du merkst es nicht.
DLLs können aber immer nur in eine Richtung referenziert werden ( also UI verwendet BL ).
Versuchst Du dann trotzdem von BL auf UI zuzugreifen, dann krachts.

Es ist also auch ein selbstständiges, disziplinarisches Instrument, das gerade am Anfang vielen Leuten hilft, die Grundstruktur beizubehalten und die Übersicht zu wahren.

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

Beispiel:
Man soll von UI auf BL, und von BL auf DAL zugreifen - nicht umgekehrt.
Machst Du das innerhalb eines Projektes (ausversehen): Du merkst es nicht.
DLLs können aber immer nur in eine Richtung referenziert werden ( also UI verwendet BL ).
Versuchst Du dann trotzdem von BL auf UI zuzugreifen, dann krachts.

In dem 3-Schichten Thread wird doch grafisch aufgezeigt das du auch mit der Präsentationsschicht auf die Datenzugriffsschicht zugreifen kannst?

Ich habe mir das jetzt angeschaut und versucht mein Projekt so zu strukturieren, sprich alle Datenbank relevanten Klassen die DAL alle Forms in die PL usw.

Derzeit sehe ich aber die Trennung noch nicht wirklich, da in der BL meine Klassen "untergebracht sind" die quasi der Support für die PL und DAL sind?

Ich habe es jetzt erstmal so unterteilt

Company.Datenbankklasssen(DAL)
Company.Services(BL)
Company.UI(PL)

Alle Schichten haben nichts miteinander zu schaffen, ich greife mit der PL auf die DAL wenn ich Einträge in der Datenbank neu erstelle oder ändere. Die BL Klassen sind nur unterstützer die zum Beispiel, ein String Verschlüsseln und Entschlüsseln oder ein PDF erzeugt.

16.835 Beiträge seit 2008
vor 7 Jahren

PL auf DAL stellt _eigentlich _eine Schichtverletzung dar.

[Artikel] Drei-Schichten-Architektur
Hier wird zwar noch in strukt unterschieden.
Ich würde aber eher sagen, dass strikt das reguläre ist und PL auf DAL eine vereinfachte Darstellung entspricht.
Vereinfachte Dreischichtenarchitektur wird sie auch in manchen Büchern und Talks genannt.

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

Das würde ja bedeuten Die PL kommuniziert ausschließlich mit der BL.

Sprich wenn in der Anwender Daten in eine Maske eingibt, z.B KundenName, KundenStraße usw.

Werden dies Daten dann an die BL übergeben, und diese wiederum übergibt dann die Daten als Parameter an die Methoden in der DAL?

Das bedeutet doch, dass die Klassen in der BL, auf die Klassen der DAL zugreifen müssen?

1.040 Beiträge seit 2007
vor 7 Jahren

Das bedeutet doch, dass die Klassen in der BL, auf die Klassen der DAL zugreifen müssen?

Ja, und das ist auch kein Problem. =)

S
Seiyaru Themenstarter:in
23 Beiträge seit 2016
vor 7 Jahren

Tut mir leid das ich da nochmal nachfragen muss.... 😕

Das heißt ich kann in einer Klasse in der BL eine Objekt-Instanz aus der DAL erzeugen? Steh grade auf dem schlauch.....

16.835 Beiträge seit 2008
vor 7 Jahren

Das würde ja bedeuten Die PL kommuniziert ausschließlich mit der BL.

Das ist prinzipiell auch korrekt so.

Modelle in einer Businessschicht sehen i.d.R. nicht so aus wie die Modelle(=> Entitäten) in einer Datenbank(schicht).
I.d.R. ist dies nur bei kleineren Anwendungen der Fall. Dort ist dann auch die vereinfachte Dreischichtenarchitektur (teilweise) angebracht

Auch sehen die Modelle i.d.R. nicht so aus, wie man sie in der UI anzeigen würde. Deswegen verwendet man in der UI auch ViewModels.

P
1.090 Beiträge seit 2011
vor 7 Jahren

Viele Projekte in einer Solution kann die Ladezeit deutlich erhören und die Builddauer deutlich verlängern.

Mein Rat:Niemals diesen gedanklichen Ansatz fahren.

Ja, die Build-dauer dauert länger. Ja die Ladezeiten von DLLs steigern sich; aber niemals in dem Bereich, dass sie den Sinn von DLLs und der physikalischen Separierung aufheben.
Es ist immer noch alles im Millisekundenbereich.
Ob das jetzt 0.001ms braucht oder 0.005: ja, das ist eine deutliche Steigerung in

Also wir hatten, viele DLL (Es gab halt nicht nur eine Horizontale sondern auch eine Vertikale aufteilung) und hab mir mal ein altes Build Log angeschaut. Damals hat der Build ca. 13min (genauer 13,7 schwankt aber immer etwas) gedauert. Nach zusammen führen einiger DLLs dauert der Build aktuell ca. 3min (3,5). Das sind grob 10min, ich denke mal schon eine Relevante Zeitersparnis.

Man soll von UI auf BL, und von BL auf DAL zugreifen - nicht umgekehrt.
Machst Du das innerhalb eines Projektes (ausversehen): Du merkst es nicht.
DLLs können aber immer nur in eine Richtung referenziert werden ( also UI verwendet BL ).
Versuchst Du dann trotzdem von BL auf UI zuzugreifen, dann krachts.

Das funktioniert solange, du nur genau 3 DLLs (bzw eine kleine Anzahl hast) oder besser gesagt eine Horizontale Separierung. Wenn du zusätzlich noch Vertikal separierst, Können DLLs vom DAL, DLLs vom BL referenzieren ohne das es knall.
Und wenn es geht wird es früher oder später jemand machen. Wir haben es beim zusammen führen der DLLs gemerkt.

Ich sag ja nicht das keine Aufteilungen in Dlls vorgenommen werden soll. Sondern das es gute Gründe dafür geben sollte.

@Seiyaru
Grade bei größeren Projekten mit mehreren Beteiligten ist die strikte Trennung Sinnvoll. Dann Wissen alle, das die immer über denn BL auf dem DAL zugreifen müssen. Habe ich die Strikte Trennung nicht, greift nachher ein Teil direkt auf den DAL zu während andere den BL verwenden. Wenn ich dann noch etwas im BL mit den Daten mache, ist es nachher korrekt bei den Klassen die den BL verwenden und wird nicht richtig angezeigt bei denen die den DAL verwenden.

Bei kleinen Anwendungen oder Anwendungen in denen es Haupt sachlich, darum geht Daten in der Datenbank zu editieren. Kann dadurch ein mehr Aufwand entstehen. Diesen kann man aber sehr gut minimieren indem man eine oder mehrere generische Basisklassen erstellt. Ich erstelle dann eigentlich nur eine neue Klasse und Erbe von der Basisklasse. So kann ich die Strikte Trennung beibehalten ohne viel Aufwand zu haben.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.835 Beiträge seit 2008
vor 7 Jahren

Ich bezweifle mehr als massiv, dass eine Buildzeit-Ersparnis von 70-75% nur mit dem Zusammenführen der Projekte zutun hat...
Plausibel ist das jedenfalls nicht.

Und wenn: sowas ist kein Anfängertipp und daher hier völlig fehl am Platz.
Solche Optimierungen machen in diesem Stadium einfach keinen Sinn. Da gibts 1000 wichtigere Dinge, die ihm wirklich helfen würden.

P
1.090 Beiträge seit 2011
vor 7 Jahren

Und wenn: sowas ist kein Anfängertipp und daher hier völlig fehl am Platz.
Solche Optimierungen machen in diesem Stadium einfach keinen Sinn. Da gibts 1000 wichtigere Dinge, die ihm wirklich helfen würden.

Wenn jemand Fragt wie man Code in Dlls Aufteilt, darauf hinzuweisen das es dort auch Nachteile gibt halte ich nicht für Falsch.

Und mit dem Vorschlag.

Probier es da vielleicht erst mal mit einer logischen Aufteilung, also Namespaces. Bevor du eine physikalische Aufteilung vornimmst (DLLs).

Bin ich auch nicht ganz alleine.

Consider first organizing your code logically (namespaces) rather than physically (assemblies).

Assembly Fiefdoms: What's the Right Number of Assemblies/Libraries?

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

F
10.010 Beiträge seit 2004
vor 7 Jahren

Doch, das kann gut sein.
Liegt aber nicht an dem Compilieren, sondern dadurch das deutlich mehr Dateien erzeugt werden.

Wenn man also bei der Erstellung der solution nicht nachdenkt, und die std Einstellungen unter "Erstellen", nämlich "bin\debug" beibehält wird ja in jedem Verzeichnis die DLL erzeugt und jede auf die verwiesen wird kopiert.
Hat man dann also Ketten von Verweisen hat , kann es schnell sein das so eine Solution dann ein und die selbe dll 50 mal hat.
Das dann selbst moderne platten und ssd's mt den Datenmengen "überfordert" sind ist ersichtlich.

Wenn man also nicht nur Architektur anschaut sondern auch mal versucht seine Tools zu verstehen, dann wird es deutlich besser.

Habe bei unserer aktuellen Anwendung auch auf ein Ausgabeverzeichnis umgestellt, und spare so 800MB an zu erzeugenden DLL's ein.
Das verkürzt ein Build schon ungemein.

16.835 Beiträge seit 2008
vor 7 Jahren

Genau die Antwort hab ich erhofft, FZelle 😃

Wenn man also nicht nur Architektur anschaut sondern auch mal versucht seine Tools zu verstehen, dann wird es deutlich besser.

.. und wenn man sein Build System im Griff hat - eben seine Tools versteht -, dann wird auch nichts unnötig erstellt.
Deswegen gibts auch Dinge wie http://cakebuild.net/

Aber eine Architektur nach der Builddauer zu richten.. da geh ich nicht wirklich mit.
Ich kümmer mich lieber um die ordentliche Beseitigung eines Schmerzes, statt diese nur zu unterdrücken.
Säg' mir ja auch nicht mein Bein ab, nur weil ich mich am Arm geschnitten hab..

P
1.090 Beiträge seit 2011
vor 7 Jahren

Wenn man also bei der Erstellung der solution nicht nachdenkt, und die std Einstellungen unter "Erstellen", nämlich "bin\debug" beibehält wird ja in jedem Verzeichnis die DLL erzeugt und jede auf die verwiesen wird kopiert.

Also unser Buildserver, hat die Dateien in einer Verzeichniss erzeugt, daran hat es nicht gelegen.

Wenn man also nicht nur Architektur anschaut sondern auch mal versucht seine Tools zu verstehen, dann wird es deutlich besser.

Auch hat sich die Software Architektur dadurch nicht geändert (mal abgesehen, dass wir da ein paar Fehler behoben haben. Z.B. das Klassen des DAL den BL Referenziert haben.)

Was sich geändert hat ist die physikalische Aufteilung.

Aber eine Architektur nach der Builddauer zu richten.. da geh ich nicht wirklich mit.
Ich kümmer mich lieber um die ordentliche Beseitigung eines Schmerzes, statt diese nur zu unterdrücken.
Säg' mir ja auch nicht mein Bein ab, nur weil ich mich am Arm geschnitten hab..

[Ironie]
Mal wieder ein sehr Fachliches Argument, man merkt direkt, das du Arme und Beine nicht verwechselst. Bei Fachlich so wichtigen Aspekten, wie Arme und Beine, scheint scheint dir nur einfach entgangen zu sein das sich die Architektur dadurch nicht geändert hat.
Ist aber Fachlich auch nicht so wichtig wie Arme und Beine.
[/Ironie]

Nun gut mir glaubt ihr nicht, Scott Hanselman auch nicht (finde ich schon bemerkenswert).
Hier dann noch mal jemand anderes zu dem Thema.

Chad Myers:Project anti-pattern: Many projects in a Visual Studio Solution File

Habt ihr eigentlich mal einen Link zu dem Thema in dem jemand Fachlich behauptet, möglichst viele Assemblys seien eine gute Lösung?

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

F
10.010 Beiträge seit 2004
vor 7 Jahren

Auch hat sich die Software Architektur dadurch nicht geändert (mal abgesehen, dass wir da ein paar Fehler behoben haben. Z.B. das Klassen des DAL den BL Referenziert haben.)

Aber eine Architektur nach der Builddauer zu richten.. da geh ich nicht wirklich mit.

Pisa lässt Grüßen.

Nicht die Architektur anpassen sondern zusätzlich zu Architektur auch die Tools kennenlernen die man benutzt.

1.040 Beiträge seit 2007
vor 7 Jahren

Also wir haben in unserer Solution ~60 Projekte und brauchen beim Bauen mit Visual Studio rund 45 Sekunden. Aufgrund einer weiteren Trennung (Repositories) haben wir sogar verschiedene Ausgabeverzeichnisse (~6).

In einer früheren Firma gab es gefühlt 100 Solutions mit max. 5 Projekten, da selbst das Öffnen und Bauen teilweise langsam war, dachte ich zu dem Zeitpunkt mehr Projekte pro Solution geht gar nicht. Bin allerdings eines besseren belehrt worden (kommt natürlich auch immer darauf an, wie gut der Rechner ist, die Ausstattung in der alten Firma war, naja, eher mau)...

742 Beiträge seit 2005
vor 7 Jahren

Ich finde die Build Zeit sehr wichtig - gerade für TDD - und würde dafür auch einige Kompromisse eingehen.

Wenn man seine Anwendung modularisiert, geht es auch immer darum, kleine Hürden aufzubauen, diese Modularisierung nicht zu umgehen. Diese werden aber zumindest in großen Projekten sehr häufig umgangen, zum Beispiel mit Aufrufen auf die Datenbank. Ich persönlich finde die Hürden bei mehreren Projekten aber so niedrig, dass man auch darauf verzichten kann. (Da sind mehrere Solutions und Micro Services besser geeignet wie ich finde).

Für mich sind Projekte aber vor allem eine Möglichkeit mich auf ein Problem zu fokussieren. Pro Projekt würde ich auch ein Projekt für die Tests anlegen und hat dann beides sehr nahe beieinander. Das reduziert die Build Zeit für die Tests drastisch und zur Not kann man ja auch alle anderen Projekte in Visual Studio vorübergehend deaktiveren.

Außerdem werden größere Refaktorisierungs leichter. Hierbei hat man bei wenigen Projekten oftmals das Problem dass für einige Stunden erstmal gar nichts mehr funktioniert. Das Projekt buildet nicht mehr und Tests kann ich auch nicht laufen lassen. Habe ich viele Projekte angelegt, kann ich mich von unten nach oben durcharbeiten und Etappenweise das Refactoring implementieren.

3.003 Beiträge seit 2006
vor 7 Jahren

Da möchte ich teilweise widersprechen. für TDD ist die Buildzeit nur wichtig, wenn die Infrastruktur nicht entsprechend organisiert ist.

Unsere Projekte mit allen Abhängigkeiten für alle erforderlichen Frameworks zu erstellen, würde ca. anderthalb Stunden dauern. Für den einzelnen Entwickler ist es keine praktikable Lösung, jedesmal so lange Däumchen zu drehen, um DANN alle 25k unit Tests laufen zu lassen (die in Summe noch einmal deutlich länger brauchen). Für so etwas gibt es CI. Ich möchte mir gar nicht vorstellen, wie die Entwicklung hier aussähe, wenn nicht "unten" ununterbrochen kompiliert und getestet würde. Natürlich kann ich auch die Tests für meinen aktuellen Code lokal laufen lassen, aber das zeigt mir nicht, ob ich eine Funktionalität in einem abhängigen Projekt zerstört habe. Diese Sicherheit hätte ich ohne CI nur, wenn ich wirklich lokal alles laufen ließe. Absolut unrealistisch.

Langer Rede kurzer Sinn: wenn die Buildzeit ein Problem wird, dann MUSS man sich Gedanken über seine Infrastruktur machen, statt an den Projektmappen zu basteln in der Hoffnung, ein paar Minuten rauszuholen. Das schiebt das Problem nur hinaus und löst es nicht.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

F
10.010 Beiträge seit 2004
vor 7 Jahren

Langer Rede kurzer Sinn: wenn die Buildzeit ein Problem wird, dann MUSS man sich Gedanken über seine Infrastruktur machen, statt an den Projektmappen zu basteln in der Hoffnung, ein paar Minuten rauszuholen. Das schiebt das Problem nur hinaus und löst es nicht.

Dem muss ich wiedersprechen.
Sicherlich ist es ab einer gewissen Größe wichtig das der CI ( wir benutzen Jenkins, früher TeamCity ) auf einer entsprechend ausgestatteten Maschine läuft, aber wenn man durch das bloße ändern des Ausgabepfades die Compilezeit halbieren kann, dann sollte man das auch tun.

Das ist wie beim entwickeln selber, wenn ich weiß das das Dictionary<T> die schnellere Lösung ist, bleibe ich doch nicht bei nem Array und sage dem Kunden er muss eben nen schnelleren Rechner kaufen.

3.003 Beiträge seit 2006
vor 7 Jahren

Natürlich. Missverständnis, ich bezog mich nicht auf die Änderung, ein gemeinsames Lib-Verzeichnis zu nutzen (liegt doch auf der Hand, begründet hast du das ja ganz oben), sondern mit "Basteleien" meinte ich, die Anzahl der DLL optimieren zu wollen, weil's dann schneller kompiliert.

Wenn ich wirklich eine Projektmappe mit 100+ Projekten drin habe und das kein schönes Arbeiten ist, kann ich das Projekt, an dem ich arbeite, immer noch lokal in eine temporäre Projektmappe schieben und weiterarbeiten, ohne dabei irgendwas zu verlieren. Kompiliert wird sowieso mit einem tool, das nach Möglichkeit die Projektmappeneinstellungen der Nutzer ignoriert. (hier: nant). So kann man sogar erst beim "richtigen" build außerhalb von VS das gemeinsame lib-Verzeichnis nutzen, wenn man möchte (halte ich für Unsinn, aber des Menschen Wille...)

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

16.835 Beiträge seit 2008
vor 7 Jahren

Wenn ich les, dass Leute 1,5h für nen Build brauchen... dann hat das nichts mehr mit Continuous Integration zutun.
Selbst größere Anwendungen, SQL Server, Windows 10.. brauchen nicht mal Ansatzweise so lange. Nur mal so als Wink mit dem Zaunpfahl.
So arbeitet kein größeres Projekt mehr.

Die Anwendungsarchitektur wurde dabei eher nicht an den Build ausgerichtet.... 😃
Pisa hin oder her. Da sieht eher die Projektstruktur aus wie Kraut und Rüben.

3.003 Beiträge seit 2006
vor 7 Jahren

So arbeitet kein größeres Projekt mehr.

Da weicht deine Erfahrung von meiner ab. Die längste Einzel-Buildzeit pro target ist für x64/.net 4.5 (logisch) 22 Minuten. Es existieren Builds für net 1.1, net 2.0, net 3.5, net4.5, net 4.5 x64, wix-setups, netcf 2.0, netcf 3.5, Android und noch ein paar mehr. Zusammen braucht ein build gegen alle targets 1h32 (jeweils letzter aktueller Build). Das ist normal. Das läuft auch nicht alles auf einer Maschine, hier rennen 22 TC-Agents. Vielleicht eine andere Dimension, als du gewohnt bist.
Wenn ich was einchecke, was den build bricht, merke ich das nach ca. 5 Minuten. Wenn ich was einchecke, was die Tests bricht, merke ich das nach ca. 30-45 Minuten, je nach Auslastung der Server. "tests bricht" heisst hier, dass meine Änderungen Auswirkungen in einem Projekt haben, das ich nicht auf dem Schirm hatte. Lokale Tests pro Projekt laufen natürlich auch. TDD.

Ziemlich mutig, ohne genauere Kenntnis der Prozesse und der Software solche Unterstellungen wie du zu treffen, wirklich.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

16.835 Beiträge seit 2008
vor 7 Jahren

Vermutlich sind es auch einfach Begrifflichkeiten, die hier manche - darunter leider Dich - zu ausfallenden Ausdrucksweisen leiten.
Ich sprach von dem Build, Du von den Builds.

Auf dem Level weiter zu diskutieren macht kein Sinn für mich; daher bin ich raus.

742 Beiträge seit 2005
vor 7 Jahren

@LaTino

Ich habe im Grund genommen die gleiche Aussage bzgl. TDD gemacht. Kleinere Module in Projekten helfen auch enorm bei TDD. Lange Build Zeiten kann man einfach nicht immer verhindern.

3.003 Beiträge seit 2006
vor 7 Jahren

@malignate: Ja, hab vielleicht mehr in deinen Satz zu TDD gelesen, als drin war. Mit war der Punkt wichtig, dass es (aus meiner Sicht) ein Fehler ist, die Projektmappenstrukturen an den Buildprozess anzupassen (nämlich, um ihn zu verschnellern). Das erkauft man sich mit Nachteilen im Bereich der Organisation des Codes. Stattdessen gibt es Werkzeuge, um den Build/Test/Publish-Ablauf fließend zu halten, und spätestens sobald ein Projekt an Grenzen stößt, sollte man auch über deren Einsatz nachdenken. Das heisst NICHT, dass man den Build-Prozess selbst nicht optimieren soll. Nur nicht auf Kosten der Projektorganisation.*
Hoffe, ich habe damit alle Klarheiten beseitigt 😉.

LaTino

  • Palins Post klang ein bisschen danach (wobei er auch nur vom Zusammenführen einiger dll sprach).

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

P
1.090 Beiträge seit 2011
vor 7 Jahren

Natürlich. Missverständnis, ich bezog mich nicht auf die Änderung, ein gemeinsames Lib-Verzeichnis zu nutzen (liegt doch auf der Hand, begründet hast du das ja ganz oben), sondern mit "Basteleien" meinte ich, die Anzahl der DLL optimieren zu wollen, weil's dann schneller kompiliert.

Ich hab mich da wohl missverständlich ausgedrückt. Wir habe ein Umfangreichs Refactoring durchgeführt. Bei dem hatten wir viele DLLs, die nur ein oder zwei Klassen enthielten zusammen geführt. Weil wir fanden das sie Logisch zusammen passten und auch immer Zusammen Ausgeliefert wurde.
Das die Builddauer dadurch reduziert wurde war ein positiver Nebeneffekt. Und das sie so deutlich Reduziert wurde, hat mich auch gewundert, deshalb ist es mir wohl besonders im Gedächtnis geblieben.

Die Builddauer sollte auch nicht direkt Einfluss auf die Anzahl meiner DLLs haben.
Wenn ich 100+ DLLs brauch, brauche ich sie und dann muss der Build damit fertig werden.

Die grundlegend frage ist brauche ich 100+ DLLs.
Und wenn ich mit der Aufteilung in Namespaces zum gleichen Ergebnis komme.
Sollte ich die Vor- und Nachteile von 1 DLL zu vielen Vergleichen und dann kann man auch die Builddauer betrachten.
Wie so soll ich also 100+ DLLs verwenden, wenn das einzig was ich dadurch erreiche, das der Build länger dauert.
Und es gibt ja auch noch andere Grunde. Die kann man in den Vorher verlinkten Beiträgen finden.

Um hier Scott Hanselman noch mal zu Zitieren.

Consider first organizing your code logically (namespaces) rather than physically (assemblies)..

Ich halte den Vorschlag jetzt nicht für Falsch und hab hier jetzt auch kein Argument gelesen ihn nicht zu befolgen.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern