Laden...
F
fluxy myCSharp.de - Member
Fachinformatiker Anwendungsentwicklung Herdecke Dabei seit 27.03.2009 183 Beiträge
Benutzerbeschreibung

Forenbeiträge von fluxy Ingesamt 183 Beiträge

16.01.2022 - 13:57 Uhr

Ich kenn auch Kunden-Bastellösungen, die das über nen eigenen Middleware-Resolver gemacht haben - alles bisher (von mir) gesehen aber war murks.

Was sind denn die Workarounds? Ich würde die gerne einmal sehen.... Vielleicht kannst du sie ja mal beispielhaft skizzieren. Also wie du schon sagtest, unterstützt das aspnetcore-api-versioning package ja die Versionierung über alle vier gängigen Varianten (Url, Query Parameter, Request Parameter und Media Types). Gleiches gilt für en api-explorer. Zumindest kann man beim api-explorer über den Parameter ApiVersionParameterSource die Quelle der Versionierung (Url, Query Parameter, Request Parameter und Media Types) angeben.

So wie ich das verstanden habe, ist der API Versioning Explorer die Schnittstelle zwischen der API Versionierung und der OpenApi Schnittstelle (OpenAPI/Swagger). Entsprechend funktioniert die Versionierung über meinen Header ja grundsätzlich schon - und es sind auch Implementierungen vorhanden, die diese Variabilität erlauben. Wenn das Vorgehen so nicht unterstützt werden würde, dann macht die Schnittstelle ja so gar keinen Sinn.

Hast Du diesen "Workaround" denn mal gesehen?

Andere Frage: Siehst Du einen Weg, die Version in Swagger UI (es geht um Swagger UI, nicht um die Generierung der Swagger Dokumente) auszublenden bzw. die Version mit der Auswahl des Swagger Dokuments für die Version zu sezten? Das das HTTP Header Feld mit in der API Definition steht, würde ich ja als richtig ansehen 🙂

@ alle anderen: Auch wenn ich jetzt in die "Du" Form wechsle, weitere Meinungen / Anregungen anderer sind ausdrücklich erwünscht

Viele Grüße,
Fluxy

15.01.2022 - 15:21 Uhr

Ich verwende Dein Vorgehen generell nirgends, weil es viele Probleme im Alltag macht. Die Grundidee ist ja, dass Versionen unabhängig voneinander betrieben und weiterentwickelt werden, zB. durch Legacy etc. Alles in eine Applikation zu packen, macht nur in ganz ganz ganz wenigen Mini-Szenarien sinn.
Und selbst in diesen würde man eher pro Version einen eigenen Namespace haben, statt alles über Mappings zu lösen. Ich hab das in freier Wildbahn, ausserhalb von Demos, noch nie gesehen.

Genau das wollte ich eigentlich verhindern. Ich weiss, dass Versionierung über Urls beziehungsweise über verschiedene Endpunkte sehr pupulär ist. Wenn man sich aber mal mit dem Thema beschäftgt und ein paar Tutorials liest, wird man schnell merken, dass jedes Vorgehen seine Nachteile hat - auch die Vesionierung über Url Segmente. Aber wollen wir uns mal wieder auf das Problem fokussieren....

Zum Problem: Implizite Versionierung ist im Framework nur dann möglich, wenn Du keinerlei andere Versionsinformationen setzt. Daher knallts.

Was meinst Du mit impliziter Versionierung?

In Deinem Fall dürfte es aber eh knallen, weil NSwag keine Versionierung über Header oder Query unterstützt. Nur URL Versionierung wird unterstützt.
D.h. Dein Header-Vorgehen mit NSwag so geht eh nicht.

Bist Du Dir da sicher? Es gibt da in der Tat ein Issue zu, siehe hier. Dort steht folgender Lösungshinweis:

If you use header versioning then you probably want to manually add the global header parameter in PostProcess, apart from that the operations should correctly be reported by ASP.NET Core API explorer and generated in the spec...

Maybe even version filtering works - if the operation versions are correctly reported in API explorer groups.

Hat jemand da vielleicht ein Beispiel oder das schonmal gemacht? Mir ist auch nicht klar, was mit "global header parameter" gemeint ist. Vielleicht kann mir da jemand helfen, dass zu verstehen.

Lieben Gruß,
fluxy

15.01.2022 - 11:40 Uhr

Grüsst Euch,

ich habe eine API, die ich gerne Versionieren möchte. Das klappt auch ganz gut, allerdings bekomme ich zwei Probleme. Zuvor möchte ich euch aber mitteilen, dass die Versionierung über den HTTP Header eine bewusste Entscheidung war und das nicht geändert werden soll (ich weiss das es dazu reichlich Diskussionen gibt...).

Dazu hier einmal ein Test Controller:


[Route("api/test")]
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
public class ValueController : Controller
{

    [HttpGet]
    [MapToApiVersion("1.0")]
    public ActionResult GetAllValues()
    {
        return Ok("1.0");
    }

    [HttpGet]
    [MapToApiVersion("1.1")]
    public ActionResult GetAllValues_V11()
    {
        return Ok("1.1");
    }
}

Problem Nummer 1 ist: Wenn ich das [MapToApiVersion] über GetAllValues (also Version 1.0) weglasse, dann kann das Swaggerdokument nicht erzeugt werden und ich bekomme eine Exception mit dem Hinweis, dass es für GET mehrere Actions für die selbe Resource gibt:

Fehlermeldung:
System.InvalidOperationException: The method 'get' on path 'api/test' is registered multiple times.
at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.AddOperationDescriptionsToDocument(OpenApiDocument document, Type controllerType, List1 operations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateApiGroups(OpenApiDocument document, IGrouping2[] apiGroups, OpenApiSchemaResolver schemaResolver)
at NSwag.Generation.AspNetCore.AspNetCoreOpenApiDocumentGenerator.GenerateAsync(ApiDescriptionGroupCollection apiDescriptionGroups)
at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.GenerateDocumentAsync(HttpContext context)
at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.GetDocumentAsync(HttpContext context)
at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.GetDocumentAsync(HttpContext context)
at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext context)
at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Das zweite Problem ist, dass ich zwar eine Auswahlliste für die Version in SwaggerUi bekomme, aber trotzdem das Header-Feld für die Eingabe der Version (try it out) angezeigt wird und dieses sogar required ist. Das macht aus meiner Sicht keinen Sinn, weil die Version ja durch die Auswahl schon bekannt sein sollte. Kann man das Feld in Swagger UI entfernen?

Anbei der wohl releante Startup code:


  public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddApiVersioning(options =>
            {
                options.DefaultApiVersion = new ApiVersion(1, 0);
                options.ReportApiVersions = true;
                options.ApiVersionReader = new HeaderApiVersionReader("service-version");
                options.AssumeDefaultVersionWhenUnspecified = true;
            });
            services.AddVersionedApiExplorer(options =>
            {
                options.ApiVersionParameterSource = new HeaderApiVersionReader("service-version");
            });
            
            services.AddSwaggerDocument(document =>
            {
                document.DocumentName = "1.0";
                document.ApiGroupNames = new[] { "1.0" };
            });

            services.AddSwaggerDocument(document =>
            {
                document.DocumentName = "1.1";
                document.ApiGroupNames = new[] { "1.1" };
            });
        }


 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
        {
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseOpenApi();
            app.UseSwaggerUi3();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

26.02.2020 - 17:55 Uhr

Beispiel: Du versuchst ein Element zu aktualisieren, das es aber nicht gibt.

Damit hast Du Recht. Entschuldige, dass hatte ich nicht geschrieben, aber mir fehlt der Insert auf die Activities. Ich sehe das die Order gefunden wird (das ist der select vor dem add):

Executed DbCommand (207ms) [Parameters=[@__domainEvent_AggregateId_0='1d49e3b2-a121-471f-93d6-fd1445fa250d'], CommandType='Text', CommandTimeout='30']
SELECT [o].[Id], [o].[ConfirmedAt], [o].[CreatedAt], [o].[DeviceId], [o].[FirstDeliveredAt], [o].[LastDeliveredAt], [o].[OrderNumber], [o].[OrderStatus], [o].[OrderText], [o].[PrintedAt]
FROM [Orders] AS [o]
WHERE [o].[Id] = @__domainEvent_AggregateId_0

ConfirmedAt CreatedAt DeviceId FirstDeliveredAt LastDeliveredAt Id OrderNumber OrderStatus OrderText PrintedAt
1 NULL 26.02.2020 09:21:33 21E5A51A-E966-49F4-AD3B-195DB96A2EAB NULL NULL C8740EBF-F935-4377-804A-C949BBABF6E4 0100 1 test NULL

Und dann sehe ich den Update auf die Activities:

Executing DbCommand [Parameters=[@p3='d60a4e49-4b40-40aa-8053-2cd8ed4b9593', @p0='1d49e3b2-a121-471f-93d6-fd1445fa250d' (Nullable = true), @p1='1', @p2='0' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [OrderActivities] SET [OrderId] = @p0, [ResultCode] = @p1, [PrinterStatus] = @p2
WHERE [Id] = @p3;
SELECT @@ROWCOUNT;

Aber ich sehe keinen Insert auf die Activities.

Ich sträube mich auch etwas dagegen ein DbSet dafür anzulegen. Die Activities sind nur im Subbaum der Orders interessant und keine eigenständige Entität. Von daher wäre es mir Recht, wenn es auch ohne ein DbSet gehen würde....

26.02.2020 - 17:10 Uhr

Ich kann mit dme Begriff "Update or ignore" nichts anfangen. Aber wenn du das meinst: Ich hatte damit angefangen, die Activity der Liste in der Order hinzuzufügen. Allerdings macht es keinen (sichtbaren) unterschied, ob ich:

-Die Order in der Activity setze

  • Die Activity in der Liste der Activity des Order-Objekts hinzufüge
  • oder beides mache.

Alle 3 Varianten schmeißen den selben Fehler. Von daher bitte nochmal Feedback, ob ich Dich richtig verstanden habe.

26.02.2020 - 15:21 Uhr

verwendetes Datenbanksystem: SQL Server 2017

Hi,

ich habe eine Anwendung, die Bestellungen hereinbekommt. Zu der Bestellung sollen nun Activities gespeichert werden. Eine Activity ist eine abstrakte Klasse, für die es die Möglichkeiten 'printed', 'accepted' und 'denied' gibt. Wenn ich nun eine Bestellung ändern möchte, bekomme ich eine fiese Exception:

Fehlermeldung:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException : Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See
>
for information on understanding and handling optimistic concurrency exceptions.
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithoutPropagation(Int32 commandIndex, RelationalDataReader reader)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.Consume(RelationalDataReader reader)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConnection connection) at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList1 entries)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList1 entriesToSave) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
at Heliprinter.Orders.ReadModel.Denormalizer.OrderDenormalizer.When(OrderPrinted domainEvent)
at Heliprinter.Orders.WebUI.ReadModel.Tests.Tests.Test1() in D:\Source\FluentSoftware\Heliprinter_neu\Heliprinter.Monitoring\Source\Heliprinter.Orders.WebUI.ReadModel.Tests\UnitTest1.cs:line 30

