Laden...

Forenbeiträge von Davaaron Ingesamt 106 Beiträge

27.01.2021 - 19:57 Uhr

Ok, ich probiere es mal. Ich hoffe mal, dass die anderen Services dann dennoch auf die Datenbank zugreifen können 😄

Danke schon mal.

Edit: Super, hat einwandfrei funktioniert! Jetzt kann ich mich auch über SSMS einloggen und die Applikation läuft dennoch ganz normal, macht ja aber auch Sinn, weil die 31000 nur von Docker aus "exposed" wird und intern immer noch die 1433 genutzt wird.

27.01.2021 - 17:49 Uhr

verwendetes Datenbanksystem: SQL Server

Hallo,

ich habe eine Entwicklungsumgebung vorliegen, bei der ein Docker-Image erstellt wird und in einem Docker-Container läuft. Das Image ist ein mssql-Image und dort wird eine DB inklusive Seeding und Migration erstellt.
Nun würde ich gerne von meinem Host über SQL Server Management Studio (SSMS) auf diese Datenbank zugreifen. Der ConnectionString für die DB beinhaltet

Data Source=localhost

sowie eine User ID und ein Passwort.
Versuche ich nun über SSMS diese Credentials (SQL Authentifizierung) einzugeben und trage als Servername "localhost" ein, bekomme ich einen Fehler:
"Fehler bei der Anmeldung für den Benutzer "sa" (Microsoft SQL Server, Fehler: 18456)"

Verwende ich statt der SQL Auth Methode die Windows Auth Methode, dann komme ich auf meine localhost Datenbank, allerdings ist dies nicht die DB aus dem Docker.

Hat jemand eine Idee, wie ich auf die Docker DB zugreifen kann? Leider kann ich an der docker-Datei nichts ändern.

04.12.2020 - 18:26 Uhr

Wie fast immer: Ich schreibe einen Post und danach fällt mir die Lösung ein.. 😄

Der Name des Target-Elements muss auf "AfterBuild" gestellt werden und app.config muss App.config heißen. Das wars auch schon.

04.12.2020 - 18:12 Uhr

Hi,

mein Ziel ist es, meine App.*.config Dateien bei jedem Build-Durchlauf zu transformieren, so dass immer die korrekte App.config bereitgestellt wird.
Dazu habe ich in meiner *.csproj folgendes hinzugefügt:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="CoreCompile" Condition="exists('app.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory -->
    <TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
    <!-- Force build process to use the transformed configuration file from now on. -->
    <ItemGroup>
      <AppConfigWithTargetPath Remove="app.config" />
      <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
        <TargetPath>$(TargetFileName).config</TargetPath>
      </AppConfigWithTargetPath>
    </ItemGroup>
  </Target>

Dies funktioniert, wenn der obj/Debug bzw obj/Release Ordner vorhanden sind und die *.exe ebenfalls. Lösche ich allerdings den obj-Ordner, dann funktioniert das Builden nicht mehr. Fehlermeldung:

  Microsoft.Common.CurrentVersion.targets(4187, 5): [MSB3030] Datei "obj\Debug\MeinProjekt.exe" konnte nicht kopiert werden, da die Datei nicht gefunden wurde.

Wieso wird diese Fehlermeldung geworfen? Ich versuche doch gar nicht auf die *.exe zuzugreifen. Vielleicht wegen der Variable $(TargetFilename)?

23.09.2020 - 13:51 Uhr

Ich habe da ein eigenes Konstrukt, benutze kein OpenId oder OAuth2.
Mittlerweile habe ich es hinbekommen, dass mein Token in jedem Request mitgesendet wird und somit kann ich mich authorisieren.
Den JWT Token nutze ich im Header um den User zu authorisieren, der Refresh-Token ist ein Eigenkonstrukt.

Was meinst Du "in Swagger" ? Swagger ist ja nur ein "Protokoll", das hat ja keinen eigenen Controller.

Stimmt, ich meinte damit den Endpunkt zum Einloggen, den ich in Swagger UI sehe. Dieser Endpunkt hat ja einen Controller.

Fürs Protokoll: Du verwendest SwashBuckle, richtig?

Richtig.

Noch eine andere Frage (oder dazu einen eigenen Thread aufmachen?).
Ich würde gerne, bevor die index.html von Swagger UI aufgerufen wird, eine eigene HTML Seite anzeigen. Dies habe ich auch hinbekommen, indem ich den IndexStream überschrieben habe:


 app.UseSwaggerUI(c =>
            {
              
                c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("My.Custom.Namespace.Resources.firstPage.html");
            });

Nun würde ich gerne zur originalen Swagger UI Index.html navigieren. Dazu habe ich mir von Github das originale index.html heruntergeladen.
Das Problem: Ich weiß nicht, wie ich dahin navigieren soll. Beim Starten der WebAPI komme ich direkt auf "index.html", d.h. meine firstPage.html dient logischerweise als index.html. Die originale index.html von Swagger UI wird nicht migeladen.
Hat das schon mal jemand gemacht?

23.09.2020 - 00:08 Uhr

Hi,

für meine Asp.Net Core Web Api habe ich einige Controller, die mit dem AuthorizeAttribute versehen sind. Für die Autorisierung benutze ich JWT Tokens. Nun will ich Swagger UI nutzen, um die WebAPI zu dokumentieren und zu testen.
Da nicht jeder die API nutzen können soll, will ich diese absichern, allerdings nicht mit einem JWT Token, sondern der Benutzer soll sich normal per Basic Auth autorisieren. Dafür habe ich auch keinen AuthController, der den JWT inklusive Refresh-Token zurückgibt.
Doch wie erreiche ich das?
In meiner Startup.cs habe ich folgenden Code:

  services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" });

                // add JWT Authentication
                var securityScheme = new OpenApiSecurityScheme
                {
                    Name = "JWT Authentication",
                    Description = "Enter JWT Bearer token **_only_**",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer", // must be lower case
                    BearerFormat = "JWT",
                    Reference = new OpenApiReference
                    {
                        Id = JwtBearerDefaults.AuthenticationScheme,
                        Type = ReferenceType.SecurityScheme
                    }
                };
                c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {securityScheme, new string[] { }}
                });

                // add Basic Authentication
                var basicSecurityScheme = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "basic",
                    Reference = new OpenApiReference { Id = "BasicAuth", Type = ReferenceType.SecurityScheme }
                };
                c.AddSecurityDefinition(basicSecurityScheme.Reference.Id, basicSecurityScheme);
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {basicSecurityScheme, new string[] { }}
                });

                
            });

Zum Testen habe ich hier zwei Autorisierungs-Methoden: JWT und Basic.
Um meinen JWT Token zu bekommen, nutze ich meinen AuthController in Swagger. Wenn ich mich darüber anmelde, wird der Token leider nicht im Header mitgegeben. Bei Basic Auth wird der Basic string allerdings mitgegeben.
Mit der Dokumentation von Swagger UI kann ich leider überhaupt nichts anfangen.

Zusätzlich habe ich noch eine andere Frage:
All meine Web API Endpunkte sind nun mit einem Schloss versehen, obwohl nicht alle Endpunkte mit dem AuthorizeAttribute versehen sind. Wieso? Und wie kann ich nur jene, die auch eine Autorisierung benötigen, mit einem Schloss versehen?

21.09.2020 - 20:41 Uhr

Habe über "Webverweis hinzufügen" die Dienst aus BC übernehmen können. Dies hat mir einige WSDL Dateien generier, allerdings kann Visual Studio die Klassen dafür nicht finden.
Bspw. habe ich eine "MitarbeiterPlan.wsdl" und der Webverweisname ist "SoapService".
Laut Blog-Post sollte ich nun den Service mit

