Das mag für dich alles ironisch und witzig sein, für mich ist es das nicht.
In der Regel bekomme ich eine Anforderung zur Umsetzung und schaue dann, wie und mit was man das am besten umsetzen könnte. Ich kenne nicht jede MS-API, nicht jede Doku, nicht jede Library und nicht alle best practices, schon gar nicht im Detail. Die Entwicklung ist mittlerweile so schnell, dass ich nicht mehr hinterher komme. Ich habe den Überblick verloren. Ich kann mich mit den Dingen erst dann beschäftigen, wenn ich sie auch aktiv nutzen will/muss. Und das dauert. Irgendwann hat man sich durch Erfahrung irgendwo eingearbeitet, und dann ist es ruckzuck obsolet und durch x andere Dinge ersetzt. Ja, das ist in der IT so. Aber ich bin nun mal nur ein Mensch und habe nur begrenzte Zeit.
Ja, ich habe mich anfangs recht knapp gehalten. Einfach aus dem Grund, weil nach meiner Erfahrung lange ausführliche Beschreibungen nicht gelesen werden. #TLTR oder #TLDR.
Du hast ne Microsoft ASP.NET Core Lib verwendet, die für Mulit Platform gedacht ist und auf die Windows Registry zugreift?
Bezweifle ich.
Auch hier wusste ich nicht, dass es eine Mulit Platform Kennzeichnung gibt. ASP.NET Core ist ein plattformübergreifendes Framework. Daher habe ich naiv gedacht, auch die Librarys für ASP.NET Core wären plattformübergreifend. Vor allem wenn sie von MS kommen.
Während der Entwicklung ist nichts aufgefallen, weil ich unter Windows entwickle. Sobald mein Service dann in einem Docker-Container lief, hat es geknallt. Seit dem wäge ich genau ab, ob es mir das Risiko wert ist. Ich nutze ja selten die komplette Funktionalität einer Library.
Sei's drum, ich glaube unsere Gags und Grundsatzdiskussionen sind ein anderes Thema. Danke für deine Hilfe.
Bitte entschuldige meine Unwissenheit.
Was Du mit den Code-Schnippseln hier willst: keine Ahnung.
Aus dem Zusammenhang gerissen und einfach mal gepostet? Ja, dann kommt invalid Token bei raus 🙂
Ich kenne das so, dass man bei einer Frage in einem Forum erst mal das Problem beschreibt und was man bereits gemacht/ausprobiert hat.
Ich habe viele Tutorials ausprobiert wie das hier und ähnliche. Je nach Tutorial war mal die neue und mal die alte URL, wobei ich nicht erkennen konnte, welche davon die Aktuelle ist. So oder so, ich bekomme bei beiden URLs das gleiche Ergebnis.
Meine Code-Schnippsel sehen so aus, wie in dem Tutorial weiter oben. HTTP-Aktion + URL + Header. Kann ja sein, dass eines davon falsch ist.
Ebenso kannst Du einfach das Microsoft Identity SDK verwenden.
Am liebsten würde ich tatsächlich plain REST verwenden. Ich habe mit den Microsoft-SDKs schlechte Erfahrung gemacht. Eine davon war ein ASP.NET Core-Library. Sie versuchte auf die Registry zu zugreifen. Da mein Service unter Ubuntu gehostet wird, gab's die natürlich nicht. Solche "Überraschungen" möchte ich gerne vermeiden.
Je nachdem was Du machst, kann es auch sein, dass Du aufgrund der Sicherheitsleveln (zB Full Access) den Weg über ein Zertifikat gehen musst.
Hmm, das wäre schlecht. Mein Service ist eine Middleware, die meist on-prem läuft. Sie bietet eine REST-API über die man z.B. Bibliothekseinträge und die dazugehörigen Dateien anfordern kann.
Bisher lief das so:
Ein Client sendet einen HTTP-Request zu meiner Middleware. In diesem Request ist unter anderem die URL zum Sharepoint-Tenant (z.B. https://myTenant.sharepoint.com), Sharepoint Benutzer Credentials (Benutzername und Passwort) und die ID einer Bibliothek/Liste. Die Middleware hat dann mit den Credentials einen Cookie angefordert und dann diese Anfrage inkl. Cookie dann an Sharepoint weiter geleitet, eine Antwort von Sharepoint bekommen und diese Antwort dann aufgearbeitet an den Client weiter geleitet. Das funktioniert auch wunderbar.
So soll es in Zukunft laufen:
Die Funktionalität von oben soll erhalten bleiben. Lediglich die Art der Authentifizierung soll von Basic auf OAuth2 umgestellt werden. Da es sich um einen Dienst, der auf irgendeinem Server läuft, handelt, darf nicht der Dialog mit der Anforderung der Zugriffsrechte erscheinen. Das muss vorher korrekt im Azure-Portal konfiguriert sein. Auch hier bin ich mir unsicher.
Sollte ein Zertifikat notwendig sein, müsste der Client dieses bei jedem Request mit senden. Das würde ich auch gerne vermeiden.
Die Aktionen, die meine Middleware auf Sharepoint durchführt:
Mir ist jetzt auch noch was anderes aufgefallen.
Wenn ich den bisherigen Weg gegangen bin (mit den Credentials), habe ich folgende URL aufgerufen um eine Bibliothek zu holen:
https://myTenant.sharepoint.com/_api/web/lists(guid'{listId}')
Kann ich das so weiter nutzen, oder muss ich jetzt die Graph-API verwenden?
https://graph.microsoft.com/sites/{siteId}/lists/{listId}
Ich habe das jetzt mal mit der Graph SDK und diesem Beispiel-Code (etwas erweitert um das Holen der Spalten einer Liste) erfolgreich ausprobiert. Aber das JSON-Objekt hat eine andere Struktur, als bei meinem bisherigen Weg mit Credentials. Kann man OAuth2 nur mit der Graph-API verwenden, oder geht das auch über die bisherige Sharepoint REST API?
Ich habe schon so einige Tutorials ausprobiert, aber nichts hat funktioniert.
Ich versuche mich per OAuth2 und REST an Sharepoint zu authentifizieren.
POST "https://login.microsoftonline.com/{targetSharepointApi.TenantID}/oauth2/token"
FormUrlEncodedContent:
client_id=myId&
client_secret=mySecret&
grant_type=client_credentials&
resource=https://myTenant.sharepoint.com
(habe es auch noch zusätzlich mit scope=https://myTenant.sharepoint.com/.default versucht).
Bekomme daraus ein JSON-Objekt in dem unter anderem auch ein accessToken enthalten ist. So weit, so gut.
Versuche ich dann aber irgendwelche Ressourcen aufzurufen (Authorization=Bearer {accessToken}), bekomme ich ein 401 zurück mit der Meldung "Unsupported app only token".
Ich habe das Gefühl, da fehlt noch ein Schritt.
App ist im Azure-Portal angelegt, Secret ist angelegt und die App hat (erst mal) weitreichende Berechtigungen.
Kann mir bitte jemand auf die Sprünge helfen?
Der frühere Standard RFC2047, der ISO-8859-1 erlaubte, wurde durch RFC7230 abgelöst.
Der erklärt einiges.
ASP.NET Core an dieser Stelle ist also korrekt implementiert; der Fehler liegt beim Absender.
Habe es zunächst über Swagger ausprobiert, aber da kann man ja eingeben, was man will. Postman lässt tatsächlich (nur) ISO-8859-1-Zeichen zu. Wenn man was anderes eingeben will, meckert Postman. Das Paragraphen-Zeichen lässt er zu. Daher dachte ich nicht daran, dass es da noch weitere Einschränkungen gibt.
Deswegen wars wichtig zu wissen, mit welcher ASP.NET Variante (und nicht der Produktfamilie selbst) Du arbeitest.
WebAPI war früher ein eigenes Modul; ist heute nur noch Marketing-Begriff weil es einfach Teil von ASP.NET MVC ist.
Ich blicke bei den Begrifflichkeiten nicht mehr durch.
Der interessante Teil hier ist (ich habs selbst noch nicht gebraucht)
"RequestHeaderEncoding": "utf-8"
private static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .ConfigureKestrel(kestrel => { kestrel.RequestHeaderEncodingSelector = _ => Encoding.Latin1; }); );
Habe ich ausprobiert, aber das Paragraphen-Zeichen wird trotzdem nicht korrekt dekodiert. 🙁
Wenn man ein neues Projekt diesen Typs anlegt, nennt sich das "ASP.NET Core-Web-API". Habe zwei Projekte, .NET Core 3.1 und .NET 6.0.
Methode im Controller sieht so in der Art aus:
[HttpGet]
[ServiceFilter(typeof(TraceReqestAttribute))]
[Produces(ContentTypes.APPLICATIONJASON)]
public async Task<ActionResult<MyEntity>> Get([FromHeader][Required] string id, [FromHeader][Required] string testString)
{
}
Problem ist hier das Argument "testString".
Ich habe es auch mit einem ServiceFilter versucht, um den Parameter möglichst früh abzugreifen:
public class TraceReqestAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ActionArguments?.Count > 0)
{
KeyValuePair<string, object>? parameter = actionArguments.FirstOrDefault(x => x.Key.ToLowerInvariant().Equals("testString"));
_testString = parameter?.Value.ToString();
}
base.OnActionExecuting(context);
}
Leider das gleiche Ergebnis.
Wenn ich an den Parameter kommen würde, bevor er dekodiert wird, könnte ich ihn selbst korrekt dekodieren. Das scheint aber schon vorher zu passieren, denn parameter.Value ist zwar vom Typ object, darin steckt aber schon ein String.
Ich habe das Problem, dass ich in meiner WebAPI in einem Request einen Header-Parameter bekomme, der das Paragraph-Zeichen (§) enthält. Dieser wird nicht korrekt angezeigt. Stattdessen sieht man �.
Eigentlich können HTTP-Header ISO-8859-1-Zeichen enthalten. Die WebAPI kann aber anscheinen nur ASCII decodieren. Oder kann man das irgendwie konfigurieren?
Wirste so ohne Mounten vermutlich nicht hinbekommen.
Alles klar, danke.
Dachte ich mir schon, werde jetzt nach einer anderen ganz Lösung suchen.
Ich arbeite an einem Dienst, den ich in ASP.NET Core entwickle. Dieser Dienst wird unter Ubuntu gehostet.
Nun muss ich auf UNC Pfade zugreifen, was an sich kein Problem ist. Aber nur so lange bis man auf UNC Pfade mit Credentials zugreifen möchte. Ich weiß zwar, dass das mit dem Einbinden von Windows-API-Funktionen advapi32.dll geht, aber die habe ich unter Ubuntu nicht.
Eine Besonderheit ist auch, dass ich diese UNC-Pfade nicht unter Ubuntu mounten kann, da sie irgendwann zur Laufzeit bekannt werden. Und es können beliebig viele sein.
Weiß jemand eine Lösung?
Das ist mittlerweile eine sehr weit verbreitete und übliche Anforderung.
Ich vermute, das wird sich in der Zukunft immer mehr Richtung Cloud verlagern, aber bis dahin müssen wir diese zwei Welten versuchen zu vereinen. Fühlt sich alles irgendwie als Notlösung an.
Warum kein standardisierten Weg nehmen?
Ich weiß es auch (noch) nicht, sitze hier sozusagen zwischen den Stühlen. Ich muss mir das nächsten Jahr mal genauer anschauen.
Danke schon mal für deine Hilfe.
Sorry, dass ich mich erst jetzt melde, Weihnachtsfeier und so... 😁
Sowas hab ich fast schon befürchtet: und wäre ein riesen Fehler.
Ja, leider habe ich da nicht die Entscheidungsgewalt. Ist aber auch ein etwas ungewöhnlicheres Problem, weil es um eine Art Hybrid-Cloud geht, also zum Teil bei Azure gehostet, teilweise on-prem.
Willst Du trotz der Gefahren, diese Richtung weiter gehen, dann lös das ganze über RoutePrefixes.
Dazu gibt es auch ein GitHub Repository, wo Du Dirdas Verhalten abschauen kannst
>
Dort gibts auch noch ein Sample mit der Swagger Integrierung. Prinzipiell bekommst damit Dein Vorhaben geschenkt.
Ich habe mir das angeschaut, muss aber ehrlich zugeben, ich steig da nicht wirklich durch bzw. weiß nicht so recht, was davon relevant für mich wäre. Wir grübeln derzeit an verschiedenen Lösungsansätzen, aber so richtig gut gefällt uns noch nichts davon.
Danke. Die machen wir jedes Jahr, teilweise sogar mehrfach. Ist ein altes Rezept von meiner Mutter, die es vermutlich von ihrer Großmutter hat. Für mich immer ein Ausflug in meine Kindheit. Und Junior liebt sie auch.
Danke für deine Antwort.
Meine Applikation ist ein Dienst, der zusammen mit anderen Diensten ausgeliefert wird (Docker, Kubernetes). Im Prinzip eine Microservices-Infrastruktur. Darum kümmert sich allerdings jemand anders.
Diese Dienste laufen alle auf einem Host. Hinzu kommt, dass sie auf diesem Host zwei mal installiert sind, eine Produktiv- und eine Test-Umgebung. Die Idee ist es diese durch unterschiedliche Pfade zu den Endpunkten zu trennen. Man kann das im entferntesten Sinne also als Multi-Tenant bezeichnen.
Ich habe eine REST-API entwickelt, die auch so weit gut funktioniert. Fürs Testen und zur Dokumentationszwecken habe ich Swashbuckle (Swagger) eingebunden.
Nun habe ich die Aufgabe den Mittelteil der URL zu den Endpunkten variabel zu machen. Dafür wird der einzusetzende Wert aus einer Umgebungsvariable ausgelesen.
Derzeit:
https://localhost:44316/System
Dabei wir die Route per Annotation über den Controller definiert:
[Route("[controller]")]
Soll:
https://localhost:44316/DynamischerTeil/System
Dabei ist "System" der Controller und "DynamischerTeil" soll aus der Umgebungsvariable ersetzt werden.
Mein Versuch (Auszug):
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: !string.IsNullOrEmpty(DynamischerTeil) ? $"{DynamischerTeil}" + "/{controller}" : "{controller}"
);
});
Allerdings funktioniert das so nicht. Und wenn ich die Route-Annotation des Controllers entferne, taucht der Controller nicht mehr in Swagger auf.
Am einfachsten wäre es, wenn ich die Route-Annotation dynamisch gestalten könnte:
[Route("{DynamischerTeil}/[controller]")]
Hat jemand einen Tipp für mich, wie man das am besten lösen könnte? Es geht "nur" um das Routing.
Danke für die Hilfe. Vom Gefühl her würde ich bei dem Projekt OnPrem eher auf BasicAuth setzen. Das wäre dann aber einen neuen Thread wert.
Ich glaub's zwar nicht, aber vielleicht habe ich das Glück, dass bis dahin sich OnPrem von alleine erledigt hat, weil kein Bedarf.
Fühl dich geknutscht 😁
Das hat mir vorerst weiter geholfen.
Ist das nur ein Sample Host? Weil sharepoint.com suggeriert, dass wir doch von SharePoint Online sprechen und nicht von On Prem.
Zum Entwickeln habe ich mal eine Azure-Instanz, weil der erste Kunde das so braucht. Später soll aber auch on-prem möglich sein.
Naja; in dem einen Fall hast Du wohl eine Authentifizierung via Cookie, im anderen via Basic Auth.
Das sind zwei verschiedene paar Stiefel. Mir fehlt hier aber das Wissen, wieso Du hier überhaupt mit einem Cookie arbeitest.
BasicAuth war hier in meinem Code nur ein verzweifelter Versuch. Also einer von vielen verzweifelten Versuchen.
Den Cookie nutze ich, weil ich vom Kunden nur User und Passwort bekommen habe und das Netz in diesem Fall das Verfahren mit dem Erzeugen eines Cookies ausgespuckt hat.
Wird Basic Auth für das Erstellen des Cookies benötigt, und dann arbeitest Du bei allen weiteren Requests mit dem Cookie oder wie?
Vermutlich nicht. War aber so in den Beispielen drin und es hat auf Anhieb funktioniert. Im Nachhinein macht das natürlich nicht so viel Sinn.
Es kann aber sein, dass Du pro Verfahren eine Client-Instanz brauchst - aber nicht für jeden Host.
Damit kann ich gut leben. Ich werde zunächst die Online-Variante mit allem, was dazu gehört, implementieren. Wenn dann die Anforderung für On-Prem kommt, werde ich nur die Authentifizierung um den entsprechenden Part erweitern.
Ist ja mit / dank DI super easy.
Joa, ich bin da noch nicht so fit drin X(
Dir wurde ja jetzt auch mehrmals gezeigt, dass man einen HttpClient problemlos injizieren kann und die Credentials (auch Basic!) auch ohne Handler setzen kann.
Jetzt stehe ich auf dem Schlauch. Wo wurde mir das gezeigt?
Ich habe den Traffic per Fiddler untersucht. Es sind nicht die Credentials, zumindest nicht direkt, sondern der daraus resultierende Cookie:
GET /_api/web/lists HTTP/1.1
Accept: application/json; odata=verbose
Accept-Language: de-DE, de; q=0.9, en-US; q=0.8, en; q=0.7
Accept-Charset: utf-8
Cookie: SPOIDCRL=77u/PD94bWwgdmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Host: xxx.sharepoint.com
Ich habe aus meinem oberen Beispiel auch aus dieser Zeile:
using (var handler = new HttpClientHandler() { Credentials = credentials })
das "Credentials = credentials" entfernt, und es hat trotzdem funktioniert, weil der Cookie korrekt gesetzt wurde.
Das heißt, ich bräuchte nur eine HttpClient-Instanz, wenn ich den Cookie im Nachhinein immer wieder neu setzen könnte. In meinem oberen Beispiel wird der Cookie über den HttpClientHandler gesetzt:
handler.CookieContainer.SetCookies(uri, credentials.GetAuthenticationCookie(uri));
An den komme ich ja nicht mehr dran, wenn der HttpCLient damit ein mal instantiiert wurde. Gibt es dafür noch eine andere elegante Methode?
Ich hatte Dich so verstanden, dass Du "SharePoint Online" verwendest und damit nicht On-Prem bist. Sorry.
Die Applikation, an der ich arbeite sollte am besten beides können (Online und On-Prem). Es gibt halt solche und solche Unternehmen in DE.
Na gut, fühlt sich irgendwie falsch an, aber ich habe keine bessere Lösung, als einen HttpClient pro Host zu instantiieren und diese dann in einem Dictionary vor zu halten.
Wir sprechen mit SharePoint über Azure Active Directory
Interessant. Funktioniert das auch mit Sharepoint on premise?
Daher kann ich Dir an der Stelle auch nicht mehr weiter helfen.
Trotzdem danke für deine Mühe. Wie machst Du das denn? Vielleicht würde mir das helfen?
Daher kann ich Dir an der Stelle auch nicht mehr weiter helfen.
Vergleich halt mal die Requests via Fiddler.
Werde ich machen.
PS: auch in Konsolenanwendungen kannst Du problemlos Dependency Injection nutzen.
Wollte eine möglichst einfache Test-Anwendung, um Seiteneffekte auszuschließen. Wäre auch nicht auf die Idee gekommen, dass DI Probleme macht. Wobei das nicht die DI ist, sondern die Kombination aus DI, dem Wiederverwenden des HttpClient und der Anforderung von wechselnden Credentials.
Andererseits kann ich mir irgendwie nicht vorstellen, dass das Framework da so starr ist.
Aus meinem Test-Projekt (.NET Core 2.2)
private string GetAsync(string realtiveUrl, SharePointOnlineCredentials credentials)
{
Console.WriteLine($"GET { realtiveUrl }");
using (var handler = new HttpClientHandler() { Credentials = credentials })
{
Uri uri = new Uri(_baseAddress);
handler.CookieContainer.SetCookies(uri, credentials.GetAuthenticationCookie(uri));
using (HttpClient httpClient = new HttpClient(handler)
{
BaseAddress = new Uri(_baseAddress)
})
{
SetRequestHeaders(httpClient);
using (HttpResponseMessage response = httpClient.GetAsync(realtiveUrl).Result)
{
Console.WriteLine($"GetAsync() StatusCode: {(int)response.StatusCode} {response.StatusCode}");
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsStringAsync().Result;
}
else
{
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
return string.Empty;
}
}
}
}
}
Ergebnis: 200 Ok
Aus meinem Produktiv-Projekt (ASP.NET Core 2.2)
protected async Task<ContentString> GetAsync(TargetSharepointApi targetSharepointApi, string relativeUri)
{
Uri requestUri = CreateUri(targetSharepointApi.BaseUrl, relativeUri);
using (HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri))
{
httpRequestMessage.Headers.Add("accept", "application/json;odata=verbose");
httpRequestMessage.Headers.Add("accept-language", "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7");
httpRequestMessage.Headers.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8"));
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("<user>:<password>")));
using (HttpResponseMessage response = await _httpClient.SendAsync(httpRequestMessage))
{
if (!response.IsSuccessStatusCode)
{
throw GetECMException(response);
}
using (HttpContent content = response.Content)
{
return new ContentString()
{
Content = await content.ReadAsStringAsync(),
ContentType = content.Headers.ContentType.ToString()
};
}
}
}
}
User und Passwort wurde natürlich ersetzt.
Ergebnis: 403 Forbidden
{
"error": {
"code": "-2147024891, System.UnauthorizedAccessException",
"message": {
"lang": "en-US",
"value": "Access denied. You do not have permission to perform this action or access this resource."
}
}
}
Das ist prinzipiell kein gutes Beispiel für ein Multi-Tenant-System.
Das ist richtig. Das habe ich nur in meinem Test-Projekt verwendet, um zu evaluieren, wie ich grundsätzlich mit dem Sharepoint kommunizieren kann. Wollte das dann in mein produktives Projekt (ASP.NET Core 2.2) übertragen.
Tatsächlich nutze ich in meinem produktiven Projekt bereits die HttpClientFactory per DI. Das ist ja im Grunde das Problem. Diese bietet nur die Methode CreateClient(), die mir einen fertigen HttpClient liefert. Da kann ich weder Credentials, noch einen neuen HttpClientHandler zuweisen.
Ich habe es jetzt auch mal mal mit HttpRequestMessage versucht, aber auch da kann ich keine Credentials setzen. Ich kann zwar einen Authorization-Header erstellen, bekomme dann aber trotzdem ein 403 zurück.
Danke für die schnellen Antworten.
Wäre es eine Option, den
> Overload zu benutzen? Da kannst du pro Request die Optionen setzen (und somit in Folge den gleichen Client benutzen).
Das hört sich erst mal gut an. Werde ich gleich mal versuchen.
Warum hat der Client Handler die Credentials?
Ich habe mich an Beispiele, wie dieses gehalten. Dort wird der HttpClientHandler mit den Credentials gefüttert. Wie man im Nachhinein diesen oder die Credentials ändern kann, habe ich nicht herausfinden können.
Du kannst den Token doch direkt dem HttpClient zuweisen.
Habe derzeit nur Benutzer und Passwort.
Hab exakt die gleiche Konstellation von HttpClient zu SharePoint, wobei ich noch auf die empfohlene Variante via HttpClientFactory setze.
Würde ich mir auch gerne anschauen. Hast Du da einen Link mit guten und aussagekräftigen Beispielen?
Ich arbeite an einer ASP.NET Core 2.2 Applikation, die im Prinzip eine Art Adapter für REST-Calls darstellt. Nach außen hin bietet meine Applikation eine REST-API. Diese REST-API empfängt Calls, die entweder Daten abfragen oder liefern. Diese Calls beinhalten unter anderem auch Informationen darüber, woher sich meine Applikation diese Daten her holen soll (URL, Credentials, ...). Die Applikation geht dann hin und fragt die Daten bei einer anderen API an, konvertiert diese Daten in ein bestimmtes Format, und gibt sie an den Aufrufer zurück.
Beispiel:
Kunde A schickt eine Anfrage an meine Applikation und möchte, dass meine Applikation ihm Daten aus seinem Sharepoint-Server (Sharepoint A) liefern soll. Im Call wird die URL und die Credentials des Sharepoint-Servers mit gesendet, so dass meine Applikation weiß, woher sie die Daten holen soll.
Das Gleiche kann Kunde B, C, D, ... mit seinem Sharepoint machen. Das heißt, es ist eine unbestimmte un variable Anzahl von Kunden und Sharepoint-Servern, die ich zur Entwicklungszeit nicht kenne.
Das hört sich komisch an (Kunde könnte direkt mit seinem Sharepoint kommunizieren), aber das ist nur ein kleiner Ausschnitt aus dem Anwendungsfall. Also keine Gedanken dazu machen.
Für die Kommunikation mit z.B. so einem Sharepoint-Server nutze ich den HttpClient. Nun ist der ja so konzipiert, dass man nicht pro Call eine neue Instanz erzeugt, sondern die vorhandene Instanz wieder verwendet. Leider ist es aber so, dass man dem HttpClient nur bei der Instantiierung im Konstruktor einen HttpClientHandler mit geben kann, der wiederum die Credentials beinhaltet. Ist der HttpClient ein mal instanziiert, kann ich weder neue Credentials noch einen neuen HttpClientHandler zuweisen.
Nun habe ich mir zwei Lösungen überlegt, die sich aber beide nicht gut anfühlen.
Wie gesagt, beides erscheint mir nicht empfehlenswert.
Hat jemand eine Idee, wie ich das Problem elegant auflösen kann?
Wir experimentieren noch herum.
Momentan versuchen wir einen Lizenz-Service zu schreiben, mit dem man Lizenzen verwalten kann und auf die man auch einige Methoden anwenden können soll.
Wir haben folgenden Fall:
Unser Lizenz-Service verwaltet Lizenzen von verschiedenen Produkten.
Vereinfacht gesagt:
So weit so gut.
Nun müssen wir aber auch Actions durchführen, die über die üblichen Verben GET, PUT, POST, DELETE gehen, wie z.B. beim Aktivieren einer Lizenz.
Das Problem ist nun, dass das System nicht unterscheiden kann, welches URL-Element die ID und welches Element die Action ist.
Ich habe bereits recherchiert, und das hier kommt dem Problem, welches wir haben, schon recht nahe.
Aber auch das fühlt sich nicht wirklich elegant an.
Wie ist das standardmäßig vorgesehen, also wie ist der best practice Fall? Oder ist die Web API lediglich für CRUD-Opertionen gedacht?
Da Du Dich an keinerlei Standards wie zB
> hälst (nicht unüblich, leider) musst Du Dir alles rund ums Thema Exceptions leider selbst zusammen basteln.
Ja, das mit den Standards ist so eine Sache. Ich versuche momentan ein Gefühl dafür zu bekommen, was man wie wann einsetzt. Dabei möchte ich natürlich auf einen zukunfts- und damit investitionssicheren Standard setzen. Die Informationen dazu sind aber nicht selten gegensätzlich.
Das macht es nicht gerade einfach sich autodidaktisch damit auseinander zu setzen. Ich hatte zwar eine Schulung zu dem Thema, aber der Dozent war unterirdisch schlecht, und hat mehr Schaden angerichtet, als geholfen.
Ich habe mir deinen Artikel durchgelesen. Ich fand ihn sehr gut. Lustig, dass es zeitlich so passt.
Allerdings verstehe ich nicht, wie das in diesem Zusammenhang helfen soll. Da gehst Du auf OData als Datenpool ein, wie man dort Daten abruft und filtert. Ich habe hier lediglich einen FileStream, der in einem iframe angezeigt werden soll. Ich verstehe nicht, wie da OData konkret helfen könnte. Ich muss hier keine Datensätze anfordern und filtern können.
Im Endeffekt musst Du manuell die REST-empfohlenen Statuscodes implementieren und diese auf dem Client auswerten.
Das wäre aus meiner Sicht Schritt 2. Vorher muss ich diesen Code auf Clientseite entgegennehmen und darauf reagieren. Doch schon das Entgegennehmen funktioniert nicht, wie ich oben beschrieben habe bzw. ich weiß nicht, wie ich es sonst konkret machen soll. Welche Methode statt OnError() sollte ich verwenden um auf die gesendeten Statuscodes reagieren zu können?
Per default Antwortet der Server bei einem Fehler und HTTP 500 einfach mit einer leeren Seite.
Da das einem Browsernutzer nichts bringt haben alle eine Custompage, WENN die Antwort leer ist.
Im IE wird eine default-Seite angezeigt, im Firefox nicht. Ich habe aber in der Zwischenzeit weiter recherchiert, und herausgefunden, dass die Antwort mindestens 512 Byte groß sein muss, damit IE nicht seine default-Seite anzeigt, sondern tatsächlich die Antwort.
Richtig Richtig Richtig wäre vermutlich alles verwerfen und ordentlich mit Standards arbeiten 😉
Du kennst das sicher auch. Zuerst ist es als Prototyp konzipiert, doch bevor auch nur eine Zeile Code geschrieben wurde, hat der Vertrieb es schon verkauft 😜
Ich bin noch relativ unerfahren in der Web-Entwicklung, fuchse mich aber langsam ein.
Momentan stehe ich vor einem Problem, dass ich trotz intensiver Recherche nicht lösen kann. Aber vielleicht hat ja jemand hier einen Tipp für mich.
Ich habe eine ASP.NET MVC Anwendung, die mittlerweile auch ganz gut funktioniert.
Nun habe ich mich mit dem Thema Fehlerbehandlung auseinander gesetzt.
In der Desktop-Welt, in der ich sonst zu Hause bin ist das relativ einfach. Man nutzt try&catch, dabei fügt man meistens eine MessageBox in den catch-Teil, die dem Benutzer das Problem mitteilt und beschreibt.
Nun ist das in der Web-Welt nicht ganz so einfach machbar. Ich habe zu Testzwecken an einer bestimmten Stelle im Code eine Exception eingebaut, um diesen Fall abfangen und testen zu können.
Meine Anwendung besteht grob aus drei Teilen; Kopf, Navigation und PDF-Dokumentenanzeige. Der Kopf ist dabei unwichtig, da statisch. In der Navigation werden Links zu PDF-Dokumenten in einem TreeView angezeigt (ähnlich einem Inhaltsverzeichnis). Klickt man auf einen Knoten im TreeView, sendet die Applikation eine Anfrage zu einem externen Webservice, der dann das gewünschte PDF-Dokument als Stream anbietet. Dieser Stream wird dann an den rechten Teil, der ein iframe ist, gesendet.
Definition des iframes:
<iframe frameborder="0" id="pdf_viewer" src=""></iframe>
Hier wird das Dokument angefordert:
function setupTree() {
$('#navtree').jstree({ 'plugins': ["wholerow"] });
$('#navtree').on('activate_node.jstree', function (e, data) {
$('#no_document').hide();
$('#pdf_viewer').show();
$('#pdf_viewer').attr("src", "@Url.Action("GetDocumentFile", "Home")/?documentId=" + data.node.id);
});
}
Hier ist die Metode, die dann das Dokument als Stream liefert. In einer der Methoden, die hier aufgerufen werden wird eine Exception geworfen:
public FileStreamResult GetDocumentFile(string documentId)
{
Stream stream = null;
string mimeType = string.Empty;
// Code für mycsharp nicht wichtig, daher entfernt
return File(stream, mimeType);
}
Hier wird die Exception serverseitig abgefangen:
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled) return;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.Result = View("_Error", filterContext.Exception);
}
Hier die _Error.cshtml:
@model System.Exception
<div class="error">
<div class="error_message">
@Model.Message
</div>
</div>
Wie man sieht, wird beim Firefox zumindest der Text meiner Exception angezeigt. Im IE wird nur der StatusCode angezeigt, sonst nichts.
Ich habe schon folgendes Versucht:
<iframe frameborder="0" id="pdf_viewer" src="" onerror="myErrorMethod()"></iframe>
(In myErrorMethod() war zu Testzwecken nur ein altert("blub") drin, wurde aber nicht ausgelöst)
2.
$('#pdf_viewer').onerror = function () { myErrorMethod};
Aber auch hier ist nichts passiert.
Nun meine Fragen:
Alles klar, danke.
Ich habe dazu keine konkreten und aussagekräftigen Informationen gefunden. Lediglich hier nur eine Anleitung, wie man die Variablen der Session zum Client übertragen kann, wenn man möchte.
Aber heißt das auch im Umkehrschluss, dass diese Variablen nicht automatisch immer zum Client übertragen werden?
Ich habe nämlich einige Informationen in der Session untergebracht, die aber nur innerhalb der Server-Umgebung relevant sind. Aus Performance-Gründen möchte ich nicht, dass diese Daten zum Client übertragen werden.
Ja, das ist mir bewusst. Es sollen in Zukunft eher interne Enterprise-Webapplikationen und nicht Webseiten, die im Internet erreichbar sein sollen, erstellt werden.
Danke schon mal, das hilft mir weiter.
Performance hängt hier stark von der Implementierung ab. Man muss ich halt bewusst sein, dass der Server bei MVC mehr zu tun hat als bei einer SPA. Der Client, egal ob Tablet/Handy/Desktop/Whatever ist halt mittlerweile brutal stark.
Den Gedanken hatte ich auch, und hatte die Befürchtung, dass z.B. Smartphones da Probleme machen könnten. Aber das hört sich ja gut an.
Wir kennen Deine Anforderungen nicht. Wenn Du das selbst nicht kannst, dann musst Du Dir evtl. jemand für 1-2 Tage ins Haus holen, der das kann oder Dich zumindest hier unterstützen kann.
Derzeit gibt es (noch) keine konkreten Anforderungen. Wir bauen momentan Know-How im Bereich MS Webentwicklung auf, bevor wir produktiv für Kunden entwickeln.
- Bei SPAs muss Dir klar sein, dass der Client aktuell JavaScript zwingend benötigt
Ja, ist es. Wie Coffeebean schon weiter oben sagte, da kommt man in der Webentwicklung kaum dran vorbei.
Du kannst auch nicht Spezialist A dahin setzen und Spezialist B an das Ding.
Das muss ein Team sein, das die Dinge gemeinsam löst.
Ja, so meinte ich das auch nicht. Mir ging es eher um die jeweiligen Skills der Entwickler. UI/UX und Design sind Wissenschaften für sich. Es wird sicher einfacher sein Spezialisten für diese bestimmten Bereiche zu finden, als Entwickler, die alles (also Front- und Backend) so gut können, wie die jeweiligen Spezialisten.
Grundsätzlich ist ein Team besser. Doch man hat die Möglichkeit einen Bereich bei Bedarf auszulagern. Wenn die WebAPI durchdacht und gut implementiert und dokumentiert ist, dann sollte ein guter Frontend-Entwickler auch ohne oder nur mit wenig Kontakt zum Backend-Entwickler klar kommen.
Lese ich richtig heraus, dass Du eher zu SPA's tendierst?
Für gewisse Fälle hat das sicher seine Vorzüge. Da muss man halt abwägen, was besser ist.
[...]
Was am Ende besser ist, kommt auf dich, dein Projekt, dein Kunden, dein Budget etc. an 😉
Das ist ja mein Problem. Ich möchte abwägen, weiß aber nicht, nach welchen Kriterien.
Die Trennung, die Du angesprochen hast, ist mir auch schon aufgefallen, und ich finde das grundsätzlich gut. So kann ich einen Kollegen, der Spezialist für JavaScript, CSS, HTML und UI/UX ist, an das Frontend setzen, während sich .NET-Entwickler an das Backend setzen. So habe ich "echte" Spezialisten an dem jeweiligen Ende. Natürlich wäre es gut, wenn der/die Entwickler alles richtig gut könnten, aber so geht es natürlich auch.
Gibt es noch technische Argumente für oder gegen das eine oder das andere? Also z.B. Sicherheit, Performance, Skalierbarkeit oder Funktionsumfang?
Ich habe mich jetzt einige Zeit mit ASP.NET MVC beschäftigt. Darüber bin ich auch über SPA's gestolpert. Nun frage ich mich was davon wann Sinn macht.
In ASP.NET MVC habe ich eine logische Trennung von Daten, Businesslogic und Präsentation. Der Controller holt sich das Model, verpackt es in eine View und schickt beides zum Client. Dabei kann man seine Seite relativ granular mit PartialViews zusammenbauen, die jeweils ihr eigenes Model haben.
Bei SPA's sendet der Controller eine Seite an den Client, und die einzelnen Komponenten der Seite kümmern sich dann selbstständig um das holen und Bereitstellen der Daten. Daten werden on demand per Ajax über entsprechende Methoden im Controller nachgeladen.
Vom Gefühl her würde ich jetzt sagen, dass der Hauptunterschied darin besteht, dass bei ASP.NET MVC viel auf dem Server gerendert wird, wohingegen bei SPA's das Meiste der Client übernimmt. Beides hätte seine Vor- und Nachteile. Trotzdem fällt es mir schwer sich für eine Technologie zu entscheiden.
Natürlich kann man auch Misch-Applikationen erstellen, aber dann stelle ich den Sinn des Einsatzes von MVC in Frage.
WIX kann das zB wie auch viele andere.
Das kommt als nächstes. Aber vorher muss erst mal ein xcopy-deployment funktionieren.
Damit eine Anwendung im IIS via ZIP importiert werden kann, muss das
> installiert sein.
Das hat geholfen, thx.
In VS2013 und höher wird die Zip durch die Veröffentlichung (Rechtsklick auf das ASP.NET Projekt -> Publish/Veröffentlichen) erstellt.
Ok, hab's gefunden. Und zwar unter der Option "Web Deploy-Paket". Der Name der Option hat mich irritiert. Unter Web Deploy stelle ich mir etwas anderes vor.
Nach langem hin und her habe ich es nun geschafft.
Danke.
Ich habe den Fall, dass ich hier eine APS.NET MVC 5 (.NET 4.5.1) und Visual Studio 2013 Premium Update 4 vorliegen habe.
Unsere Kunden haben alle ihre eigenen IIS in ihrem Intranet, an die wir aber von außen nicht so einfach dran kommen. Web deploy ist also nicht möglich.
Ich habe ein wenig recherchiert und das hier gefunden. Anscheinend ist das aber eine ältere Version des VS, denn in meinem finde ich diese Optionen (Erstellen einer zip-Datei) nicht. Auch im IIS finde ich keine Import-Funktion für die zip-Datei.
Wie sieht Microsoft es vor, wie man in einem solchen Fall deployt?
das kann man mit WiX machen. Nennt sich "Remember Property Pattern".
So, wie ich das verstanden habe, werden diese Werte in der Registry abgelegt, oder?
Funktioniert das auch, wenn man zwischen erster Installation und dem Upgrade die Werte per Texteditor in der Config ändert?
Suchst du vielleicht sowas?
>
Ja, ich denke aber, dass das die Lösung wird, wenn nichts anderes geht, weil das ja bedeutet, man muss alles "zu Fuß" machen.
Bei dem Vorgehen seh ich gar keine andere Möglichkeit als es manuell zu machen.
Schade. Meine Hoffnung war, dass das ein weit verbreitetes Problem ist und es daher eine einfache und allgemeine Lösung gibt. Es ist ja eher unrealtisisch, dass eine Config sich über zig Versionen einer Software nicht verändert/erweitert.
Ich hätte verschiedene Config für Auslieferungszustand und CustomConfiguration angelegt - dann gäbe es diese Situation nun nicht 😃
Wie das halt so ist, habe ich/wir das so "geerbt". Wobei ich nicht glaube, dass das Problem so lösbar wäre. Aber vielleicht verstehe ich dich auch nur falsch.
Beispiel:
Ich liefere MyProgram in der Version 1.0.0.0 aus. Dieses Programm benötigt den Zugriff auf eine Datenbank. Dafür werden bei der Installation der Software die Zugangsdaten zum Datenbankserver abgefragt und in der Config abgelegt.
Für die Version 2.0.0.0 von MyProgram benötigt das Programm nun zusätzlich die Kommunikation mit einem Web-Service. Um diesen Web-Service ansprechen zu können, benötigt MyProgram die Endpunkt-Adresse des Web-Services. Diese würde man bei der Installation der Version 2.0.0.0 von MyProgram eingeben, und die werden dann auch in der Config gespeichert.
Dafür muss man halt irgendwie die beim Kunden vorliegende Config aus der Version 1.0.0.0 so erweitern, dass die Daten aus 1.0.0.0 erhalten bleiben, und gleichzeitig die nötigen Elemente zum Speichern des Endpunkts eingefügt werden.
Ich habe hier einige Produkte, für die wir nach und nach WiX-Setups erstellen.
Jetzt ist die Frage aufgetaucht, wie man bei einem Update oder Upgrade der Software per Setup mit der vorleigenden app.config umgehen soll.
Situation:
Bei vielen unserer Produkte wurde eine app.config erstellt, in der z.B. ConnectionStrings, Endpoint-Adressen usw. abgelegt werden. Installiert der Kunde eines dieser Produkte, werden vom Installer Dialoge angezeigt, auf denen der Benutzer die notwendigen Daten eingeben kann. Der Installer schreibt diese dann in die Config.
Problem:
Diese Configs werden manchmal für weitere Versionen des Produkts erweitert. Diese Erweiterung soll bei den Kunden natürlich auch ankommen. Gleichzeitig sollen die bereits bestehenden Informationen in der Config des Kunden erhalten bleiben. Ein einfaches Sichern und Zurückspielen der Config reicht also nicht aus, da sonst die neuen Elemente nicht enthalten wären. Ein einfaches Ersetzen der Config würde die bisher vom Kunden erfassten Daten verwerfen.
Frage:
Gibt es da ein dafür schon irgendeine allgemeine Lösung, irgendeine Standard-Funktion, die genau für dieses Problem gedacht ist?
Oder muss ich das relativ aufwändig manuell in jedem Setup einzeln und quasi manuell behandeln?
Forms bestehen aus *.frm ud *.frx Dateien. Die frx-Dateien sind binär und enthalten leider Informationen, die nirgendwo anders stehen.
In Small guide to using VB6 with TFS 2012 habe ich einen netten Guide für TFS gefunden, allerdings nichts über Branches. Im TFS Branching Guide steht leider auch nichts dazu.
Ok, ich habe noch mal recherchiert.
Git wäre sehr wohl problematisch, da in VB6 die Forms in der Regel aus zwei Dateien bestehen, und eine davon ist binär. Die kann man nicht mergen.
In SVN ginge das, da man für diese eine Regel aufstellen könnte, mit der man diese Dateien exklusiv auscheckt.
Für den TFS gibt es auch ein PlugIn und einen Provider, mit dem die Anbindung VB6 zu TFS klappen würde. Wie das allerdings mit den Branches klappt, steht nirgendwo.
Im Prinzip nichts.
Ich meine mich aber erinnern zu können, dass das Mergen dort nicht so einfach war. Eine der wichtigeren Anforderungen ist nämlich, dass man Quellcode-Dateien nicht mehr exklusiv auscheken muss.
Wären mehr Lizenzen notwendig. Aber das ist nicht mein Bier.
Cloud-Lösung wird wohl aber nicht gefragt werden, da das Unternehmen seinen Quellcode ungern nach außen frei gibt.
Du hast dir Git anscheinend nicht genauer angesehen.
Ich habe da kaum Erfahrung, und Git ist nicht so einfach auf Anhieb zu verstehen. Werde das aber nächste Woche mit einem erfahrenen Git-Benutzer ausprobieren.
Ich nutze Perforce wie auch TFS/Git - bevorzuge aber allein aufgrund der Bedienung zweiteres.
Heißt das, dass Du auch VB6-Code im TFS verwaltest? Wenn ja, wie klappt das mit den Branches?
Derzeit nutzen wir Microsoft SourceSafe (Version 6) als Versionsverwaltung für unser Produkt. Das Produkt besteht aus einem Kern, der in VB6 programmiert ist, und aus diversen kleineren Projekten, die in VB6 und .NET programmiert sind.
Da SourceSafe alles andere als benutzerfreundlich und aktuell ist, sind wir auf der Suche nach einem neuen Versionsverwaltungssystem. Ich habe mir schon diverse Systeme angeschaut (darunter auch TFS, SVN und Git), allerdings waren alle nicht wirklich geeignet, da man damit den VB6-Code nicht wirklich gut verwalten kann.
SourceSafe hat noch den Vorteil, dass es eine Integration in die IDE für VB6 und ins Visual Studio gibt. Das ist aber nicht so das Problem. Das größte Problem ist, dass das Branchen von VB6-Code mit diesen Systemen nicht möglich ist. Zumindest nicht, ohne manuell Dateien hin und her zu kopieren.
Aber vielleicht hat je jemand eine ähnliche Erfahrung gemacht. VB6 ist ja durchaus noch weit verbreitet.
Ich habe das Problem, dass ich in der Entwicklungsumgebung (VS2010) auf Propertys eines VB6 COM-Objektes (ist ein fpSpread-Control) zugreifen kann. Starte ich das Programm normal (also nicht aus der IDE), dann ist das Objekt zwar da, aber beim Zugriff auf egal welche Property bekomme ich eine NotImplementedException.
Woran kann dieser Unterschied liegen?
Hier mein Adapter, mit dem ich auf die verschiedenen Propertys zugreife:
public class FFFpSpreadAdapter
{
private dynamic _oleFpSpread;
/// <summary>
/// Anzal der Zeilen in der Liste
/// </summary>
public int MaxRows
{
get
{
try
{
return _oleFpSpread.MaxRows;
}
catch (Exception)
{
return 0;
}
}
}
/// <summary>
/// Anzahl der Spalten in der Liste
/// </summary>
public int MaxCols
{
get
{
return _oleFpSpread.MaxCols;
}
}
/// <summary>
/// Anzahl der markirten Elemente in der Liste
/// </summary>
public int SelectionCount
{
get
{
return _oleFpSpread.SelectionCount;
}
}
/// <summary>
/// Aktuelle Zeile in der Liste
/// </summary>
public int ActiveRow
{
get
{
return _oleFpSpread.ActiveRow;
}
}
/// <summary>
/// Aktuelle Spalte in der Liste
/// </summary>
public int ActiveCol
{
get
{
return _oleFpSpread.ActiveCol;
}
}
/// <summary>
/// Ist ein Block markiert
/// </summary>
public bool IsBlockSelected
{
get
{
return _oleFpSpread.IsBlockSelected;
}
}
/// <summary>
/// Aktuelle Spalte (wird zum Iterieren durch die Liste benötigt)
/// </summary>
public int Col
{
get
{
return _oleFpSpread.Col;
}
set
{
_oleFpSpread.Col = value;
}
}
/// <summary>
/// Aktuelle Spalte (wird zum Iterieren durch die Liste benötigt)
/// </summary>
public int Col2
{
get
{
return _oleFpSpread.Col2;
}
set
{
_oleFpSpread.Col2 = value;
}
}
/// <summary>
/// Aktuelle Zeile (wird zum Iterieren durch die Liste benötigt)
/// </summary>
public int Row
{
get
{
return _oleFpSpread.Row;
}
set
{
_oleFpSpread.Row = value;
}
}
/// <summary>
/// Aktuelle Zeile (wird zum Iterieren durch die Liste benötigt)
/// </summary>
public int Row2
{
get
{
return _oleFpSpread.Row2;
}
set
{
_oleFpSpread.Row2 = value;
}
}
/// <summary>
/// Text der aktuellen
/// </summary>
public string Text
{
get
{
return _oleFpSpread.Text;
}
set
{
_oleFpSpread.Text = value;
}
}
/// <summary>
/// Konstrutor
/// </summary>
/// <param name="spread">fpSpread als Object</param>
public FFFpSpreadAdapter(Object spread)
{
_oleFpSpread = spread;
}
}
Ok, wegen der Fehlerbehandlung bin ich nun etwas schlauer. Man kann in der .NET-DLL einfach eine Exception werfen, und in der VB6-Anwendung ganz normal mit z.B. "On Error GoTo" abfangen. Der Code (Nummer) des Fehlers ist zwar nichtssagend, aber eine Fehler-Nachricht kann man so übermitteln.
Ich bin gerade dabei eine .NET-DLL (genauer gesagt in C#) zu erstellen, die man sowohl in einer .NET- wie auch in einer VB6-Anwendung nutzen soll.
Das Grundgerüst für die DLL habe ich bereits fertig, kann sie also von VB6 aus sehen und nutzen.
Nun muss ich zunächst mal wissen, welche Rückgabetypen ich in der DLL verwenden kann, die auch VB6 versteht. Ich meine, ich habe mal gelesen, dass alle primitiven Typen, Arrays mit primitiven Typen, Collections und selbst definierte Typen mit einem entsprechenden COM-Interface in der DLL nutzbar sind. Aber gehen auch z.B. RecordSets? Ich finde die Info aber nicht mehr im Netz.
Kann mir da jemand aushelfen?
Außerdem suche ich eine saubere Methode der Fehlerbehandlung. In der DLL würde ich ganz normal Exceptions nutzen. VB6 kennt das allerdings nicht, dort wird ja mit dem Err-Objekt gearbeitet. Wie wird das normalerweise in einem solchen Fall gehandhabt? Muss ich die Exception abfangen und die Info darin in das Err-Objekt umbiegen? Wenn ja, wie?
So weit ich das verstanden habe, bieten die aber nur CRUD-Aktionen, oder?
Danke für die Antworten.
Ich habe bisher auch eher zu WCF tendiert, wollte allerdings auch die andere Möglichkeit beleuchten.
Was genau ist für welchen Fall besser geeignet? (.NET FW ≥4.0)
Ich betreue gerade ein Projekt, bei dem eine REST-API auf eine bestehende Datenbank zugreifen soll. Diese REST-API soll später für verschiedene Zwecke verwendet werden. Smartphone-Apps, simple CRUD-Aktionen, Zugriffe von Webseiten/AJAX, Zugriffe von Fremdsoftware, Datenmigrationen, ...
Nun weiß ich nicht, welche Technologie die bessere Wahl wäre. Hat da jemand Erfahrung, und kann mir sagen, welche Technologie wofür besser geeignet ist?
Löschen funktioniert nur so lange, wie ich die Projektmappe nicht neu erstelle. dann sind die Dateien wieder da.
Hab aus Neugier in VS2008 mein Projekt "veröffentlicht". Nun befindet sich einige neue Dateien (z.B. *..application, *.manifest, ... und im Prokejt selbst app.config).
Wie kann ich das wieder rückgängig machen?