Der Code dazu ist


var savedOrder = _context.Orders.Where(order => order.Id == domainEvent.AggregateId).ToList().SingleOrDefault();
if (savedOrder != null)
{
       var activity = new OrderPrintedActivity()
       {
              Id = domainEvent.Id,
              PrinterStatus = domainEvent.PrinterStatus.ToString(),
              ResultCode = domainEvent.ResultCode,
              Order = savedOrder
       };

       _context.SaveChanges();
}


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

        // andere Properties ...

        public virtual ICollection<CommunicationActivity> Activities { get; set; }
    }

    public abstract class CommunicationActivity
    {
        public Guid Id { get; set; }

        public virtual OrderEntity Order { get; set; }

        public int ResultCode { get; set; }

    }

Ich habe extra einen Unit-Test erstellt, um auszuschlißen, dass ein Nebenläufigkeitsproblem existiert. Aber scheinbar tritt das Problem auch Single-Threaded auf. Könnt ihr mir da helfen?

VG

11.11.2018 - 19:49 Uhr

Ich sehe in Deinem Szenario nirgends den sinnvollen Einsatz von Headern; Du brauchst ja prinzipiell im Container nirgends den Hostname.
Denn für das Routing sind diese ja nicht relevant. Das Routing ist Aufgabe von NGINX.

Okay, dazu noch eine Frage zu meiner Config. Ich habe ja nur eine Location für /webui/ bedeutet:

http://192.168.7.113/webui/* -> http://containername/*

http://192.168.7.113 -> keine regel vorhanden

Der Request zur Web-App wird ja richtig geroutet. Nur Links im HTML-Dokument werden nicht richtig aufgelöst. Im übertragenen HTML-Code steht beispielsweise


<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css" />

Dieser wird zu http://localhost//lib/bootstrap/dist/css/bootstrap.css was ja falsch ist. Dazu die Fragen
1.Wie funktioniert die auflösung der virtuellen Pfade generell? Woher kommt der Server localhost, ud nwer macht das? 1.Warum wird mein Pfad dann nicht zurück übersetzt, bzw. welche Möglichkeiten habe ich, das zu debuggen?

Ich möchte nicht ausschließen, dass das ganze ein konzeptionelles Problem mit meinem Routing ist. Wie würdet ihr weiter vorgehen?

Viele Grüße

10.11.2018 - 22:43 Uhr

Sicher. Die Container haben damit nichts zu tun. Ich habe sie hier auch nur genannt, damit ihr wisst, woher die hostnamen kommen. Bei der Anwendung bin ich mir nicht ganz so sicher - scheinbar (siehe code beispiele) muss man der Anwendung schon beibringen, bestimmte Header zu akzeptieren.

In dem Beispiel wird nur ein proxy_pass gemacht, das mache ich ja genauso. Also wo ist der Unterschied? Ich habe das Beispiel jetzt nicht ausprobiert (könnte ich auch nicht), aber ich gehe mal davon aus, dass es funktioniert - nur bei mir eben nicht.



        location /games/ {
             proxy_pass http://games:8080/;
        }


Meist Du also, dass der Fehler allein im NGINX-Server zu suchen ist?

Viele Grüße,
Fluxy

10.11.2018 - 15:02 Uhr

Hallo zusammen,

für die Frage werdet ihr mich auslachen, aber ich bekomme es einfach nicht hin und hoffe auf eure Hilfe. Ich frage mich gerade: Wie wird in Asp.net core eigentlich der Hostname aufgelöst? Hintergrund ist folgendes:

Ich habe mehrere Docker container (API's und eine Web UI) und möchte jetzt die container auf konkrete URL's mappen. Der direkte Zugriff über Ports funktioniert. Also:

container1 => localhost:5000 => localhost/webui
container2 => localhost:5001 => localhost/api/service1
container2 => localhost:5002 => localhost/api/service2

Die Web UI selber kann auch aufgelöst werden, aber da über den Reverse Proxy die URL's umgebogen werden, können die restlichen Resourcen (CSS, JS, Medien, etc.) nicht mehr geladen werden. Dazu einmal meine nginx.config:


http
{
	
	server {
		listen       80;
		server_name 192.168.7.113;
    
		location /webui/ {  
			proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
			proxy_set_header Referer $http_referer;

			
			proxy_pass http://container1/;
			
			
		}
    }
}

Ich bitte zu entschuldigen, dass noch kein HTTPS da ist. Die im Servernamen eingetragene IP ist die externe IP meines Rechners in meinem Netzwerk. Ich wollte nicht localhost nehmen, um zu sehen, welche URL genommen wird (auf dem Webserver gibt es ja auch ein anderes localhost)

Wenn ich jetzt einen Request starte auf http://192.168.7.113/webui wird dieser Request folgerichtig umgeleitet auf http://container1. Soweit funktioniert das auch. Allerdings wird von der Webseite (1:1 Visual Studio Template) unter anderem versucht die bootstrap.css zu laden:


<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />

Dieser Aufruf endet in http://localhost/lib/bootstrap/dist/css/bootstrap.css . Auf dem Server zweifelslos richtig, allerdings wäre die richtige Url http://192.168.7.113/webui/lib/bootstrap/dist/css/bootstrap.css, denn auch der Request muss über den Proxy Server gerootet werden.

Wie ihr seht, habe ich bereits bestimmte Header mitgeschickt, die meinen Informationen nach bnötigt werden, damit .net den Hostnamen richtig auflösen kann. Jedoch funktioniert das nicht. Ich habe zwei Middlewares gefunden, die man wohl einbinden soll, damit der Hostname richtig aufgelöst wird, die habe ich eingebunden:


		
			services.Configure<ForwardedHeadersOptions>(options =>
			{
				options.ForwardedHeaders =
					ForwardedHeaders.All;
			});
		

und


		
			app.UseForwardedHeaders(new ForwardedHeadersOptions()
			{
				ForwardedHeaders = ForwardedHeaders.All
			});

Aber irgendwie scheint noch etwas zu fehlen. Ich habe mir auch nie über die Hostauflösung gedanken gemacht. Könnte jemand meine Wissenslücke schließen?

Viele Grüße,
fluxy

04.05.2018 - 14:27 Uhr

Was "per design" bedeutet, weiss ich leider nicht. Wie gesagt, dass ist nicht meine Aussage, sondern die der Quelle.

Normalerweise wird unter VS2017 aktuell nur die override und debug erzeugt.

Also, bei mir wird keine Debug erzeugt sondern die Root (docker-compose.yaml) und die override (docker-compose.override.yaml). Der Container wird dann gebaut mit:

docker-compose -f ".....\docker-compose.yml" -f ".....\docker-compose.override.yml" -f "......\obj\Docker\docker-compose.vs.release.g.yml" -p dockercompose3349660439106525625 --no-ansi config

Die letzte Datei ist die, die generiert wird. In meinem Fall ist das jetzt release, das könnte aber auch debug sein. Laut dem Pluralsight Kurs sollte sich im Docker Projekt noch eine Debug dabei befinden - das hast Du ja auch gesagt.

Oder habe ich Dich falsch verstanden?
Zu dem Multistage: Hast du da Links bzw. Infomaterial?

VG

03.05.2018 - 19:27 Uhr

Hallo Community,

ich habe eine Verständnisfrage und ich hoffe dass ihr mir weiterhelfen könnt. Es geht um Docker, bzw. eigentlich sogar um docker-compose und zwar in Verbindung mit Visual Studio. Ich habe mir auf Pluralsight einige Videos dazu angeschaut und dächte verstanden zu haben, dass es neben der docker-compose.yaml und der docker-compose.override.yaml auch noch die docher-compose.debug.yaml und docker-compose.prod.yaml gibt und die Dateien über docker-compose gemergt werden (sodass ich z.B. das setzen des ASPNETCORE_ENVIRONMENTS in der debug/prod datei machen kann, da die Dateien im Build über docker-compose gemergt werden.

Jetzt habe ich Docker-Support zu meinem aktuellen Projekt hinzugefügt und diese Dateien nicht gefunden. Als ich danach gegoogled habe, habe ich eine Aussage gefunden, dass die Datei für debug/release nun "per design generiert wird". Ich habe leider die quelle nicht mehr.

Meine Frage ist nun: Wenn die Dateien nun generiert werden und somit als Konfigurationsmöglichkeit nicht mehr zur Verfügung stehen, wie kann ich dann dafür sorgen, dass in Abhängigkeit von Debug/Release beim bauen des Images die richtige ASP.NET Core Konfiguration verwendet wird?

Meine Informationen scheinen etwas veraltet zu sein. Ich wäre euch daher dankbar, wenn ihr mich auf den aktuellen Stand bringen könntet.

Viele Grüße

20.06.2017 - 11:24 Uhr

Hallo Liebe Community,

ich habe ein Problem mit JQuery und Typescript und würde gerne wissen, ob ihr das kennt und eine Lösung dafür habt:

Umgebung ist Visual Studio und Typescript 2.4.0, JQuery in der Version 3.2.1, und @types/jquery auch in 3.2.1.

Wenn ich das Projekt dann bauen möchte, kommt es zu diversen Fehlern in der index.d.ts, die sinngemäß folgendes sagen:

Severity Code Description Project File Line Suppression State
Error TS2314 Generic type 'JQueryStatic<TElement, HTMLElement>' requires 2 type argument(s). Scripts (tsconfig project) ..\node_modules@types\jquery\index.d.ts 28 Active

Es sieht für mich aus wie eine inkompatibilität, aber könnt ihr mir genaueres sagen? Hattet ihr das problem schonmal?

vg

01.05.2017 - 19:19 Uhr

Gut, dann sind wir jetzt wieder am Anfang, aber das macht nichts, weil diese Ehrenrunde hatte einige gute Lerneffekte -;)

Als Orientierung hatte ich dieses Tutorial verwendet: Hypermedia und WebApi

Grundsätzlich werden die Links im Controller hinzugefügt. Beispiel:


public Post Get(int id) 
{
    var post = Session.Load<Domain.Post>(id);
    var response = Mapper.Map<Post>(post);
    response.AddLink(new EditLink(Url.Link(...)));
    return response;
}

Die Implementierung geht davon aus, das jede Ressource von einer abstrakten Basis erbt, die die Möglichkeit erweitert, der Resource Links hinzuzufügen.

Der Author geht dann noch einen Weg um Duplizierten Code zu vermeiden, nämlich die Resourcen immer wieder hinzuzufügen. Soweit ich das verstehe ich der Mechanismus folgender:

Es gibt für verschiedene Ressourcen verschiedene "Enricher". Enricher sind dann dafür da, die Ressourcen um Links zu erweitern. Die Enricher werden quasi einfach als Service registriert, aber es gibt noch einen Handler.

Ich habe noch nicht viel mit WebApi gemacht, aber ich finde das Vorgehen gar nicht so schlecht. Ich hoffe ihr könnt mir das bestätigen. Meine Idee war es jetzt, den Handler durch eine Middleware abzulösen. Ist das wirklich ein ungünstiges Vorgehen?

Vielleicht habe ich mich auch anfangs nicht konkret genug ausgedrückt?!

Viele Grüße,
fluxy

01.05.2017 - 10:19 Uhr

Hallo,

Vielen Dank für eure Informationen.

Aber eine Frage: so wie ich euch verstanden habe, Ist sowohl Hypermedia als auch HATEOAS als Aufsatz auf REST zu verstehen. Im Internet als auch in dem Link zu dem Artikel von Martin Fowler verstehe ich es eher so, das es dabei eher um die Herbeiführung von Zustandsänderungen geht und somit beides Teil von REST ist, bzw. Genutzt werden sollte um REST zu implementieren.

Ich habe übrigens noch einen anderen Artikel gefunden, der auch für andere hilfreich sein könnte. Es ist zwar ein Artikel aus der Javawelt, Aber da es sich bei REST um ein Protokoll handelt und nicht um eine Implementierung, sollte das egal sein

hateoas Artikel von jaxcenter

Was mir noch nicht klar ist!! Ist wie man so etwas nun mit webapi Core explizit umsetzt. Habt ihr vielleicht diesbezüglich gute Links?

Vg fluxy

30.04.2017 - 20:49 Uhr

Verstehe. Dann ist das ja nicht Rest, sondern wieder etwas neues?
Wie würde man denn mit so einer Situation umgehen, Wenn man einen REST Service ohne hypermedia schreiben will?

Ich bin mir nicht sicher, ob es das richtige ist, sich in hypermedia einzuarbeiten, Wenn man sich gerade mit den Grundlagen von REST beschäftigt.

Viele Grüße und danke für die bisherigen Anmerkungen
Fluxy

30.04.2017 - 13:16 Uhr

Guten Tag,

ich weiss gar nicht, ob mein Vorgehen richtig ist, aber ich habe gelesen, dass man REST-Services so implementiert, dass nicht nur das Ergebnis der Abfrage übermittelt wird, sondern auch Links, die genutzt werden können, um weiter abzufragen. Dazu habe ich versucht eine kleine Middleware zu schreiben, die es mir erlaubt, die Links hinzuzufügen.

Ich habe also zum Beispiel als Resource Projekte, die ich über einen ProjectsController abfrage:


        [HttpGet]
        public IEnumerable<ProjectWebRepresenation> Get()
        {
            var queryProjects = _queryProjectsBusinessController.QueryProjects();
            if (queryProjects == null)
                return null;

            return queryProjects.Select(project =>new ProjectWebRepresenation { Number = project.Number, Name = project.Name});
        }

Projekte haben neben dem Namen und einer Projektnummer noch andere Daten, zum Beispiel hängen an den Projekten Kategorien. Die Kategorien sollen aber nicht mitgeschickt werden, sondern bei Bedarf abgefragt werden.

Stattdessen soll ein Link in die JSON-Antwort eingefügt werden, über die die abhängigen Objekte nachgeladen werden können. Ich habe dafür einen Artikel gefunden, allerdings ist der nicht für Core.

Anreichern von REST-Nachrichten über Links

Den Handler habe ich versucht, in eine Middleware umzuwandeln. Das Erzeugen der Middleware funktioniert ganz gut, allerdings bekomme ich ein Problem weil die Middleware ein HttpContext erwartet und kein HttpResponseMessage.

Vielleicht bin ich auch auf einem ganz falschen Weg. Es wäre schön, von euch zu hören.

Mfg fluxy

29.03.2017 - 17:40 Uhr

Guten Abend,

ich finde mein Problem ganz witzig, aber das hatte bestimmt schonmal jemand. Ich nutze schon das neue Studio (2017), weiss aber nicht ob das damit etwas zu tun hat.

Ich habe einfach einen Unittest geschrieben, der einen Webapi Controller testet. Das Problem ist, er kann die Assembly nicht laden:

Fehlermeldung:
Expected: <System.ArgumentNullException>
But was: <System.BadImageFormatException: Could not load file or assembly 'MyProject.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was made to load a program with an incorrect format.
File name: 'MyProject.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'

Das Testprojekt hat die Referenz, der Pfad ist:

D:...\MyProject.Web\bin\Debug\net452\Timecheck.Web.exe

Das interessante ist das net452, das fügt Visual Studio dem Buildpath hinzu. Die Exe landet auch wirklich da, jedoch sucht .NET sie da nicht:

Fehlermeldung:
LOG: Attempting download of new URL file:///D:/.../bin/Debug/MyProject.Web.DLL.
LOG: Attempting download of new URL file:///D:/../bin/Debug/MyProject.Web/MyProject.Web.DLL.
LOG: Attempting download of new URL file:///D:/../bin/Debug/MyProject.Web.EXE.
ERR: Failed to complete setup of assembly (hr = 0x8007000b). Probing terminated.

Die ersten beiden versuche schlagen eh fehl, und der dritte kann nicht funktionieren weil er von dem net452 nichts weiss. Dummerweise restored VisualStudio die Projectsettings, wenn man versucht den Pfad wegzusetzen.

Any Suggestions?

VG, Sebastian

09.02.2017 - 11:41 Uhr

Sorry für Doppelpost!

Ich habe mir die aktuelle Version von .NET Core installiert und nun läuft es!

Danke, kann geschlossen werden!

09.02.2017 - 11:40 Uhr

Ah Hallo Apt, schön von Dir zu hören!
Tut mir leid, du hast schon recht ich habe ein paar Informationen vergessen....

Also ich nutze das englische Visual Studio 2015 mit Update 3 => en_visual_studio_enterprise_2015_with_update_3_x86_x64_dvd_8923288.iso

Das ganze Läuft auf Windows 10 Professional und einem x64-basierten, also 64-Bit System (die Information könnte interessant sein auch wenn Du das nicht gefragt hast).

Die IIS-Version müsste die von Windows 10 mitgelieferte sein (10.0.14393.0)

.NET core version bin ich mir nicht sicher, aber das ist die version die ich mit dotnet --version rausbekomme oder? Dann ist es die 1.0.0-preview2-003131 (gibt es schon ein release?)

Ausser das von Visual Studio mitgelieferte habe ich kein besonderes SDK installiert. Gleiches gilt für die CLI.

08.02.2017 - 16:05 Uhr

Hallo zusammen,

ich hoffe ihr könnt mir helfen. Es geht wahrscheinlich um ein lokales Problem bei mir. Und zwar habe ich meinen PC neu aufgesetzt, und habe Probleme Webapplikationen aus Visual Sutdio heraus zu starten.

Das Witzige ist: "Normale" ASP.NET MVC Webanwendungen laufen - ASP.NET Core Webawendungen nicht. Den Vorschlag das .vs verzeichnis zu löschen und den Port zu ändern habe ich schon durch, das bringt bei mir nicht so viel Erfolg.

Der IIS Express läuft auch im systray.

Kann mir jemand dazu etwas sagen?

VG, fluxy

02.04.2016 - 12:59 Uhr

Nagut, dann muss ich halt noch etwas konkreter werden. Ehrlich gesagt finde ich das Beispiel etwas ungünstig gewählt (wurde ja auch schon gesagt), das Problem ist das dies was wir da sehen ja ein Daten-Viewmodel ist, und nicht etwa ein Viewmodel, welches direkt an eine View gebunden werden könnte.

Ich werde einfach mal eine Frage stellen und die Antwort offen lassen (auch weil das gerade ein Punkt ist an dem ich selber scheitere, aber vielleicht habt ihr ja eine Lösung).

Bleiben wir doch bei dem Person beispiel. Das Beispiel alleine reicht nur nicht, weil DDD ohne eine Fachdomäne ist irgendwie quatsch. Okay sagen wir, wir entwerfen eine Software für einen kleinen Handwerksbetrieb. Dieser Handwerksbetrieb möchte Löhne abrechnen. In einem ersten Schritt soll eine Software entwickelt werden, in der die Unternehmensstruktur abgebildet werden kann, und jedem Mitarbeiter ein Monatsgehalt, und die anzahl der Wochenstunden zugeordnet werden kann.

Wir setzen uns nun mit der Fachdomäne zusammen und eiigen uns auf auf folgendes kleines Modell, ihr findet es als Anhang. Es ist ganz einfach: Jeder Mitarbeiter hat einen Vertrag (contract), und jeder Mitarbeiter ist Chef von 0..* weiteren mitarbeitern (auf die bildung von Teams wurde erstmal verzichtet). Gut also implementieren wir das Fachmodell, jede Person bekommt also die Möglichkeit, Personen als Mitarbeiter zu seiner Mitarbeiterliste hinzuzufügen, und für seine Mitarbeiter das Gehalt sowie die Wochenarbeitsstunden zu erfassen. Nun gut, soweit so schön.

Jetzt sind wir zurück in der eigenen Firma und man möchte das Projekt wirklich nach DDD umsetzen, also kein Anemic Data model. Der Kunde möchte eine schöne UI, also beschliesst man eine schöne WPF Anwendung mit dem MVVM Pattern umzusetzen. Anforderung ist also

  • Wir wollen kein Anemic Data Model, Fachdaten und Fachlogik werden also nicht getrennt
  • Wir wollen eine UI mit WPF aufbauen
  • Und es soll das MVVM-Pattern eingesetzt werden.

Okay der erste Schluss ist, unsere Viewmodels kennen unser Model nicht. Das Laden von Daten ist relativ einfach. Wir wollen diese Logik nicht im Viewmodel haben, also schreiben wir uns einen ApplicationController. Nach Eric Evans ist das eine Schicht, die keine Logik ausführt, sondern nur Delegiert. Also delegieren wir an ein Repository, das läd für uns alle Personen. Das Viewmodel bekommt Personen-Objekte als Viewmodel (die allerdings keine Cohesion zu einem Person-Objekt haben). Alle Daten die wir erfassen setzen nur Properties auf dem Viewmodel.

Der User kann bekommt sich selbst als Person auswählen und bekommt ein Grid mit Personen angezeigt, welche seine Mitarbeiter sind. Es gibt einen Button um Mitarbeiter zum Grid hinzuzufügen, dafür nutzen wir ein Command. Während des Hinzufügens soll aber nicht nur ein neues Viewmodel erzeugt werden, sondern die Person auf Fachlogikebene auch hinzugefügt und gespeichert werden.

Wie machen wir das nun?
Wie kommen wir an das Personenobjekt, ohne (z.B. über eine Factory uns ein neues zu erstellen)?
Macht ihr doch mal weiter, aber bitte verschmutzt den Thread hier nicht. Ich habe mir soooo viel Mühe gegeben -;)