SoapService.MitarbeiterPlan plan = new SoapService.MitarbeiterPlan() 

instanziieren und verwenden können. 🤔

Nur Probleme.. 😭

21.09.2020 - 19:19 Uhr

Warum magst Du darauf setzen?

Mögen ganz und gar nicht. Ich mag SOAP nicht, erinnert mich immer an WCF und das mochte ich auch nicht.

Aber ich denke ich muss, weil ich eine Codeunit habe, die ich gerne per API anstoßen will und das leider nur mit SOAP funktioniert laut Microsoft:

Microsoft Web-Services

Wüsste sonst nicht, wie ich das ermöglichen könnte.

21.09.2020 - 16:11 Uhr

Weiß jemand, ob man SOAP statt ODataV4 verwenden kann und ob es dafür schon Libraries gibt?

10.09.2020 - 19:49 Uhr

Aber es ist halt auch nur ein Code Generator, der nicht zwangsläufig alle Anforderung abdecken kann, die Du haben willst - und er ist veraltet.

Also ich benutze hierfür die "OData Connected Service" Erweiterung, die, glaube ich, nicht veraltet ist, denn sie erstellt OData V4 Services, die wiederum Klassen aus dem "OData Client" Projekt verwenden. Beispielsweise kommt der automatisch erstellte Datenkontext (DataServiceContext) aus Namespace "Microsoft.OData.Client".

Gibt es eine Möglichkeit, auch in Visual Studio die genauen Exceptions anzuzeigen?

Nein, denn Visual Studio ist prinzipiell nur eine Entwicklungsumgebung. Wenn Dir der OData Client das nicht aufsammeln, dann bekommst Du natürlich auch nichts.

Die Frage habe ich missverständlich gestellt. Damit meinte ich eher, ob ich es mithilfe der Connected Services (OData) so gut hinbekommen kann wie der Web Debugger von Fiddler.

@Papst, @Abt
Das würde wahrscheinlich mit OData Lib gehen (habe ich gerade eben durch Abts Link gefunden), aber das ist mir zu viel Aufwand für das aktuelle Projekt. Ich bleibe bei Fiddler (wieso Dinge neu erfinden). Wäre aber für die Zukunft sicherlich ein Gedanke wert, alleine schon des Loggings wegen.

10.09.2020 - 18:18 Uhr

Von der Struktur erinnert mich die OData Client API etwas an Entity Framework: Man hat einen Context mit allen Endpunkten als Properties (z.B. new MyContext().Persons.ToList()) und man kann Queries erstellen. Allerdings finde ich keinen Weg, direkt an den Http Client zu kommen.
Man kann an ein paar Events hängen bzw Actions/Funcs mitgeben, aber auch da sehe ich aktuell keine Chance. Ein Event "ReceivingResponse" scheint zunächst mal vielversprechend, aber in der HttpResponseMessage Klasse sehe ich den Body nicht bzw sehe auch da nur "400 - Bad Request".
Wahrscheinlich müsste ich mir das alles selbst aus dem Stream ziehen und dekodieren. Ich werde voerst bei Web Debugger bleiben.

10.09.2020 - 15:16 Uhr

Hi,

für meine Business Central API habe ich mir Connected Services (ist eine Erweiterung von Visual Studio, früher ging das mit "Add service reference") erstellt. Die Erweiterung erstellt mir Proxies aus der angegebenen $metadata-URL, die ich dann ganz einfach im Code verwenden kann.
Beim Testen dieser Services ist mir aufgefallen, dass ich manchmal ein "Bad Request" - 400 zurückbekomme, ohne Inhalt oder einer Bemerkung, was schief gelaufen ist. Zum Glück habe ich den Web Debugger von Telerik gefunden, der mir das Mitschneiden des Network traffics erlaubt und glücklicherweise auch die SSL (wohl eher TLS) verschlüsselten Requests and Reponses anzeigt. In der Reponse sehe ich dann, was genau schief gelaufen ist. Allerdings sehe ich die konkrete Fehlermeldung nicht beim Debuggen in Visual Studio.

Gibt es eine Möglichkeit, auch in Visual Studio die genauen Exceptions anzuzeigen?

03.09.2020 - 12:46 Uhr

Ok, das war einfach...
aus irgendeinem Grund war für Icons.xaml im "Benutzerdefiniertes Tool"-Eintrag "MSBuild:Compile" eingetragen. Diesen Eintrag habe ich entfernt und habe das Bild magnifier.png auf Resource gesetzt und "immer kopieren".

03.09.2020 - 12:18 Uhr

Hi,

damit ich künftig nicht mehr immer wieder von 0 anfangen muss, verlagere ich gerade ein paar UserControls in eine Library aus. Meine Vorstellung ist es, dass die Library schon alle ResourceDictionaries beinhaltet, die in dieser Library gebraucht werden wie Icons, Styles für Buttons, Texts, etc. Die Styles sollen später zu einem DefaultTheme.xaml zusammengefasst werden mithilfe von MergedResourceDictionary.
Die Library wird in meine App eingebunden und dort sollen die Styles geladen werden in der App.xaml.

Dabei hätte ich gerne volle Design-Time Unterstützung für die Library und für die App, d.h. der Designer sollte alle Styles und Icons anzeigen. Hier soll es aber zunächst mal nur um die Icons gehen. Im Anhang seht ihr übrigens die Projektstruktur.

Die Icons.xaml ist minimalistisch und sieht so aus:


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Davaaron.WPFManager.Resources">
    <BitmapImage x:Key="SearchIcon" UriSource="/Davaaron.WPF.Controls;component/Resources/Images/magnifier.png" />

</ResourceDictionary>

Als Beispiel ziehe ich mal meine SideMenuView heran:

<UserControl x:Class="Davaaron.WPF.Controls.Views.SideMenuView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Davaaron.WPF.Controls.Views"
             mc:Ignorable="d" 
             xmlns:vm="clr-namespace:Davaaron.WPF.Controls.ViewModels"
             d:DesignHeight="200" d:DesignWidth="150"
             d:DataContext="{d:DesignInstance vm:SideMenuViewModel, IsDesignTimeCreatable=True}">

    <!--<UserControl.Resources>
        <ResourceDictionary Source="../Resources/Dictionaries/Icons.xaml"/>
    </UserControl.Resources>-->
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBox VerticalContentAlignment="Center">Hello, dude</TextBox>
            <Button Grid.Column="1" Command="{Binding }">
                <!--<Image Source="{StaticResource SearchIcon}"></Image>-->
                <Image Source="../Resources/Images/magnifier.png"></Image>
            </Button>

        </Grid>
        <TreeView Grid.Row="1" ItemsSource="{Binding Items}">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type vm:SideMenuItem}" ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding DisplayName}"></TextBlock>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</UserControl>

Ich habe mehrere Wege ohne Erfolg probiert:

Fall 1 (auskommentierte Zeilen in SideMenuView.xaml):
Resource-Dictionary wird in dem UserControl innerhalb der Library eingebunden und im Image-Control wird die StaticResource angegeben. Der Designer zeigt das Icon nicht an, obwohl der Code fehlerfrei ist. IntelliSense zeigt mir sogar "SearchIcon" an. Starte ich die Applikation, fehlt allerdings das Icon.

Fall 2 (so wie oben gezeigt):
Das Icon wird direkt an die Source des Images gebunden (relativer Pfad). Der Designer zeigt das Icon an, aber auch beim Start der Applikation fehlt dieses. Diese Implementierung bevorzuge ich nicht, sondern war eher als "Proof of concept" gedacht.

Wenn ich in meiner App.xaml das ResourceDictionary aus WPF.Controls einbinde, bekomme ich den Fehler, dass das ResourceDictionary bereits eingebunden wurde, egal ob ich Fall 1 oder 2 verwende.

Nun meine Frage:
Wie realisiere ich es, dass ich in der Library und in der App jeweils auf die gleiche ResourceDictionary zugreifen kann? Viele Bibliotheken wie Metro bspw verwenden intern ja die selben Styles, die der Benutzer später in seiner App.xaml angibt.

24.08.2020 - 17:18 Uhr

Mit ScalePrecision habe ich es nicht hinbekommen. Wenn ich es richtig verstehe, geht das aber auch schlecht. Allerdings konnte ich dort mein Regex einfügen und die Validierung funktioniert. Natürlich zuerst in ein string umwandeln.

Bei der Microsoft Annotation Lösung habe ich meinen double gegen ein string getauscht und es hat dennoch nicht funktioniert.

24.08.2020 - 16:54 Uhr

Stimmt, stimmt. Vieles wird über Nuget Pakete importiert. Trotzdem wollte ich es mal mit einer Microsoft Lösung versuchen. Beim Loggen benutze ich auch immer Serilog oder NLog, wobei es eine Microsoft Lösung gibt. Aber ja, ich gucke mir gerade FluentValidation an, das wird ja hoch gelobt.

Die Erklärung mit dem Double vs String macht Sinn. Dachte der validiert auch gegen Doubles, aber das erklärt, warum es nicht funktioniert.

24.08.2020 - 15:22 Uhr

Eigentlich ist es eine .NET 4.7 Konsolenapplikation. ValidationContext habe ich als "Built-In" Klasse gefunden (kommt ja von System.ComponentModel.DataAnnotations). Mich wundert es nur, dass die Built-In Validierung nicht wirklich zu funktionieren scheint. Andere Annotations habe ich noch nicht ausprobiert.

Die Annotation gefallen mir besser als alles "fluent" zu schreiben. Nuget Pakete wollte ich eigentlich vermeiden. Wollte mal sehen, was Microsoft so zu bieten hat und wie es "von Haus aus" funktioniert.

24.08.2020 - 15:07 Uhr

Hi,

ich würde gerne ein paar Constraints für meine Domain-Models angeben per Annotation.
Für eine double-Property habe ich die Voraussetzung, die Zahl nach dem Schema "####.##" aufgebaut ist, also maximal 4 Dezimalstellen und 2 Nachkommastellen hat.
Als valide würde ich beispielsweise
255.5
255.55
.55
2.55
2.5
255.
bezeichnen.

Meine Annotation sieht so aus:


[RegularExpression(@"\d{1,4}(\.\d{0,2})?", ErrorMessage = "Invalid property (4/2)")]
public double Property{ get; set; }

Die Methode zum Validieren sieht wie folgt aus (alle Models bekommen per Vererbung die Methode zur Verfügung gestellt, daher ist "this" das aktuelle konkrete Objekt)


public (bool isValid, List<ValidationResult> validationResults) Validate()
{
    var context = new ValidationContext(this, serviceProvider: null, items: null);
    var validationResults = new List<ValidationResult>();

    bool isValid = Validator.TryValidateObject(this, context, validationResults, true);
    return (isValid, validationResults);
}

Wenn ich nun Validate aufrufe, bekomme ich immer false.


var obj = new MyObject(); //erbt die Methode Validate()
obj.Property = 255.55;
var val = obj.Validate();
Console.WriteLine(val.isValid); // = false

Wieso?

Wenn ich das ganze manuell mache mit Regex, dann funktioniert es einwandfrei, hier mal ein paar Beispiele:


var regexp3 = @"\d{1,4}(\.\d{0,2})?";
            Regex re3 = new Regex(regexp3);
            var m7 = re3.IsMatch("-120");
            var m8 = re3.IsMatch("200.55");
            var m9 = re3.IsMatch("200.5");
            var m10 = re3.IsMatch("200.");
            var m11 = re3.IsMatch(".33");
            var m12 = re3.IsMatch("100");

10.08.2020 - 16:02 Uhr

Aso, danke für die Erklärung.

Die Erweiterung gucke ich mir mal an.

10.08.2020 - 14:29 Uhr

Okay, verstanden.
Bedeutet das also, dass ich ohne ein Exchange-Konto keine richtige Synchronisation hinbekomme?

Dass die Mail niemals in Outlook ankam wird vermutlich daran liegen, dass Du nach dem Abrufen der Mails diese vom Server löscht (über IMAP, teil der Settings).

Das hieße ja, ich kann mir die Arbeit sparen, andere Clients als Outlook zu testen, richtig?

10.08.2020 - 13:46 Uhr

Hi,

Outlook ist auf meinem PC (Win10) und meinem Handy (Android) installiert. Das Problem ist, dass ich nicht alle E-Mails auf dem Handy als auch auf dem PC anschauen kann.
Beispielsweise habe ich heute zwei E-Mails bekommen, die ich direkt auf dem Handy gelesen habe. Antworten wollte ich über den PC, aber da kam die E-Mail niemals an.

Die verwendete E-Mail Adresse ist von web.de und die Kontoeinstellungen beider Outlook-Accounts läuft mit IMAP (Web.de-Server: imap.web.de mit Port 993).

Hat einer von euch eine Idee, woran es liegen könnte, dass die E-Mails nicht synchronisiert werden?

22.07.2020 - 14:24 Uhr

Hi,