Vielleicht bekommen wir ja doch noch eine Lösung hin...

vg fluxy

01.04.2016 - 22:37 Uhr

Also ich beschäftige mich auch seit einigen Tagen mit dem Thema DDD und habe den Thread hier gerade mal verfolgt. Übrigens ich finde das Thema sehr interessant, aber ich glaube hier geht einiges durcheinander.

Ich muss gestehen, ich sehe bei mir noch viele Fragezeichen, aber das Hauptproblem (auch hier im Thread) ist doch, das so ein "Amenic Data Model", also reine DTO's als Model und die Logik steckt sonstwo (aber woanders) zumindest im Term von DDD ein Anti-Pattern ist.

Die Frage ist doch vielmehr wie man MVVM und DDD gemeinsam nutzen kann. In DDD ist das Model halt der Kern der Software, aber ich glaube zum Thema Validierungen gibt es doch zwei unterschiedliche Arten der Validierung.

Das eine ist, die Validierung innerhalb des Fachmodells. Innerhalb des Fachmodels müssen Validierungen erlaubt sein. Das ist ja auch gerade das Konzept der Factories und der Aggregates. Die Factories sollen ja gerade Zustände erzeugen, die "gültig" sind, das heisst, das erzeugte Objekt erfüllt alle Invarianten und ist somit "valid". Natürlich sollten auch alle Operationen ein Objekt nicht in einen invaliden Zustand überführen, ich denke das erklärt sich von selber.

Die andere Sache ist die UI - und die hat meiner Meinung nach mit dem Fachmodell gar nix zu tun. Bei dem was wir in WPF typischerweise unter "Validierung" verstehen, ist die Überprüfung von User-Eingaben, ob diese richtig oder falsch sind. Das hat meiner Meinung nach nichts mit dem Fachmodell zu tun, weil die UI nix mit dem Fachmodell zu tun hat. Es geht lediglich darum sicherzustellen, dass die Eingaben, die getätigt werden, sinnvoll sind, damit in der Folge dann über Commands oder ähnliches Operationen auf dem Fachmodel (z.B. über einen ApplicationController) mit validen Daten ausgeführt können.

Mit anderen Worten: Das eine ist UI, das andere ist Fachlichkeit.

Ich weiss der Thread ist schon einige Zeit zum erliegen gekommen, aber vielleicht gibt es ja doch noch einige, die das Thema weiterdiskutieren wollen, und vielleicht helft ihr mir mit euren Antworten auch noch ein bisschen, tiefer in das Thema einzusteigen.

vg

01.12.2015 - 21:30 Uhr

Moin,

ja gut es ist eine Logging-Bibliothek. Wahrscheinlich gut es da ja auch nicht mehr so viel zu tun. Aber ich frag mal so:

Alternative?!

Grundsätzlich muss ich aber sagen, ich war mit Log4Net immer zufrieden - sobald die Konfiguration lief gab es keine Probleme. Das Einrichten ist nervig, aber wenns einmal läuft, dann lief es immer. Aber zurück zu meinem Problem, ich denke das entscheidene ist dies hier (passt auch zu dem guten Hinweis):

A simple call to LogManager.GetLogger will cause the attributes on the calling assembly to be read and processed. Therefore it is imperative to make a logging call as early as possible during the application start-up, and certainly before any external assemblies have been loaded and invoked.

Heisst, LogManager.GetLogger interpretiert das Attribute. Ich habe einige Logs eingebaut (ausserhalb von NHibernate), das funktioniert soweit. Ich habe allerdings noch ein anderes Problem, was vermutlich mit meiner Config zu tun haben wird. Dazu muss ich sagen ich nutze die folgende Kombination:

Visual Studio 2015 + Resharper 10 + NUnit 10

Und zwar ist das Problem, dass irgendwie die Ausgaben teilweile auf dem falschen Output landen (zumindest gefühlt). Und zwar habe ich 2 Appender, console und rolling file. Ich habe den Root Logger und 2 weitere Logger für NHibernate). Nun landen meine Logs (die ich selbst aufrufe im Logfile und im Testoutput) und die NHibernate Logs in der Konsole, aber nicht im Logfile und auch nicht im Testoutput). Könnt ihr mal über die Config schauen ob ich etwas total doofes gemacht habe?


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- Others sections -->
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>

  <!-- This section contains the log4net configuration settings -->
  <log4net debug="false">

    <!-- Define some output appenders -->
    <appender name="trace"
          type="log4net.Appender.TraceAppender, log4net">
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern"
             value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n" />
      </layout>
    </appender>

    <appender name="console"
          type="log4net.Appender.ConsoleAppender, log4net">
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern"
             value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n" />
      </layout>
    </appender>

    <appender name="rollingFile"
          type="log4net.Appender.RollingFileAppender,log4net" >

      <param name="File" value="nhibernate.log" />
      <param name="AppendToFile" value="true" />
      <param name="RollingStyle" value="Date" />
      <param name="DatePattern" value="yyyy.MM.dd" />
      <param name="StaticLogFileName" value="true" />

      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern"
          value="%d [%t] %-5p %c - %m%n" />
      </layout>
    </appender>

    <!-- Setup the root category, add the appenders and set the default priority -->
    <root>
      <priority value="ALL" />
      <appender-ref ref="console" />
      <appender-ref ref="rollingFile" />
    </root>
    
    <logger name="NHibernate">
      <level value="ALL" />
      <appender-ref ref="console" />
      <appender-ref ref="rollingFile" />
    </logger>

    <logger name="NHibernate.SQL">
      <level value="ALL" />
      <appender-ref ref="console" />
      <appender-ref ref="rollingFile" />
    </logger>
  </log4net>
</configuration>

vg

29.11.2015 - 11:01 Uhr

verwendetes Datenbanksystem: sql server 2014

Guten Morgen,

ich hatte gestern das folgende Problem: Ich habe für eine Applikation einen Integratiostest geschrieben, und zwar geht es um eine Art Work-Tracking-System für den eigenen Gebrauch, und in dem System werden halt Arbeitsaufgaben verwaltet. Diese Arbeitsaufgaben haben einen Status, der Lazy geladen wird. Der Test legt per Hand so eine Arbeitsaufgabe in der Datenbank an, und prüft dann ob der richtige Status geladen wird. Das Abholen der Arbeitsaufgaben (ist nur eine) läuft in einer Session (#1), das prüfen auf den richtigen Status (also dann auch das Nachladen des Status läuft in einer neuen Session (#2).

Es wird ziemlich schnell klar, dass ich die Objekte aus Session1 detachen, und in Session 2 attachen muss. Die tue ich auch und zwar für das Item und den Status (wobei der Status eigentlich nicht in Session1 sein kann da er nicht geladen wurde aber sicher ist sicher)


 public void Detach(WorkingItem item)
        {
            _session.Evict(item);
            _session.Evict(item.Status);
        }

Das selbe mache ich dann beim Attachen in der zweiten Session


        public void Attach(WorkingItem item)
        {
            _session.Lock(item, LockMode.None);
            _session.Lock(item.Status, LockMode.None);
        }

Kleine Randnotiz: Das sieht jetzt so aus als wäre es die selbe Session, aber das stimmt nicht. Der Code befindet sich in einem Repository, und das wurde von einer UnitOfWork erstellt, und zwar von einer anderen:



            using (var unitOfWork = _unitOfWorkFactory.CreateUnitOfWork())
            {
                var repository = unitOfWork.CreateWorkingItemRepository();
                workingItems = repository.GetAll();
                workingItems.ForEach(workingItem => repository.Detach(workingItem));
            }

            using (var checkUnitOfWork = _unitOfWorkFactory.CreateUnitOfWork())
            {
                var repository = checkUnitOfWork.CreateWorkingItemRepository();
          
                var actualFirstWirkingItem = workingItems.First();
                repository.Attach(actualFirstWirkingItem);
                Assert.That(repository.RemoveProxy(actualFirstWirkingItem), Is.InstanceOf<InProgressWorkingStatus>());
            }

Was noch wichtig ist: Der Test läuft in einer Transaktion, damit er keinen Datenmüll hinterlässt. Der Rollback wird nach dem Test durchgeführt. Da die Transaktion noch offen ist, ist die connection auch noch offen, die für session1 verwendet wurde. Allerdings bekomme ich nun folgende Exception:

Fehlermeldung:

NHibernate.LazyInitializationException : Initializing[Timecheck.DomainObjects.Enums.WorkingStatus.WorkingStatus#f81ddde2-569a-4f91-a116-b7dd6d10c003]-Illegally attempted to associate a proxy with two open Sessions

bei NHibernate.Proxy.AbstractLazyInitializer.set_Session(ISessionImplementor value)
bei NHibernate.Engine.StatefulPersistenceContext.ReassociateProxy(ILazyInitializer li, INHibernateProxy proxy)
bei NHibernate.Engine.StatefulPersistenceContext.ReassociateIfUninitializedProxy(Object value)
bei NHibernate.Event.Default.ProxyVisitor.ProcessEntity(Object value, EntityType entityType)
bei NHibernate.Event.Default.AbstractVisitor.ProcessEntityPropertyValues(Object[] values, IType[] types)
bei NHibernate.Event.Default.AbstractReassociateEventListener.Reassociate(AbstractEvent event, Object entity, Object id, IEntityPersister persister)
bei NHibernate.Event.Default.DefaultLockEventListener.OnLock(LockEvent event)
bei NHibernate.Impl.SessionImpl.FireLock(LockEvent lockEvent)
bei NHibernate.Impl.SessionImpl.Lock(Object obj, LockMode lockMode)
bei Timecheck.Persistence.NHibernate.Repositories.WorkingItemRepository.Attach(WorkingItem item) in D:\Fluent-Software\VSGit\Timecheck\Implementation\Timecheck.Persistence.NHibernate\Repositories\WorkingItemRepository.cs:Zeile 49.
bei Timecheck.Persistence.NHibernate.Tests.WorkingItemRepositoryTests.LoadWorkingItems() in D:\Fluent-Software\VSGit\Timecheck\Implementation\Timecheck.Persistence.NHibernate.Tests\WorkingItemRepositoryTests.cs:Zeile 83.

Das ich 2 Sessions aufhabe, ist mir klar, aber die Entities sollten aus Session 1 detached worden sein. Ich habe jetzt etwas länger kein NHibernate mehr gemacht, dachte eigentlich das Attach funktioniert über SaveOrUpdateCopy, aber das scheint nicht mehr so zu sein, weil die Methode ist nicht mehr da...

vg
fluxy

29.11.2015 - 10:41 Uhr

Moin Community,

ich habe das Problem eigentlich schon lange, aber ich habe jetzt mal beschlossen der Sache auf den Grund zu gehen. Es geht um log4net und die Konfiguration dieser. Ich habe in einem Projekt jetzt wieder log4net eingebunden, um nhibernate zu debuggen. Wie dem auch sei, ich habe gestern log4net über NuGet bezogen (Aktuelle Version ist 2.0.4) und losgelegt.

Als Referenz nehme ich diesen Artikel: Log4Net und NHibernate

Das ist zwar ein Hibernate-Artikel, aber eigentlich ist das auch egal, das Pattern sollte ziemlich identisch sein und dem was ihr vielleicht von log4net aus anderen Quellen kennt. Die Config habe ich übernommen so wie sie dort ist und sicherheitshaltber nicht nur im Testprojekt sondern auch im NHibernate-Projekt in Form einer app.config bereitgestellt.

Jetzt ist die Erwartungshaltung, dass nur eine Attributierung durchgeführt wird, und dadurch log4net gestartet wird. Ich habe erst, wie immer beschrieben, das Attribut über einen zufällig ausgewählten Namespace in der Testassembly und NHibernate Assembly getestet, nachdem das nicht funktioniert hat, habe ich das Attribut in die AssemblyInfo.cs verschoben (google hat mir da einen Artikel zu Tage befördert). Allerdings hilf dies alles nix.


[assembly: log4net.Config.XmlConfigurator(Watch = true)]

Auf jedenfall wird kein Logfile erzeugt. Ich hatte das Problem wie gesagt schon in der Vergangenheit öfters, die Lösung war dann den XmlConfigurator von Hand irgendwo (z.B. im TestInitialize) zu erzeugen. Dann funktionierte das logging auch (das habe ich jetzt allerdings nicht ausprobiert). Letztendlich würde das bedeuten, dass das Attribut nicht ausgewertet wird, und deshalb der Log4Net Initialisierungsprozess nicht gestartet wird.

Aber kann mir jemand sagen warum? Ich würde das gerne verstehen.

Viele Grüße,
fluxy

28.10.2015 - 17:49 Uhr

Das ist ja genau das Problem, das würde ich gerne testen. Also ich gehe lokal nicht über Azure. Ich gehe lokal auf einen lokalen SQL Server, und beim Deployment wird der ConnectionString ausgetauscht. Allerdings habe ich Propleme zu checken, ob der Connectionstring auch in der Webconfig in meiner Azure-Webapplikation landet. Ich habe schon viel gesehen, aber da es beim Update schema keine Exceptions gibt, gehe ich mal davon aus, dass er zumindest irgendeine Datenbank hat. Nur welche das ist, das würde ich gerne prüfen. In der Konfiguration der Webapplikation im Azure Portal steht zwar ein ConnectionString, dass ist aber definitiv nicht der selbe, wie der, der angeblich deployed wird....

vg

26.10.2015 - 20:38 Uhr

Hallo zusammen,

ich bin auf der Suche nach Infomaterial zu einem Problem, das ich mit Azure habe. Habe eine kleine Webapplikation dort veröffentlicht und was soll ich sagen ich bekomme das ganze nicht zum laufen. Der Fehler ist, dass NHibernate die Tabellen nicht anlegt - das Problem ist aber das ich grundlegende Dinge nicht nachschauen kann.

Wie kann ich zum Beispiel in die Web.config schauen, und zu sehen, ob mein Connectionstring richtig konfiguriert ist? Hat mein User überhaupt die Rechte Tabellen auf der Datenbank anzulegen?

Habt ihr vielleicht infomaterial über das ich mich zu diesem Thema einlesen kann? Eventuell hat auch jemand direkt einen Rat für mich. Bin für jeden link dankbar.

vg fluxy

15.08.2015 - 11:18 Uhr

Guten Tag,

eigentlich dachte ich total simple, aber ich bekomm es irgendwie nicht hin: Ich möchte das beim TFS bei der Erstellung einer neuen US als Defaulttext der Text "In my role as a <role> I want to <decription> in order to do <task>" stehen haben. Das ich dafür dem Description feld eine COPY field rule hinzufügen muss ist sicher, aber die Platzhalter bereiten Probleme (BTW: Das Description field ist ein HTML control).
*Wenn ich den Text als value so in die XML-Datei schreibe, dann meckert witadmin, dass man das zeichen "<" verwendet hat *Maskiert man <, > mit &lt; und &gt; dann funktioniert der import zwar, jedoch wird nichts angezeigt. Er macht dann für die Platzhalter eigene HTML-Tags die kein Brwoser auswerten kann, also auch schwachfug... *Und wenn ich den gesamten Text als CDATA Tag schreibe, kommt er damit gar nicht zurecht

Es gibt doch bestimmt jemanden, der sowas schon hat oder? Wäre cool wenn hier eine Lösung zustande kommen könnte....

VG, fluxy

26.07.2014 - 10:51 Uhr

Hi,

erstmal dachte ich das wird ein ganz einfacher Test. Einfach den RegionManager mocken und testen ob für jede Region eine View registiert wird, alsp RegisterViewWithRegion aufgerufen wird. Mit MOQ eigentlich ganz einfach dachte ich, ist ja nur ein Verify. Okay dann finde ich aber raus das die Methode RegisterViewWithRegion gar nicht im Interface IRegionManager definiert ist, sondern eine ExtentionMethod auf das Interface ist (was nen schwachsinn...)

Es gibt zwar auch ein anderes Interface IRegionViewRegistry, auf welches die Methode auch wirklich aufgerufen wird, und welches über den ServiceLocator abgerufen wird, aber da wäre die Frage: Wie registriere ich mein Mock in dem ServiceLocator von Prism?

Kann mir jemand helfen? Es hat doch bestimmt schonmal jemand so einen Test geschrieben, oder?!

Viele Grüsse,
Fluxy

24.05.2014 - 23:39 Uhr

-;)

Deine Welle der Begeisterung was das ASP.NET Prinzip der Authentifizierung angeht, schwingt förmlich mit^^ . Aber nen eigener Custom Manager ist glaub ich ein bisschen aufwendig. Man bekommt den user manager aber aus dem Request. Also gelöst ^^

24.05.2014 - 16:25 Uhr

Hallo zusammen,

ich baue mir gerade eine Webapplikation mit dem ASP.NET MVC5 Framework und nutze die bereits integrierte OWIN-Authentifizierung (ASP.NET Identity heisst das glaube ich). Allerdings habe ich die bei der Registrierung noch 2 weitere Informationen gespeichert (einen Namen und das aktuelle Datum).

Per Default wird ja einfach der Loginname (standardmäßig ist das die Email-Adresse) ausgegeben, wenn man eingeloggt ist. Das würde ich gerne ändern, sodass eine andere Information ausgegeben wird. Das Problem ist: Bis auf den Namen steht eigentlich nix im Identity-Objekt, welches in der View für die ausgabe verwendet wird:

User.Identity.GetUserName() 

Es scheint aber noch die Möglichkeit zu geben, über den UserManager auf die Informationen der Registrierung zuzugreifen. Allerdings wie komme ich von der View aus an den UserManager?

VG,

10.05.2014 - 14:25 Uhr

verwendetes Datenbanksystem: MSSQL2012

Hallo,

ich bin verwirrt. Wollte gerade EF in meine Applikation einbauen und bekomme die folgende Fehlermeldung beim ersten Select auf die Datenbank:

Fehlermeldung:
An unhandled exception of type 'System.Data.Entity.Core.ProviderIncompatibleException' occurred in EntityFramework.dll

Additional information: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct.

Inner Exception : > Fehlermeldung:

Methode nicht gefunden: "System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher System.Data.Entity.Infrastructure.Interception.DbDispatchers.get_Connection()".

Kann mir einer sagen was dort los ist? Ich verwende eine eigene DbConfiguration und setze diese mit DBConfiguration.SetConfiguration.

Hier noch das Stacktrace, falls es weiterhilft:

Fehlermeldung:
A first chance exception of type 'System.Data.Entity.Core.ProviderIncompatibleException' occurred in EntityFramework.dll
'WMCalendar2014.vshost.exe' (CLR v4.0.30319: WMCalendar2014.vshost.exe): Loaded 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Transactions.resources\v4.0_4.0.0.0_de_b77a5c561934e089\System.Transactions.resources.dll'. Module was built without symbols.
System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>
>
</TraceIdentifier><Description>Unbehandelte Ausnahme</Description><AppDomain>WMCalendar2014.vshost.exe</AppDomain><Exception><ExceptionType>System.Data.Entity.Core.ProviderIncompatibleException, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct.</Message><StackTrace> bei System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection)
bei System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.&amp;lt;&amp;gt;c__DisplayClass1.&amp;lt;ResolveManifestToken&amp;gt;b__0(Tuple3 k) bei System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory) bei System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.ResolveManifestToken(DbConnection connection) bei System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(DbConnection connection, DbProviderManifest&amp;amp;amp; providerManifest) bei System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection) bei System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext) bei System.Data.Entity.Internal.RetryLazy2.GetValue(TInput input)
bei System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
bei System.Data.Entity.Internal.InternalContext.Initialize()
bei System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
bei System.Data.Entity.Internal.Linq.InternalSet1.Initialize() bei System.Data.Entity.Internal.Linq.InternalSet1.GetEnumerator()
bei System.Data.Entity.Infrastructure.DbQuery1.System.Collections.Generic.IEnumerable&amp;amp;lt;TResult&amp;amp;gt;.GetEnumerator() bei System.Collections.Generic.List1..ctor(IEnumerable1 collection) bei System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)
bei VMCaldenar2014.ViewModels.MainViewModel.LoadTeams() in d:\Dev\Projects\WMCalendar2014\VMCaldenar2014.ViewModels\MainViewModel.cs:Zeile 31.