im Wesentlichen geht es darum, dass ich in meinem Projekt ein Datenseeding auf Methodenebene anstrebe. Aktuell verwende ich XUnit zum Testen meiner Services (MVC Service/Repo, die EF Core's DbContext verwenden) und das funktioniert auch ganz gut. Ein klassischer Aufbau eines solchen Tests



public class SpecialServiceTests: BaseServiceTests
{

    public ServiceTests()
    {
         _serviceUnderTest = Resolve<SpecialService>();
    }

    [Fact]
    public async Task CreateMultipleObjects_Retrieve_ShouldNotBeNull()
    {
          //omitted for brevity
    }

    [Fact]
    public async Task DeleteSingleObject_Retrieve_ShouldBeNull()
    {
          //omitted for brevity
    }
}


Jede Testklasse erbt von BaseServiceTest, in dessen Konstruktur die Erstellung des DbContexts und das Datenseeding stattfindet, d.h. jede Methode hat ihre eigene DbContext-Instanz (InMemory) und sind somit voneinander isoliert.
Nun ist es so, dass ich nicht für jede Methode das Seeding benötige, bspw. für die Methode "CreateMultipleObjects". Dies will ich ändern, und zwar möglichst elegant.

Nach langer Überlegung komme ich zu folgendem Schluss: Ich kann nicht explizit sagen, dass für eine bestimmte Methode das Seeding gemacht werden soll. Daher müsste ich generell hingehen und sagen, dass das Seeding überhaupt nicht stattfindet im Konstruktur, sondern am Anfang einer jeden Methode, die das Seeding benötigt.
Also z.B.



    [Fact]
    public async Task CreateMultipleObjects_Retrieve_ShouldNotBeNull()
    {
          //does not need seeding, do your stuff
          Enumerable.Range(1, 10).ForEach(x => ....);
    }

    [Fact]
    public async Task DeleteSingleObject_Retrieve_ShouldBeNull()
    {
          //needs seeding
          SeedData();
          
          //Now do your stuff
    }


Diese Lösung gefällt mir nicht, vor allem weil es bedeutend mehr Testfälle gibt, in denen ich das Seeding brauche. Meine liebste Lösung wäre, einfach mithilfe eines Attributs an einer Methode zu sagen, dass es kein Seeding braucht.

Dafür bräuchte ich aber mehr Kontrolle über das Test-Framework. Soweit ich weiß, weiß XUnit im Konstruktur der Testklasse noch gar nicht, welche Methode ausgeführt wird und eine solche Flexibilität wie ich sie gerne hätte, habe ich auf der XUnit Seite nicht finden können.

Kann mir jemand weiterhelfen? Ich wäre auch bereit, auf ein anderes Test-Framework umzusteigen.

Hinweis:
Das Seeding findet klassisch in C# statt. Später will ich noch die Möglichkeit, das Seeding per ".json" oder ".xml" anzugeben, damit man nicht jedes Mal den Code anpassen muss, wenn sich etwas ändert.

Edit: So etwas in der Art wäre toll


    [Fact]
    [Seed(typeof(MySpecialDbContext), typeof(MySpecialEntity), typeof(MyDependentEntity))]
    //oder einfach [Seed]
    public async Task DeleteSingleObject_Retrieve_ShouldBeNull()
    {
          //Seeding happens by attribute
          
          //Now do your stuff
    }


Damit könnte ich angeben, welcher DbContext mit welchen Entities geseedet werden soll. Dafür bräuchte ich logischen Code, der im Attribut selbst ausgeführt wird. Oder ich gebe einfach ein Flag mit, ob geseedet wird oder nicht.

23.06.2020 - 18:11 Uhr

Die Architektur ist etwas das Problem. Aber dann weiß ich ja, wo ich anfangen muss.

Danke für eure Hilfe. Ohne euch hätte ich wahrscheinlich noch ewig probiert, die zwei DbSets irgendwie zu verbinden.

23.06.2020 - 09:38 Uhr

Der DbContext wird mittlerweile zu groß. Viele Bereiche benötigen jedoch nur immer nur einen Teil des DbContexts, daher die Überlegung mit dem Aufteilen.

22.06.2020 - 20:26 Uhr

Puh, schade.
Ich hatte die Idee, meine Anwendung in kleine Module zu unterteilen und pro Modul sollte es ein DbContext geben. Natürlich gibt es da hin und wieder Überschneidungen, 100%ige Bounded Context sind leider nicht möglich.

Ich probiere mal, inwieweit ich den DbInterceptor ausreizen kann. Meine App unterstützt Dependency Injection, vielleicht kann ich einen eigenen DbInterceptor schreiben, der auf die besonderen Entitäten bzw. Properties achtet und aus dem richtigen Kontext liest und füllt.

22.06.2020 - 16:16 Uhr

[Verwendet wird Sqlite In-Memory, es soll aber für SqlServer, Sqlite und InMemory funktionieren]

Hi,

ich stehe gerade vor dem Problem, zwei Db-Kontexte zu verbinden. Genauer es geht darum, dass es in einem DbContext "PersonDbContext" ein DbSet<Person> gibt, dessen Klasse "Person" Properties besitzt, die in dem anderen DbContext "DocumentDbContext" als DbSet definiert sind.
Angenommen eine Person kann sich eine Medienlizenz auf einer Plattform kaufen. Die Person ist in PersonDbContext definiert und die "MediaLicense" in "DocumentDbContext". Ziel ist es, eine Person inklusive einer "MediaLicense" zu laden, wobei das "MediaLicense-Objekt" als Property an der Person hängt. Also:


var person = personDbContext.Persons.Include(x => x.License).FirstOrDefault();
Assert.True(person.License!=null);

Jedoch bekomme ich einen Fehler "FOREIGN KEY constraint failed" wenn ich der Person eine LicenseId hinzufüge..

So seede ich die Daten: zuerst SeedDocuments, dann SeedPersons (DocumentService benutzt DocumentDbContext, PersonService benutzt PersonDbContext):


 private static void SeedPersons(PersonService persService, DocumentService docService)
        {
            var persons = persService.GetPersons();
            if (!persons.Any())
            {
                var license = docService.GetMediaLicenses().FirstOrDefault();
                var docs = docService.GetDocuments();
                persService.Insert(new PersonModule.Entites.Person()
                {
                    Id = Guid.NewGuid(),
                    FirstName = "A",
                    Lastname = "B",
                    //LicenseId = license.Id
                });
            }
        }

        private static void SeedDocuments(DocumentService docService)
        {
            var licenses = docService.GetMediaLicenses();
            if (!licenses.Any())
            {

                docService.Insert(new DocumentModule.Entities.MediaLicense()
                {
                    Id = Guid.NewGuid(),
                    Code = "Code1"
                });

                docService.Insert(new DocumentModule.Entities.MediaLicense()
                {
                    Id = Guid.NewGuid(),
                    Code = "Code2"
                });

                docService.Insert(new DocumentModule.Entities.MediaLicense()
                {
                    Id = Guid.NewGuid(),
                    Code = "Code3"
                });
            }
        }

Wenn ich die auskommentierte Zeile "//LicenseId = license.Id" einfüge, dann kommt die Fehlermeldung. Wenn ich statt einer Id direkt das Objekt setze, also statt "LicenseId = license.Id" setze ich "License = license", dann wird ein Eintrag hinzugefügt und das Speichern & Laden funktioniert. Allerdings gibt es Situationen, in denen nicht das ganze Objekt vorhanden ist, sondern nur die Id, weshalb es gut wäre, wenn es auch funktionieren würde, wenn ich nur die LicenseId setze. Hat jemand eine Idee, wie ich zum Ziel komme? Muss ich einen eigenen DbInterceptor schreiben oder den DbContext nur richtig konfigurieren? Wenn ja, hat jemand eine Idee Ahnung wie?

Klassendefinitionen:

Klasse Person:


 public class Person
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string Lastname { get; set; }
        public ICollection<Document> Documents { get; set; }

        public Guid? LicenseId { get; set; }
        public MediaLicense License { get; set; }

        public Adress Adress { get; set; }
    }

PersonDbContext


public class PersonDbContext : DbContext
    {

        public DbSet<Person> Persons { get; set; }
        public DbSet<Adress> Adresses { get; set; }

        public PersonDbContext(DbContextOptions options) : base(options)
        {

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Person>().ToTable("Persons");
            modelBuilder.Entity<Adress>().ToTable("Adresses");


        }


    }

Klasse MediaLicense


public class MediaLicense
    {
        public Guid Id { get; set; }

        public string Code { get; set; }

    }

Klasse DocumentDbContext


public class DocumentDbContext : DbContext
    {
        public DbSet<Document> Documents { get; set; }
        public DbSet<MediaLicense> Licenses { get; set; }
        public DocumentDbContext(DbContextOptions options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Document>().ToTable("Documents");
        }
    }

17.04.2020 - 19:49 Uhr

Hi,

ich refactore etwas und brauche etwas Hilfe.
Viele meiner Views sind nach dem gleichen Schema aufgebaut: Header, Content1, Content2. Dabei sind in jeder View die Styles definiert sowie das Grid und alles was dazu gehört.
Da die Applikation viel MVVM benutzt, habe ich mir überlegt, ein gemeinsames ViewModel zu erstellen und ein Control, welches das Layout-Schema liefern soll. Die ViewModels erben dann vom Layout-VM und die Inhalte kann ich ja dann per Binding im ContentPresenter füllen.
In der View kann ich das Layout dann so benutzen (DataContext ist ein VM, welches von ArctionSearchResultPanelViewModel erbt):


    <local:ActionSearchResultPanel DataContext="{Binding }"/>