Kann mir jemand sagen was da los ist?
VG, Fluxy

21.03.2014 - 19:02 Uhr

Naja, habe ich doch eigentlich schon geschrieben.
Ich habe einen DbConfigurationService, welcher die DBConfiguration setzt:

z.b. für den MssqlServerDatabaseConfigurationService


      public void SetDatabaseConfiguration()
        {
            System.Data.Entity.DbConfiguration.SetConfiguration(DbConfiguration.GetDbConfiguration);
        }


Der wird in der Realen Applikation von einem PrismModul benutzt, oder im Test direkt.
Also ich rufe einfach die Methode SetConfiguration der Klasse DbConfiguration auf.

Benutzt wird das ganze wiefolgt:


using (var unitOfWork = _unitOfWorkFactory.CreateUnitOfWork())
            {
                var documentRepository = unitOfWork.DocumentRepository;
                foreach (var document in documentRepository.GetAll())
                    FoundDocuments.Add(new DocumentViewModel(document));
            }

Die UnitOfWork Factory sowie die passende DBConfiguration wird vom Prism module in Unity registriert und von der Factory aufgelöst. Mit der Factory und der DbConfig wird dann ein neuer Context erzeugt und in die Uow injiziert.


 public IUnitOfWork CreateUnitOfWork()
        {
            IFluxyDocContext context = new FluxyDocContext(_dbconfigurationService);
            return new EntityFrameworkOfWork(context);
        }

VG

21.03.2014 - 15:19 Uhr

Abt,

Du verstehst mich nicht. Oder ich kann mich nicht mitteilen.

Natürlich, müssen meine Tests autark sein, sind sie auch. Sie sprechen auch nur eine DB an, alles andere wäre Quark. Die Reihenfolge, in der die Tests ausgeführt werden ist dann egal.

Normalerweise sieht ein Test ja so aus:

1.) Vorbereitungen
2.) Act
3.) Assert
4.) Vorbereitungen wieder aufräumen

Wenn ich also einen Test habe, der Daten aus einer DB liest sähe das so aus:

1.) Daten schreiben
2.) Act = Daten lesen
3.) Assert: Sind die Daten die ich gelesen habe, die selbe, wie diejenigen, die ich geschrieben habe
4.) geschriebene Daten löschen

Bezogen auf die DbConfiguration wäre das:

1.) DBConfiguration setzen
2.) Act, was auch immer
3.) Assert, was auch immer
4.) DBConfiguration aufräumen (wegsetzen, unregistrieren, löschen, was auch immer)

Ich habe aber keine Möglichkeit gefunden, Schritt 4 zu realisieren. Das sorgt dafür das der 2. Test (welcher das ist bestimmt der TestRunner), in Schritt 1.) wieder die DBConfiguration setzt. Weil diese im Cleanup nicht weggesetzt wurde, knallt es.

VG

21.03.2014 - 11:51 Uhr
  1. Wie prüfe ich denn ob sie schon da ist? Auch dazu habe ich nix gefunden. Klar, ich könnte das Setzen der DbConfiguration als Singleton implementieren, aber dann habe ich eine Schicht, die absolut unnötig und hässlich ist. Oder gibt es eine Möglichkeit, die aktuelle DbConfiguration abzufragen?

  2. Was ist denn wenn ich eine andere DBConfiguration setzen will?

VG

19.03.2014 - 23:30 Uhr

verwendetes Datenbanksystem: MSSQL2012, aber ich völlig egal

Nabend,

Lustige Situation. In EF6 gibt es die Möglichkeit, die Datenbank-Configuration, die in Zusammenspiel mit einem DbContext verwendet wird zu konfigurieren. Dazu wird beim Starten der Applikation die Methode SetConfiguration aufgerufen. Das sollte man machen, wenn Context und DbConfig nicht in einer Assembly liegen.

Nachzulesen ist das hier: Code-Based Configuration (EF6 onwards)

Ein Beispiel, so wie es bei mir läuft wäre folgendes:


public class SqlServerDbConfigurationService : IDbConfigurationService
    {
        public IFluxyDbConfiguration DbConfiguration { get; private set; }


        public SqlServerDbConfigurationService(string instance, string databaseName, string username, string password)
        {
           // some validation

            DbConfiguration = new SqlServerDbConfiguration(instance, databaseName, username, password);
        }

        public SqlServerDbConfigurationService(string instance, string databaseName)
        {
           //some validation
            DbConfiguration = new SqlServerDbConfiguration(instance, databaseName);
        }


        public void SetDatabaseConfiguration()
        {
            System.Data.Entity.DbConfiguration.SetConfiguration(DbConfiguration.GetDbConfiguration);
        }

        public string ConnectionString
        {
            get { return DbConfiguration.ConnectionString;  } 
        }
    }

Wichtig ist, dass der Code ausgeführt wird, bevor EF das erste mal verwendet wird. Funktioniert einbandfrei. Und nun das Problem: Der Code, vor allem der Context, will auch getestet werden. Das heisst ich habe (für mein Repository) Integrationstests, die an die Datenbank gehen (und nicht gemockt werden können).

Wenn ich die nun ausführe bekomme ich eine Fehlermeldung, sobald ich die DbConfiguration das zweite mal setzen will:

Exception is: InvalidOperationException - An instance of 'SqlServerDbConfiguration' cannot be set because an instance of 'SqlCompactDbConfiguration' is already being used. Only one DbConfiguration type can be used in an application. See
>
for more information.

Wenn ich die Tests einzeln ausführe, werden sie grün. Ist auch vollkommen klar: Man kann die DbConfiguration nicht überschreiben. Eigentlich ist das Problem, dass ich die DBConfiguration im Test nicht aufräume, und auch nicht aufräumen kann. Es gibt zwar ein SetConnection, aber ein UnsetConnection (oder ähnliches) habe ich noch nicht gefunden....

Siehe hierzu: DBConfiguraton (MSDN)

Weiss jemand eine Lösung, wie ich die DbConfiguraton in einem Test aufräumen kann?

VG, Fluxy

08.03.2014 - 23:30 Uhr

Hallo allerseits,

Ich möchte mich heute mal wieder meinem Lieblingsthema wittmen, MVVM.
Auch wenn mir zu Ohren gekommen ist, dass es zahlreiche Diskussionen gibt, ob das Pattern veraltet ist, ist es immer noch sehr mächtig.

Grundsätzlich muss ich gestehen, dass ich nie wirklich ein MVVM Framework eingesetzt habe. Die Komponenten, die ich benötige, habe ich mir selbst geschrieben. Mit ein paar Services (windowservice, dialogservice, etc.), den Bindingmechanismus mit ein paar Attachéd behaviors kommt man recht weit. Mein generelles Vorgehen in den viewmodels war es immer, das Model in das viewmodel zu injizieren (konstruktorinjektion) und properties wurden an das Model weitergeleitet.

Jetzt habe ich mich die letzten Tage mit prism, einem MVvm Framework vom Microsoft practices Team, beschäftigt.

Das Vorgehen scheint dort etwas anders zu sein. Zunächst ist es so, dass es Module gibt, die von einem bootstrapper geladen werden. Diese Module registrieren views und zeigen diese views in regions an. Zusätzlich werden in den viewmodel Klassen Services injiziert.

Daraus ergeben sich folgende Konsequenzen....

  1. es werden wie schon gesagt keine modelklassen in das viewmodel injiziert (das geht auch glaube ich gar nicht, es sei denn man würde die modelklassen im Container registrieren). Stattdessen werden Services geschnitten, um auf Modeladen zuzugreifen.

  2. in prism Tutorials liegen die viewmodels meinst im Projekt für das Modul. Sie sind zwar immer noch von den views logisch getrennt, befinden sich aber im gleichen manifact.

Ist das überhaupt in MVvm erlaubt? Ich finde die Benutzung etwas gewöhnungsbedürftig. Habt ihr schon Erfahrungen gesammelt?

VG, Fluss.

26.02.2014 - 22:50 Uhr

Das ist ja höchst interessant.

Ich habe die Datenbank einfach mal gelöscht und neu angelegt, nachdem ich meinem Schlüssel auf Integer geändert habe. Dann meine Tests ausgeführt fing EF an zu zicken. Ich habe die Fehlermeldung leider nicht kopiert, jedoch war die Rede davon, dass er meine Migration nicht ausführen will, weil er einen Datenverlust befürchtet. Generell finde ich die Exception ja gut, aber welcher Datenverlust (meine DB ist wie gesagt frisch angelegt).

Nagut egal, weiter gehts. DatabaseInitializer ausgetauscht, weil Code Migrations beim Entwickeln nerven (MigrateDatabaseToLatestVersion => DropCreateDatabaseIfModelChanges), und siehe da, meine UnitTests sind grün. Mit Integer's scheint es also zu funktionieren.

Model geändert, wieder auf Guids. Tests neu gestartet - immer noch grün.

Ich kann euch leider nicht sagen warum, aber irgendwie schien etwas mit den Migrations schief gewesen zu sein. Schön ist etwas anderes aber nun läuft es.

@all: von der Datenbank generierte Guids funktionieren also auch in EF6.

Viele Grüße,
Fluxy.

26.02.2014 - 22:00 Uhr

So wie ich es jetzt verstanden habe, wäre also das folgende Mapping korrekt:


  modelBuilder.Entity<DocumentSource>().ToTable("docs.DocumentSource");
  modelBuilder.Entity<FileSystemDocumentSource>().ToTable("docs.FilesystemDocumentSource");

            modelBuilder.Entity<DocumentSource>().HasKey(x => x.Id);
            modelBuilder.Entity<DocumentSource>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            modelBuilder.Entity<FileSystemDocumentSource>().HasKey(x => x.Id);
            modelBuilder.Entity<FileSystemDocumentSource>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

Übrigens: Mit Attributverwendung meint Abt die Verwendung von Attributen in Modelklassen, z.B. [Key] als Attributierung der Modelklasse.

Mein Fehler bleibt jedoch. Eventuell liegt es dann wirklich an EF6. Findet sich jemand der schon mit EF6 gearbeitet hat?

Schönen Abend noch,
Fluxy.

26.02.2014 - 20:01 Uhr


public class A 
{
public Guid Id{ get; set; }
public string Name{ get; set;
}

public class B : A
{
public string Strasse { get; set;}
}

public class  C: A
{
public int Count{ get; set; }
}

BTW: Das Beispiel ist sinnfrei.

Er würde mappen: Class A auf Tabelle A, Class B auf Tabelle B, Class C auf Tabelle C.
In jeder Tabelle nur die Properties die nicht vererbt wurden.

Jetzt ist es ja so, dass auch Tabelle B und C einen Key brauchen (Id von A), damit er Joinen kann.
Das heisst letztendlich muss er für Tabelle A, B oder C einen Schlüssel generieren, und den Schlüssel für alle 3 Tabellen verwenden.

Sofern ich das gesehen habe, ist das Standardverhalten das er keinen Schlüssel generiert.
Ich frage mich nun: Muss ich DatabaseGeneratedOption.Identity für alle drei Tabellen setzen?
Oder nur für A? oder nur für B und C? Wenn ich das mapping für alle setze, würde er dann mehrmals einen neuen Key generieren? Ich habe keine Ahnung, wie EF an der Stelle arbeitet...

26.02.2014 - 10:12 Uhr

Hallo Abt,

ich verwende EntityFramework 6. Bei der .NET Runtime bin ich mir jetzt nicht sicher, aber da ich das Projekt mit Visual Studio 2013 erstellt habe und die Runtime nicht umgestellt habe, sollten die Projekte .NET 4.5 benutzen.

Wenn ich Dich richtig verstehe, sollte das dann aber funktionieren.

Frage zu meinem Mapping: Das ist ja nur das Mapping der Basisklasse.
Ich habe zum Beispiel eine FilesystemDocumentSource. Wie würde das Mapping für die Tabelle aussehen? Genauso? Also gebe ich da dann auch DatabaseGeneratedOption.Identity an? Er braucht ja nur für eine Tabelle einen Schlüssel generieren.

VG, Fluxy

25.02.2014 - 23:05 Uhr

verwendetes Datenbanksystem: MSSQL 2012

es gibt in OR-Mappern prinzipiell 3 verschiedene Arten, eine Klassenhierarchie auf Tabellen zu mappen. Dabei gilt meist das Prinzip Convention-over-Configuration, welches dazu führt, dass meinstens TPH gemappt wird, ohne das der Entwickler etwas merkt.

Bei TPH (Table per Hierarchie) werden alle Objekte in einer Klassenhierarchie in eine Tabelle gemappt. Ich möchte dies allerdings in meinem aktuellen Anwendungsfall nicht, weil für mich TPT besser geeignet ist (bei TPH entstehen u.Umständen größere Lücken die ich vermeiden will).

Zu TPT (Jede Klasse wird in ihre eigene Tabelle gemappt) gibt es zwar einige Tutorials, allerdings habe ich noch eine weitere Anforderung. Ich möchte den Schlüssel nicht von der Anwendung erstellen, sondern von der Datenbank generieren lassen. Genau das bekomme ich nicht hin.

Fehlermeldung:

System.Data.SqlClient.SqlException: Der Wert NULL kann in die Id-Spalte, smartDocTest.docs.DocumentSource-Tabelle nicht eingefügt werden. Die Spalte lässt NULL-Werte nicht zu. Fehler bei INSERT.

Hat jemand eine Idee, wie ein Mapping für diese Situation aussehen müsste?
Ich habe gesehen das ich für Property-Mappings die Generator-Stategy setzen kann:


  modelBuilder.Entity<DocumentSource>().HasKey(x => x.Id);
  modelBuilder.Entity<DocumentSource>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

*Ist es richtig, dass ich für einen solchen fall HasKey() und Property nacheinander verwenden muss? *Die Objekte werden bei TPT zusammengejoint (Tabelle zum Typ + alle Basisklassen). Die Tupel in den Tabellen sollten den selben Schlüssel haben. Wo muss ich den Schlüssel als DatabaseGeneratedOption angeben? *Habt ihr ein Mapping? Habt ihr so etwas schonmal gemappt?

VG, Fluxy

25.02.2014 - 22:05 Uhr

Hallo Abt,

was EF an der Stelle tut, mir mir schon klar. Ich möchte allerdings eine automatische Migration auf keinen Fall ausführen. Schon gar nicht, ohne den Benutzer zu fragen.

das mit der Factory ist schonmal eine ganz gute Idee. Das Problem ist allerdings eher, dass wenn ich die vorgeschlagene MigrateDatabaseToLatestVersion als DatabaseInitilizer verwende, ich die Klasse typisieren muss (und zwar auf meine Configurationsklasse). Hier benötige ich den Typ (welcher als internal generiert wird), sodass mir eine Factory nicht wirklich weiterhilft.

Mir war aufgefallen, dass Enable-Migrations and Add-Migration nur auf dem Projekt ausgeführt werden können, in welchem auch der DataContext definiert ist. Es gibt zwar die Möglichkeit, ein Startup-Projekt anzugeben, aus welchem der ConnectionString gelesen wird, aber das Projekt in dem die Migrations erstellt werden ist zwangsläufig immer das selbe, wie das Projekt, in welchem der DataContext implementiert ist. Ausserdem wird die Configuration-Klasse immer als internal generiert.

Könnt ihr mir das so bestätigen? Ich fände es extrem schade, wenn das System wirklich so unflexibel ist. Eigentlich hatte ich gelesen, das sich bei EF vieles zum positiven verändert hat. Falls jemand Erfahrungen in ähnlichen Situationen gesammelt hat (den DataContext in einer Dll zu definieren ist glaube ich nicht ungewöhnlich), würde ich gerne erfahren, welche Lösung ihr gefunden habt.

Viele Grüße,
Fluxy

17.02.2014 - 22:20 Uhr

Hallo zusammen,

erstmal ein Dank an coffeebean, die Migration funktioniert wie bechrieben. Die Exception, die ich bekommen habe, hat irgendetwas mit der Migration ansich zu tun. Da scheint es Probleme zu geben, will man das Schema umbenennen. Aber nagut, das schau ich mir bei Zeiten mal an.

Mir liegt aber noch etwas anderes am Herzen. Ich finde es potentiell gefährlich, beim Starten der Applikation die Datenbank zu aktualisieren. Mir wäre es lieber, wenn die Applikation sich beim Starten bemerkbar machen würde, dass die Programmversion nicht mehr zur Datenbank passt und eine weitere Anwendung auszuführen ist.

Am liebsten würde ich die Migrations auch gar nicht in der Assembly halten, in der der DbContext definiert ist, sondern in der separaten Migration-App. Diese ganzen Migrationsklassen müllen sonst die Anwendung nur zu. Da ich aber bisher keine Möglichkeit gefunden habe, so etwas zu realisieren, würde ich euch mal um Feedback bitten.

Das Problem ist im Prinzip, dass Enable-Migrations nur auf Projekten ausgeführt werden kann, die den DbContext besitzen. Für eine Applikation, die eine Dll referenziert, welche den DbContext besitzt, habe ich die Erfahrung gemacht, das Enable-Migrations und Add-Migration nicht funktioniert. Darüber hinaus ist die erzeugte Konfigurationsklasse wie schon vorher beschrieben internal, also ohne Tricks für eine externe Applikation nicht sichtbar.

Ich freue mich schon auf euer Feedback.

VG, Fluxy

16.02.2014 - 20:10 Uhr

So wie Du das willst, sehe ich leider keine Möglichkeit. Aber Du könntest darüber nachdenken, Kundenspezifische Felderweiterungen als Generalisierung zu sehen. Dann bräuchtest Du eine Dll, in der du die Domänenobjekte für einen Kunden spezialisierst und könntest dann für diese Domänenklassen auch in Hibernate ein eigenes Mapping erstellen. Es würde sich dann anbieten, jede Implementation deines Domänenobjekts auf eine eigene Tabelle zu mappen. Also so sollte das möglich sein.

Ob diese Abbildung sinn macht, weiss ich nicht, da ich dein Modell nicht kenne.

VG

16.02.2014 - 16:35 Uhr

Hallo Coffeebean,

wie genau wird denn Deine Methode InitializeDataBase aufgerufen? Ich habe mir jetzt eine Consolenanwendung angelegt, in welcher der connectionString definiert ist. Die Migrations bekomme ich erzeugt, allerdings erzeugt das folgende Probleme