Jetzt habe ich aber festgestellt, dass es sich für einfache Inhalte in Content1/Content2 nicht lohnt, eine neue View und VM zu erstellen, weil sie lediglich nur einen string binden oder dergleichen.

Praktisch wäre so was:


  <local:ActionSearchResultPanel DataContext="{Binding }">
        <local:ActionSearchResultPanel.Content1>
            <TextBox Text="{Binding MyName}"></TextBox>
        </local:ActionSearchResultPanel.Content1>
        <local:ActionSearchResultPanel.Content2>
            <ListView ItemsSource="{Binding MyList}"></ListView>
        </local:ActionSearchResultPanel.Content2>
    </local:ActionSearchResultPanel>

Irgendwo habe ich die Syntax schon mal gesehen, aber ich weiß nicht wie sie genannt wird und komme daher auf kein Ergebnis beim Suchen. Die Properties, die ich bei der suche gesehen hatte, sahen nicht nach dem aus. Kann mir jemand weiterhelfen?

Zusätzliche Infos:

Die XAML für ActionSearchResultPanel sieht so aus:


 <Grid>
        <Grid.ColumnDefinitions>

        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        
        <StackPanel Orientation="Horizontal">
            <ItemsControl ItemsSource="{Binding ActionCmds}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"></StackPanel>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </StackPanel>
        <ContentPresenter x:Name="SearchViewContent" Grid.Row="1" Content="{Binding SearchView}"/>
        <ContentPresenter x:Name="ResultViewContent" Grid.Row="2" Content="{Binding ResultView}"/>
    </Grid>

Das ViewModel sieht so aus:


private ObservableCollection<ButtonCommandViewModel> actionCmds = new ObservableCollection<ButtonCommandViewModel>();
        private BaseViewModel resultView;

        private BaseViewModel searchView;

        public ObservableCollection<ButtonCommandViewModel> ActionCmds
        {
            get { return actionCmds; }
            set { SetField(ref actionCmds, value); }
        }
        public BaseViewModel ResultView
        {
            get { return resultView; }
            set { SetField(ref resultView, value); }
        }

        public BaseViewModel SearchView
        {
            get { return searchView; }
            set { SetField(ref searchView, value); }
        }

25.03.2020 - 16:37 Uhr

Wie würdest du dir denn so was vorstellen?

21.03.2020 - 18:37 Uhr

Das halte ich für eine ganz schlimme Idee; und widerspricht im weiteresten Sinne auch dem Sinn von Namespaces.

Gebe ich dir eigentlich recht. In gewissen Situationen kann das aber ganz hilfreich sein, bspw in WPF Projekten, wenn man Views und ViewModels Namespaces in seine XAML importieren will. Meistens nutze ich da 2-3 Namespaces jeweils für die Views und ViewModels, sodass ich möglichst wenig Namespaces in der XAML deklarieren muss.

.. was nur in kleinen Teams (theoretisch) funktioniert und in großen Teams mindestens 3 mal am Tag zu Merge-Konflikten führt.

Stimmt. Aktuell sind wir ein sehr kleines Team und haben sehr viele Solutions.

Hatte mal überlegt, eine Extension mit Roslyn zu schreiben, aber kam bislang nicht dazu.

21.03.2020 - 12:56 Uhr

Auf Viasfora bin ich auch schon gestoßen, sieht ganz interessant aus, aber da es ja aktuell buggy ist, wohl keine Option für mich..

Schade, dass OzCode die IDE sehr stark verlangsamt. Das habe ich allerdings auch schon befürchtet aufgrund der ganzen "Berechnungen" zur Laufzeit und die IDE ist auch so stellenweise beim Debuggen mal lahm..