  1. Werden die Code-Migrations im EF-Projekt erzeugt und nicht in meiner Konsolenanwendung. Ob das richtig oder falsch ist, keine Ahnung.

  2. Die Klasse Migrations.Configuration ist internal, in meiner Konsolenanwendung also nicht sichtbar. Ich könnte Sie im Projekt internal visible machen, aber ich glaube ich mache da eher noch etwas falsch. Kannst Du mir ein komplettes Beispiel geben, über das man sieht, wie (und vor allem von wem) die Methode aufgerufen wird?

  3. Läuft die Migration nicht durch, weil ein Index nicht richtig abgeräumt wurde:
    System.Data.SqlClient.SqlException (0x80131904): Fehler bei dem Vorgang, weil ein Index oder eine Statistik mit dem Namen 'IX_Id' für 'docs.FilesystemDocumentSource' (Tabelle) bereits vorhanden ist.

Das habe ich getan:

  1. Enable-Migrations -ProjectName "SmartDoc.Persistence.EntityFramework" -StartUpProjectName "SmartDoc.Persistence.EntityFramework.Migrations"
  2. Add-Migration SchemaRename1 -StartUpProjectName "SmartDoc.Persistence.EntityFramework.Migrations"
  3. Update-Database -ProjectName "SmartDoc.Persistence.EntityFramework" -StartUpProjectName "SmartDoc.Persistence.EntityFramework.Migrations" -verbose

Vielen Dank für Deine Hilfe.

16.02.2014 - 15:19 Uhr

verwendetes Datenbanksystem: MSSQL Server 2012

Hallo,

ich habe gerade versucht, das erste mal in meinem Leben eine Code First Migration durchzuführen. Beim EnableMigrations wird jedoch versucht, auf meine Datenbank zuzugreifen. So wie ich es verstanden habe, soll daraus eine Initiale Klasse mit dem aktuellen Timestamp generiert werden.

Das Problem: Ich verwende für meinen DbContext einen ConnectionString, dessen name in in den Ctor von DbContext injiziere:


 public class SmartDocContext : DbContext
    {
    
        public SmartDocContext() : base("SmartDoc")
        {
            
        }
}

Diese Abhängigkeit vom (in der app.config) Konfigurierten Connectionstring kennt Code Migrations natürlich nicht, und ich glaube das wird genau das Problem sein. Könntet ihr mir sagen, wie ich ihm den Namen des ConnectionStrings mitteilen kann, sodass er diesen auch nutzen kann?

Der Fehler sieht so aus

Fehlermeldung:
Enable-Migrations -Force
Checking if the context targets an existing database...
System.Data.Entity.Core.ProviderIncompatibleException: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct. ---> System.Data.Entity.Core.ProviderIncompatibleException: The provider did not return a ProviderManifestToken string. ---> System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover) at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover) at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout) at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance) at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData) at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal&amp; connection) at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal&amp; connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource1 retry)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource1 retry) at System.Data.SqlClient.SqlConnection.Open() at System.Data.Entity.SqlServer.SqlProviderServices.&lt;&gt;c__DisplayClass2f.&lt;UsingConnection&gt;b__2d() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.&lt;&gt;c__DisplayClass1.&lt;Execute&gt;b__0() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1 operation)
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation)
at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action1 act) at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action1 act)
at System.Data.Entity.SqlServer.SqlProviderServices.GetDbProviderManifestToken(DbConnection connection)
at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
--- End of inner exception stack trace ---
at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection)
--- End of inner exception stack trace ---
at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection)
at System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.<>c__DisplayClass1.<ResolveManifestToken>b__0(Tuple3 k) at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory) at System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.ResolveManifestToken(DbConnection connection) at System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(DbConnection connection, DbProviderManifest&amp; providerManifest) at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection) at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext) at System.Data.Entity.Internal.RetryLazy2.GetValue(TInput input)
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ModelBeingInitialized()
at System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(DbContext context, XmlWriter writer)
at System.Data.Entity.Utilities.DbContextExtensions.<>c__DisplayClass1.<GetModel>b__0(XmlWriter w)
at System.Data.Entity.Utilities.DbContextExtensions.GetModel(Action`1 writeXml)
at System.Data.Entity.Utilities.DbContextExtensions.GetModel(DbContext context)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
at System.Data.Entity.Migrations.Design.MigrationScaffolder..ctor(DbMigrationsConfiguration migrationsConfiguration)
at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.Run()
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldInitialCreate(String language, String rootNamespace)
at System.Data.Entity.Migrations.EnableMigrationsCommand.<>c__DisplayClass2.<.ctor>b__0()
at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)

VG

31.10.2013 - 23:49 Uhr

Ich muss gerade ein bisschen schmunzeln, weil gerade an Position 1 im Forum über den SimpleMembershipProvider von MVC hergezogen wird, und ich nun eine Frage stelle, weil ich ihn auch verwende. Vielleicht sollte ich es ja lieber lassen, aber sagen wir mal so ich implementiere kein Forum 😄

Ich habe mir als Einarbeitung das Video von Pluralsight zum Thema Sicherheit von ASP.NET MVC4-Anwendungen angeschaut. Dort werden Rollen beim Initialisieren der Datenbank erstellt und zugeordnet. Leider kann bzw. möchte ich das so nicht machen, also dachte ich mir, ich schreibe mir eine kleine Adminseite, auf der man jedem User mit einer oder mehreren Rollen verbinden oder auch wieder trennen kann.

So weit so gut, schreibe ich mir mal meine Actionmethode (ich verwende direkt den schon existierenden Accountcontroller). Kurz gegoogled und direkt eine "umständliche" Methode gefunden die anscheinend meine erste Teilaufgabe erledigt: Das Abfragen aller User.

Ausprobiert, aber leider kommt eine NotSupportedException. Leider sagt mir die Exception nicht warum. Aber vielleicht habe ich ja Glück, und jemand von euch weiss weiter.

BTW: Das ist erstmal nur eine Testmethode für den ersten Schritt, wie man unschwer erkennen kann.

Zunächst mal der Code:


  [Authorize(Roles = "Admin")]
        public ActionResult ManageUsers()
        {
            var roleProvider = (SimpleRoleProvider)Roles.Provider; 
            var membershipProvider = (SimpleMembershipProvider)Membership.Provider;
            int totalNumberOfUsers;

            //hier wird die Exception geworfen
            MembershipUserCollection membershipCollection = membershipProvider.GetAllUsers(0, 10, out totalNumberOfUsers);
            IList<MembershipUserViewModel> usersViewModels = new List<MembershipUserViewModel>();

            foreach (MembershipUser user in membershipCollection)
            {
                var membershipUser = new MembershipUserViewModel();
                membershipUser.Username = user.UserName; 

                usersViewModels.Add(membershipUser);
            }
            return View(usersViewModels);
        }

und nun die Exception:

Fehlermeldung:
System.NotSupportedException was unhandled by user code
HResult=-2146233067
Message=Die angegebene Methode wird nicht unterstützt.
Source=WebMatrix.WebData
StackTrace:
bei WebMatrix.WebData.SimpleMembershipProvider.GetAllUsers(Int32 pageIndex, Int32 pageSize, Int32& totalRecords)
bei WorktimeRecorder.Controllers.AccountController.ManageUsers() in d:\Dev\Projects\WorktimeRecorder\WorktimeRecorder\Controllers\AccountController.cs:Zeile 36.
bei lambda_method(Closure , ControllerBase , Object[] )
bei System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
bei System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) bei System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters)
bei System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41()
bei System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass81.&lt;BeginSynchronous&gt;b__7(IAsyncResult _) bei System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult1.End()
bei System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
bei System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
bei System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()
InnerException:

Ich finde eine unspezifiziertere Meldung gibt es gar nicht. Von daher hoffe ich nun auf Hilfe von euch.

VG, Fluxy

12.10.2013 - 16:04 Uhr

Hallo,

danke für eure Antworten.

@Abt: Danke für Deinen Tip mit den SubmitModels, aber ich glaube das wird des ganzen eine Abstraktionsschicht zu viel. Ich habe ja schon die ViewModel, die so etwas wie SubmitModels sein sollen. Du wirst sagen, aber die Referenzieren ja noch das Domain Model, und damit hättest Du recht. Da brauche ich also noch eine Abstraktionsschicht, so wie es jetzt ist bekomme ich sogar Probleme mit LazyLoading. Den Begriff der SubmitModels werde ich mir trotzdem mal merken.

@MarcoT:
Danke für Dein Beispiel. Ich weiss nicht warum es mittlerweile funktioniert, aber ich konnte mir schon selbst helfen. So funktioniert es:


 @Html.DropDownListFor(model => model.ProjectId, Model.Projects, "please select project...")

Warum es nicht funktioniert hat, kann ich nicht reproduzieren.

Warum erzeugst Du eigentlich die SelectList in der View, und nicht im Code? Was ist sauberer?
Wo ist in der MVC Architektur die Richtige stelle, um SelectList's zu erstellen?

VG, Fluxy

07.10.2013 - 12:32 Uhr

Problem gelöst:

Das Problem war, dass ich aus versehen beim Disposen des Repositories ein Flush gemacht habe. Das mag NHibernate nicht, und ist auch nicht notwendig.