Was ich mir wünschen würde:*Für gewisse Ordner eine Regel einführen (bspw. alle Klassen im Ordner "ViewModel" sollen von "ViewModelBase" erben) *Zusätzliche Debugging-Infos ohne Hover wie bspw in IntelliJ *Solutionübergreifende Refactorings (Namensänderungen, Namespaceänderungen - 'n kleiner Traum) *Template-Favoriten im Solution Explorer Kontextmenü (was soll angezeigt werden und was nicht?)

13.03.2020 - 16:25 Uhr

Hi,

mich würde gerne mal interessieren, welche Visual Studio Extensions (nicht VS Code) ihr so verwendet, um eure Produktivät zu steigern. Sei es, um Aufgaben oder einzelne Schritte zu automatisieren oder um die IDE um Funktionen zu erweitern.
Natürlich habe ich auch schon gegoogelt und durch den Marketplace gestöbert, aber ich denke es gibt auch Perlen unter den "nicht so bekannten" Extensions, die man nicht so im Auge hat.

Mich würden hauptsächlich Extensions interessieren, die sich nicht auf Web - oder Cloudsachen beziehen, sondern generell für Visual Studio, C# Code, Desktopanwendungen (WPF, WinForms) oder Web Services/WCF gedacht sind.

Meine Favoriten aktuell sind:*CodeMaid *Add New File *C# Methods Code Snippets

Aktuell wäge ich noch zwischen folgenden ab:*CodeRusher *OzCode

07.03.2020 - 19:58 Uhr

Hi,

ich habe einige Klassen und Interfaces (siehe unten), die ich gerne dem DI Container von .NET Core (IServiceCollection) hinzufügen will. Als Erweiterung habe ich Scrutor installiert, damit ich einfacher die benötigten Typen registrieren kann.
Ziel ist es, neben einigen Services auch generische Presenter zur Collection hinzuzufügen, auch welche, die außerhalb der ausführenden Assembly liegen.
Soweit funktioniert auch alles, aber bei einem Konstrukt habe ich das Problem, dass sie anscheinend nicht der Collection hinzugefügt werden. Beim Auflösen bekomme ich eine Fehlermeldung:

"System.ArgumentException: Open generic service type 'Winforms.MVP.UI.IPresenter`2[TView,TModel]' requires registering an open generic implementation type. (Parameter 'descriptors')"

Die Interfaces und Klassen sehen folgendermaßen aus:


public interface IPresenter<TView, TModel> where TView : IView
        where TModel : BaseViewModel
    {
        TView View { get; set; }

        TModel Model { get; set; }

        IDialogService DialogService { get; }

        INavigationService NavigationService { get; }
        IWizardService WizardService { get; }

        IMediator Mediator { get; }


    }



 public abstract class BasePresenter<V, M> : BasePresenter where V : IView
                                    where M : BaseViewModel
    {


        public new V View { get => (V)_view; set => _view = value; }

        public new M Model { get => (M)_model; set => _model = value; }


        public BasePresenter(V view, M model, INavigationService navigationService,
            IDialogService dialogService, IWizardService wizardService, IMediator mediator, IServiceCollection serviceCollection)
            : base(view, model, navigationService, dialogService, wizardService, mediator, serviceCollection)
        {


        }
    }

    public abstract class BasePresenter : IPresenter<IView, BaseViewModel>
    {
        protected IView _view;
        protected BaseViewModel _model;

        public INavigationService NavigationService { get; }
        public IDialogService DialogService { get; }
        public IWizardService WizardService { get; }
        public IMediator Mediator { get; }
        public IServiceCollection ServiceCollection { get; }

        public BasePresenter(IView view, BaseViewModel model, INavigationService navigationService, IDialogService dialogService, 
            IWizardService wizardService, IMediator mediator, IServiceCollection serviceCollection)
        {
            View = view;
            Model = model;
            NavigationService = navigationService;
            DialogService = dialogService;
            WizardService = wizardService;
            Mediator = mediator;
            ServiceCollection = serviceCollection;
        }
}


So registriere ich alle Presenter:


Container.Scan(scan =>
              scan.FromApplicationDependencies()
               .AddClasses(@class => @class.AssignableToAny(Config.PresenterTypes))
               .AsImplementedInterfaces()
               .WithSingletonLifetime());

            //Oder
            Container.AddTransient(typeof(IPresenter<,>), typeof(BasePresenter<,>));


Config.PresenterTypes:


        public List<Type> PresenterTypes { get; } = new List<Type>() { typeof(BasePresenter<IView,BaseViewModel>) };


So versuche ich sie aufzulösen:


  var presenter = container.Get<IPresenter<TestView, TestModel>>();
            var presente2r = container.Get<BasePresenter<TestView, TestModel>>();
            var presente2r3 = container.Get<BasePresenter<IView, BaseViewModel>>();
            var testPresenter = container.Get<TestPresenter>();
            var presenters = container.Get(typeof(IPresenter<,>));
            Assert.IsTrue(presenter != null || testPresenter != null || presenters != null || presente2r != null || presente2r3 != null);



Keine der Auflösungen funktioniert, da, wie ich vermute, kein Presenter gefunden und hinzugefügt wird. Hat jemand einen Typ, wie ich alle Klassen dem Container hinzufügen kann, die entweder von IPresenter oder BasePresenter implementiert bzw. erbt?
Auflösen würde ich dann einen bestimmten Presenter, indem ich den konkreten Implementierungstyp angebe, bspw.

DIContainer.Get<BillpaymentPresenter>();

21.01.2020 - 21:23 Uhr

Stimmt, so geht's. Hatte vorher kein MergedDictionaries benutzt sondern einfach ein ResourceDictionary und auf der gleichen Ebene die DataTemplates geladen. Da kam dann auch die Fehlermeldung "The property Resources can only be set once.".

Mein erster Versuch war dieser hier:


 <Application.Resources>
        <ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyle.xaml"/>
        <DataTemplate DataType="{x:Type vms:MainWindowViewModel}"></DataTemplate>
        <!-- weitere DataTemplates -->
</Application.Resources>

Das meinte ich quasi mit

Man kann kein ResourceDictionary laden und gleichzeitig noch DataTemplates innerhalb des Resources-Tag deklarieren.

Mir war schlichtweg nicht klar und es hat etwas gedauert um zu begreifen, dass "Application.Resources" neben "ResourceDictionary" keine weiteren Subelemente haben darf, wenn man sich dazu entscheidet, eben ein ResourceDictionary zu verwenden. Man kann aber, wie du schon gezeigt hast, beides kombinieren, wenn man ein MergedDictionary verwendet.

17.01.2020 - 18:25 Uhr

Ne, das ist es leider auch nicht. Hab auch den Namen per C&P hinter "StaticResource" eingefügt. Hab aber aus "Closeable" mal "Closable" gemacht, der Genauigkeit wegen.

Aber ich hab es nun... in der App.xaml hatte ich all meine DataTemplates (die Styles in der ButtonStyle.xaml) und hatte das ResourceDictionary per Code aber erst im MainWindowViewModel geladen. Daher konnte das xaml tatsächlich nicht gefunden werden..
Da ich kein AppViewModel haben will, probierte ich nochmal die XAML-Variante aus und die Lösung ist denkbar einfach: Man kann kein ResourceDictionary laden und gleichzeitig noch DataTemplates innerhalb des Resources-Tag deklarieren. Also sind die DataTemplates jetzt ebenfalls in die XAML gewandert und zwar ganz nach unten, so dass nun auch die per Key definierten Styles zur Verfügung stehen. In der App.xaml lade ich die Styles dann so:


<Application.Resources>
        <ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyle.xaml"/>
        
    </Application.Resources>

17.01.2020 - 12:56 Uhr

Aktuell lade ich das Resource Dictionary so rein (Code-Behind in MainWindow, nach InitalizeComponent):


var s= new Uri("pack://application:,,,/Styles/ButtonStyle.xaml", UriKind.RelativeOrAbsolute);
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = s});

Weil folgende Lösungen nicht funktioniert haben (innerhalb App.xaml):


<ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Styles/ButtonStyle.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

oder aber auch auch


<ResourceDictionary x:Key="T" Source="Styles/ButtonStyle.xaml">

Es gab dazu keine Fehlermeldung und nichts, aber die Styles wurden eben nicht geladen.

Mein Problem ist aber nun nicht, dass das Resource Dictionary nicht geladen wird. Das Problem ist eher, dass der Key (CloseableBtn) aus dem Resource Dictionary nicht gefunden wird. Und da müsste es doch eigentlich egal sein, ob ich das Resource Dictionary nun per XAML oder Code-behind lade, oder nicht?

17.01.2020 - 00:08 Uhr

Ups, die Anführungszeichen habe ich beim Abtippen vergessen. Und ich hatte das falsche Template gepostet..
Das mit dem SolidColorBrush war eine gute Idee, jetzt habe ich wieder IntelliSense wenn es um die StaticResource Auswahl geht.
Was allerdings noch nicht so recht funktioniert ist das Einbinden des Styles per x:Key.

So sieht der aktuelle Style des Buttons aus:


<Style x:Key="CloseableBtn" TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="BorderThickness" Value="0"></Setter>
            <Setter Property="Background" Value="Transparent"></Setter>
            <Setter Property="BorderBrush" Value="Transparent"></Setter>
            <Setter Property="Padding" Value="0,0,3,0"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Selected}" Value="True">
                    <Setter Property="Background" Value="Transparent"></Setter>
                </DataTrigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Transparent"></Setter>
                    <Setter Property="Foreground" Value="LightGoldenrodYellow"></Setter>
                    <Setter Property="BorderThickness" Value="1"></Setter>
                </Trigger>
                <DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource AncestorType=TabItem}}" Value="True">
                    <Setter Property="Visibility" Value="Visible"></Setter>
                    <Setter Property="Foreground" Value="DarkOrange"></Setter>
                    <Setter Property="BorderBrush" Value="DarkOrange"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>

Das Resource-Dictionary lade ich per Code-Behind, da es mit XAML irgendwie nicht laden wollte..
Und so verwende ich dann den Key in meinem Template:


  <DataTemplate DataType="{x:Type vms:TabItemViewModel}" >
            <Grid MinWidth="75">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBlock VerticalAlignment="Center" Text="{Binding Header}"/>
                <Button Style="{StaticResource CloseableBtn}" Content="X" Grid.Column="1" Focusable="False"
                        FontFamily="Courier" FontSize="9" FontWeight="Bold"  Margin="0,1,0,0" Padding="0" 
                         Width="16" Height="16" 
                            Command="{Binding CloseCmd}"/>
            </Grid>
        </DataTemplate>

Die Fehlermeldung lautet:

Exception: Die Ressource mit dem Namen "CloseableBtn" kann nicht gefunden werden. Bei Ressourcennamen wird die Groß- und Kleinschreibung berücksichtigt.

Was mich ebenfalls etwas stört, ist, dass die Styles nicht mehr im Designer angwendet werden. Somit sehen alle Controls gleich aus.. wenn ich aber den Style auskommentiere, dann sind in der laufenden Anwendung die Styles angewandt worden. Entferne ich dann während der Laufzeit die Kommentare im .xaml, dann werden auch die Styles mit dem x:Key angezeigt. Beende ich die App und starte sie neu, bekomme ich oben genannte Fehlermeldung.

Zu beachten ist dabei auch, dass ich NET Core 3 verwende.. könnte sich wohl auch um einen Bug handeln, oder? Müsste ich mal mit dem Framework gegen testen, oder fällt euch dazu was ein?

16.01.2020 - 22:17 Uhr

Hi,

ist es möglich, dass ich eine statische Resource, z.B. Color, als Wert für einen Setter innerhalb eines Styles verwende? Ich habe es bereits auf dem "herkömmlichen" Weg versucht, aber ohne Erfolg. Im Editor konnte ich das Ergebnis kurz sehen und es sah gut aus. Nach ein paar Sekunden bekomme ich eine Fehlermeldung, dass die statische Ressource eine Exception ausgelöst hat, weil sie nicht gefunden werden kann.
So verwende ich den Style quasi in der Resource Dictionary:


<Color x:Key="TxtDarkColor">#BEBEBE</Color>


    <Style x:Key="Test" TargetType="Window">
        <Setter Property="Background" Value="{TxtDarkColor}"></Setter>
    </Style>


Und so binde ich sie dann ein:


<Button Style={StaticResource Test}/>

Wie macht ihr so was?

21.11.2019 - 17:30 Uhr

Hi,

ich räume etwas meinen alten Code auf und habe gesehen, dass viele Views mehr oder weniger viel duplizierten Code enthalten.
Nun dachte ich mir, dass ich eine BasisKlasse erstelle:


public class BaseUC : UserControl
{

   protected DateDropDown dateDropDown;

   protected string date;

}


public partial class FirstUC: BaseUC
{
     FirstUC(){ InitializeComponents(); }

}

public partial class FirstUC { //Designer Implementation }

Effektiv habe ich die private Deklaration aus der Designer-Datei entfernt. Die aktuelle Situation führt zu folgendem Fehler, wenn ich die Designer-Datei von FirstUC öffne:
The variable 'dateDropDown' is either undeclared or was never assigned.

Das Control 'dateDropDown' wird vom Designer der Klasse FirstUC erstellt und entsprechend konfiguriert (habe ich vor dem Refactoring in der Designer-Klasse gemacht);
Wieso erkennt der Designer nicht die Variable? Kann ich mein Vorhaben überhaupt umsetzen?

10.11.2019 - 10:50 Uhr

Habe es gelöst. Letzten Endes war es nur eine Kleinigkeit: Ich habe jedes mal eine neue Liste erstellt in der Init Methode. Jetzt prüfe ich, ob die Liste null ist oder nicht. Demnach erstelle ich eine Liste oder füge die Elemente der bestehenden Liste hinzu.
Nun funktioniert alles wie erwartet und ich brauche auch keine Singletons mehr für die View, VM, etc.

09.11.2019 - 23:27 Uhr

Ja, bzw. erbt BindableBas (von Prism, welches INotifyPropertyChanged implementiert).
Hab eben den ContentPresenter mal aufgeklappt und sehe, dass die Werte von der ProjectCardViewModel tatsächlich leer sind. Es scheint also einen Fehler beim Übertragen des Datenkontextes zu geben.
Ich gehe davon aus, dass die Zeile im XAML
"<viewscontrols:ProjectCardView DataContext="{Binding }" Padding="15"></viewscontrols:ProjectCardView>"
der ausschlaggebende Punkt ist.

Wenn es euch hilft, kann ich das Projekt auch gerne per .zip bereitstellen.

Edit: Ok, das Problem liegt doch daran, dass mir Prism jedes mal eine neue Instanz des ViewModels zurückgibt, wenn ich container.Resolve<T> aufrufe. Mal sehen, ob ich dies umgehen kann.
Denn das ist natürlich etwas doof: Zum Navigieren benutze ich den DI Container, um meine View zu bekommen. Dadurch wird dann aber ein ViewModel erzeugt. Die UI/View scheint aber an das alte ViewModel gebunden zu sein.

Edit2: Nein, daran liegt es nicht. Habe das ProjectOverviewModel nun als Singleton dem Container hinzugefügt (ich weiß, sollte man nicht machen): Selbes Verhalten.
Mein XAML für die ProjectOverviewView sieht nun so aus:

 <UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:ProjectCardViewModel}">
            <viewscontrols:ProjectCardView DataContext="{Binding }" Padding="15"></viewscontrols:ProjectCardView>
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Label>Projects</Label>
        <Label Grid.Row="1" Content="{Binding }"></Label>
        <Label Grid.Row="2" Content="{Binding ProjectCards.Count, UpdateSourceTrigger=PropertyChanged}"></Label>

        <ItemsControl Grid.Row="3" ItemsSource="{Binding ProjectCards}" Margin="20">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>

Wenn ich für die ProjectCardView das DataContext entferne während der Laufzeit und wieder hinzufüge, dann werden die Cards mit Inhalt gefüllt (der Name, etc sind nun da).

09.11.2019 - 16:52 Uhr

Hi,

ich probiere mich mal etwas mit Prism aus und habe jetzt Regionen in WPF eingebaut.
Es gibt verschiedene Ansichten in der gleichen Region: Startansicht, Projektübersicht und Projekt erstellen.
Von der Projektübersicht gelangt man in die Ansicht "Projekt erstellen". Das Problem ist nun folgendes: Wenn ich ein Projekt erstelle (über Speicher-Button), dann sehe ich beim Debuggen im ViewModel der Projektübersicht auch das neue Projekt, die UI zeigt aber nur die alten Projekte an.
Ich dachte es liegt eventuell daran, dass Prism jedes mal ein neues VM erzeugt und die UI noch an das alte VM gebunden ist. Aber es wird nur ein Mal der Konstruktor meines VM aufgerufen.
Zudem habe ich das Problem, dass der Inhalt von der ProjectCardView (in XAMl von projectsOverviewView) leer ist.
Hier mal die relevanten Teile:

XAML: ProjectsOverviewView



        <ItemsControl Grid.Row="3" ItemsSource="{Binding Path=ProjectCards}" Margin="20">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <viewscontrols:ProjectCardView DataContext="{Binding }"  Padding="15"></viewscontrols:ProjectCardView>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>


 public class ProjectsOverviewViewModel : ViewModelBase<List<Project>>, IToolbarProvider, INavigationAware
    {
        private ObservableCollection<ProjectCardViewModel> _projectCards;



        private IRegionService _regionService;

        public ProjectsOverviewViewModel(IRegionService regionService)
        {
            _regionService = regionService;
            ProjectCards = new ObservableCollection<ProjectCardViewModel>();

            Init();
        }

        public ObservableCollection<ProjectCardViewModel> ProjectCards
        {
            get => _projectCards;
            set => SetProperty(ref _projectCards, value);
        }
        public ObservableCollection<ToolbarItemViewModel> ToolbarItems { get; private set; }
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        private void Init()
        {

            InitCards();
        }

        private void InitCards()
        {
            ClientInfo.Workspace.Projects.ForEach(proj =>
            {
                ProjectCards.Add(new ProjectCardViewModel(_regionService)
                {
                    Name = proj.Name,
                    CreatedAt = proj.Created,
                    Modified = proj.Modified,
                    Model = proj
                });
            });
        }



        private void CreateNewProject()
        {
            _regionService.ChangeView<CreateProjectView>(RegionNames.MainContentRegion);
        }
    }


 public class CreateProjectViewModel : ViewModelBase<Project>
    {


        private void CreateProject()
        {
            Project newProject = new Project()
            {
                ID = Guid.NewGuid(),
                Created = DateTime.Now,
                Modified = DateTime.Now,
                Name = Name,
                Description = Description,
                Path = ClientInfo.ProjectsPath
            };
            try
            {
                var filename = Path.Combine(ClientInfo.ProjectsPath, newProject.Name + ".proj");
                newProject.Filename = filename;

                newProject.Workspace = ClientInfo.Workspace;
                ClientInfo.Workspace.Projects.Add(newProject);
                //Update model
                Serialization.SerializeDataContract(newProject, filename);
                Model = newProject;

                _regionService.ChangeView<ProjectsOverviewView>(RegionNames.MainContentRegion);

            }
            catch (Exception e)
            {

            }
        }
    }

Die Methode ChangeView<T>(string regionName) macht nichts besonderes:


 public void ChangeView<T>(string regionName, NavigationParameters parameters)
        {

            if (string.IsNullOrEmpty(regionName))
            {
                Trace.WriteLine("Cannot change view!");
                return;
            }
            var view = Provider.Resolve<T>();
            Debug.Assert(view.GetType().Name.EndsWith("View"));
            Navigate(regionName, typeof(T).Name, view, parameters);
        }

        public void Navigate(string regionName, string source, object view = null, NavigationParameters parameters = null)
        {
            if (parameters == null)
                RegionManager.RequestNavigate(regionName, source);
            else
                RegionManager.RequestNavigate(regionName, source, parameters);

            Trace.WriteLine($"Navigated {regionName} - {source}");

            if (view != null)
                OnChangedView?.Invoke(this, new ChangeViewEventArgs(regionName, view));
        }

Mit dem Tool Snoop sehe ich auch, dass die Daten eigentlich da sind... woran könnte es liegen?
Habe auch mal ein Bild hochgeladen mit den Snoop-Werten und der App. Diese Kacheln sind die ProjectCardsViews(Models).

21.10.2019 - 17:54 Uhr

Super. Danke für die schnellen Antworten.
Auf die Exceptions-Settings verzichte ich mal, einfach damit die Handhabung für jeden Entwickler gleich ist, ohne dass jeder die selbe Einstellung vornehmen muss.
Daher entscheide ich mich für Th69's Lösung.

Merci!

21.10.2019 - 17:17 Uhr

Hi,

eine Anforderung ist, dass die Applikation nur ein Mal gestartet werden kann bzw. dass es maximal eine laufende Applikation gibt. Dazu verwende ich Mutex.OpenExisting(string name).
Beim Debuggen fliegt aber immer eine Exception, da ja noch keine Applikation des Namens vohanden ist. Jetzt wollte ich mal wissen, ob es eine Möglichkeit gibt, das zu umgehen?
Die Methode ist innerhalb eines Try-Catch Blocks.

Es wäre ja schon schön, wenn die Applikation ohne Exceptions aufstarten würde.

11.10.2019 - 10:06 Uhr

Ok super, das hat diesmal funktioniert. Man muss nur dran denken, die Exceptions immer wieder anzuschalten wenn man sich während dem Debuggen dazu entscheidet, gewisse Exception einfach zu ignorieren, was ja auch Sinn macht.

08.10.2019 - 14:53 Uhr

Hi,

wenn ich debugge, dann sind meistens mit alle CLR Exceptions angehakt in den Einstellungen. Leider musste ich jetzt schon öfter feststellen, dass die Einstellungen immer wieder auf "Default" gesetzt werden, wenn ich VS neustarte.
Auf SO hatte ich mal eine Antwort gefunden, die besagte: Wenn man mehrere Instanzen geöffnet hat, dann werden die Exception Settings derjenigen Instanz übernommen, die als letztes geschlossen wird.
Das hat bei mir leider nicht funktioniert...

Ab und zu vergesse ich den Haken zu setzen und dann kann es schnell kompliziert werden wenn mal etwas nicht funktioniert und man sich fragt wieso.

Kennt jemand eine Möglichkeiten, diesen Haken standardmäßig im angehakten Zustand zu lassen?

23.07.2019 - 17:12 Uhr

Danke, das wars.

23.07.2019 - 14:12 Uhr

Hi,

ich habe eine ListView, die eine fixe Breite hat. Damit alles in einer einzigen Spalte angezeigt wird, habe ich der ListView eine "Dummy-Column" verpasst. So weit, so gut. Jetzt zum Problem:
Einige der ListView Items haben einen relativ langen Text, also soll es möglich sein, horizontal zu scrollen (vertikal natürlich auch). Der Clue dabei ist, dass nur horizontal gescrollt werden soll, wenn es notwendig ist. Heißt: Ist die Breite des Textes kleiner als die Breite der ListView, soll kein horizontaler Scrollbalken angezeigt werden. Andernfalls schon.

Ich habe über Stackoverflow herausgefunden, dass man bei der Spalte einen Wert von -2 für die Breite eintragen soll, damit sich die Breite an dem Inhalt orientiert.
Allerdings erscheint mit dieser Lösung auch ein horizontaler Balken, wenn eigentlich keiner notwendig wäre.

Hat jemand schon mal so was gemacht und kann weiterhelfen?

15.03.2019 - 19:02 Uhr

Hi,

ich habe drei Xamarin Projekte: ein Shared Projekt, ein Android und ein iOS Projekt.
Das Shared Projekt bestizt die ganzen Assets und setzt die Applikation auf. Das Android und iOS Projekt erweitern oder ersetzen jeweils plattformspezifische Funktionen.

Aktuell linke ich die Dateien aus dem Shared in das jeweilige andere Projekt, indem ich die Ordner physikalisch erstelle und die Dateien über VS17 als hinzufüge.

Das manuelle Hinzufügen der ganzen Assets ist anstrengend und fehleranfällig. Gerade wenn man mal einen Ordner im Shared Project verschiebt, weil es thematisch nicht mehr passt oder ähnlich, muss das Verlinken zwei Mal wiederholt werden (ein mal für Android, ein mal für iOS).

Kennt jemand eine Möglichkeit, die Dateien jeweils automatisch beim Bauen auf Änderungen zu prüfen?
Lösche ich beispielsweise die Datei "1b.txt" aus dem Shared Project, sollen die Links zu dieser Datei in Android und iOS entfernt werden. Andersrum sollen Links erstellt werden (also beim Hinzufügen einer Datei).

16.02.2019 - 15:54 Uhr

Wenn Du aktuell die Idee hast, dass Du jede Technologie erlernen und vertiefen willst.. dann viel Glück dabei 😃
Wärst der Erste, der das packt 😃

Keineswegs 😉

Super einfach via Google zu finden 😉

Fast alles ist via Google zu finden. Dennoch als mal interessant, was andere Leute in Foren so denken 😉

15.02.2019 - 18:01 Uhr

Lustig. Das was ihr schreibt habe ich heute auch so gehört 😄

Über die aktuelle Marktlage habe ich bis weil nur wenig erfahren können, danke für den kurzen Einblick über die moderne Bewegung.
Was WPF anbelangt, stimme ich dir voll zu. Letztens erst wollte ich mich mit WPF vertraut machen, aber habe dann schnell gemerkt, dass das nicht unbedingt jetzt sein muss. Es gibt viele andere Frameworks, die ich zuerst vertiefen und lernen sollte.

Und da spielen die Solution und Software Architekten solcher Apps eine große Rolle, damit Apps bzw. das Fundament einer App so aufgebaut wird, dass man die verschiedenen Runtimes drunter schieben kann -> Abstraktion.

Vor diesem Problem stand ich, als ich meine eigene IDE schreiben wollte.

Klar hat man Variationspunkte (Beispielsweise muss auf Mobile die Camera anders angesprochen werden als auf dem Desktop / Web)

Das ist ja beispielsweise auch bei Xamarin so. Gibt es eigentlich gegenüber Cordova irgendwelche Vorzüge? Mal angenommen, ich kann nicht nur C#, sondern auch JS/TS.