Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

c# ASP.NET Dependency Injection RouteConfig

Pagina: 1
Acties:

  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
Beste,

Ik ben op dit moment bezig met het ontwikkelen van een Content Management Systeem op maat. Ik maak gebruik van verschillende technieken als Dependency Injection van Ninject, Entity Framework, Code First etc.

Ik probeer dus dynamische url's op te halen met behulp van Ninject. Dit is gelukt tot op een zekere hoogte.
Zodra de pagina ongeveer 20 request krijgt komt er een melding dat de database disposed is. Maar nog wel benodigd is voor de query. Nu vermoed ik dat het komt door de scope, gezien ik

code:
1
DependencyResolver.Current.GetService


gebruik voor het ophalen van de service. (injection via de contructor in de routeconfig is niet mogelijk).

Ik zit nu een tijdje te zoeken naar oplossingen zonder resultaat.

Wellicht dat iemand van jullie mij in de juiste richting kan duwen?

Mijn code is als volgt

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class RouteConfig
    {
        /// <summary>
        /// The register routes.
        /// </summary>
        /// <param name="routes">
        /// The routes.
        /// </param>
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "CmsRoute",
                url: "{*permalink}",
                defaults: new { controller = "Page", action = "Index" },
                constraints: new { permalink = DependencyResolver.Current.GetService(typeof(IContentPageConstraints)) });

            routes.MapRoute(
                name: "Default",
                url: "{area}/{controller}/{action}/{id}",
                defaults: new { area = "Management", controller = "Home", action = "Index", id = UrlParameter.Optional },
                namespaces: new[] { "NAMESPACE.Web.Areas.Management.Controllers" });
        }
    }


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 public class ContentPageConstraints : IRouteConstraint, IContentPageConstraints
    {
        /// <summary>
        /// The context.
        /// </summary>
        private readonly IMyDbContext context;

        /// <summary>
        /// Initializes a new instance of the <see cref="ContentPageConstraints"/> class.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        public ContentPageConstraints(IMyDbContext context)
        {
            this.context = context;
        }

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (values[parameterName] != null)
            {
                var permalink = values[parameterName].ToString();
                return this.context.ContentPages.Any(p => p.Url == permalink);
            }

            return false;
        }
    }


Ik hoop dat iemand mij in de juiste richting kan duwen om het probleem op te lossen.

Alvast bedankt voor het meedenken.

Met vriendelijke groet,

Kevin.

[ Voor 0% gewijzigd door RobIII op 30-05-2014 17:33 . Reden: code-highlighting toegepast (d.m.v. [code=c#]) ]


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
Dat heeft an sich niks te maken met hoe je de DependancyResolver gebruikt. Het heeft te maken met de plek waar je het gebruikt.

Volgens mij wordt namelijk voor iedere request die je doet, je constraint variabele opnieuw geinstantieerd. Wat dus betekend dat je voor iedere request opnieuw je IContentPageConstraints resolved, wat een nieuwe database connectie ophaalt.

Alleen vanwege de plek in de request pipeline, betwijfel ik of je dependency dan wel word gedisposed. Ik denk het niet, aangezien je feitelijk een foutmelding krijgt welke zegt dat je connectionpool vol is.

Ik zou ook niet op deze manier de routing van je CMS pagina's oplossen. Ik zou kijken naar een custom virtual path provider, en of een eigen routehandler.

Er zijn legio open source mvc CMS frameworks. Zoek ze op, en kijk hoe zij de routing en servering van hun CMS pagina's hebben opgelost.

Ik kan je alvast verklappen dat als je meer wilt dan het meest simpele CMS je een flinke uitdaging voor de boeg hebt.

  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
Puur uit nieuwsgierigheid. Waarom zou hij hem niet meer kunnen disposen, naar virtuele path providers heb ik al naar gekeken, maar nog niks geprobeerd. Wellicht dat ik daar naar kan kijken en een betere oplossing vinden.

Ook ben ik nieuwsgierig waar je de uitdaging in ziet? Ik kan me daarop echter beter voorbereiden uiteraard ;-)

[ Voor 4% gewijzigd door kevinkrs op 30-05-2014 20:27 ]


  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
D-Raven schreef op vrijdag 30 mei 2014 @ 19:54:
Dat heeft an sich niks te maken met hoe je de DependancyResolver gebruikt. Het heeft te maken met de plek waar je het gebruikt.

Volgens mij wordt namelijk voor iedere request die je doet, je constraint variabele opnieuw geinstantieerd. Wat dus betekend dat je voor iedere request opnieuw je IContentPageConstraints resolved, wat een nieuwe database connectie ophaalt.
Nope. Constraints worden eenmalig aangemaakt. Je geeft een object met voorgemaakte constraint instances mee aan de constraints parameter van de route.
D-Raven schreef op vrijdag 30 mei 2014 @ 19:54:
Alleen vanwege de plek in de request pipeline, betwijfel ik of je dependency dan wel word gedisposed. Ik denk het niet, aangezien je feitelijk een foutmelding krijgt welke zegt dat je connectionpool vol is.
Of de dependency gedisposed wordt of niet is, in elk geval in Unity, afhankelijk van de lifetime manager en de levensduur van de injection container zelf. Dat heeft bar weinig te maken met de plek in de request pipeline.
D-Raven schreef op vrijdag 30 mei 2014 @ 19:54:
Ik zou ook niet op deze manier de routing van je CMS pagina's oplossen. Ik zou kijken naar een custom virtual path provider, en of een eigen routehandler.
Dit los je in elk geval niet met virtual path providers op. Die zijn bedoeld om statische of dynamische content van een geabstraheerd file system te trekken, niet om routering naar bepaalde pagina's te leggen.

De IRouteConstraint.Match method is de juiste plek om op te zoeken of een bepaald permalink token gematched kan worden aan data voor de opbouw van een content pagina, en de constraint dus gematched wordt.

Op dat moment kun je de route value dictionary ook nog bijwerken en die data er in zetten als extra route value. Of je kunt, als je dat wilt, de origineel uit de URL gelezen route value overschrijven. Als je de interne logica bekijkt van de System.Web.Routing.Route class kom je er achter dat het scenario waar een route constraint de collectie route values muteert, expliciet ondersteunt wordt.

Wel fijn, want dan hoef je slechts één keer de database te queryen. (En je komt niet met rariteiten te zitten als contexts die verlopen, usw.)

  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
Ik heb nog wat verder getest. Het lijkt echt mis te gaan bij de database. Zodra in een XML bestand uitlees met URL paden lijkt het wel te werken. Zou dit een oplossing kunnen zijn?

Het liefst uiteraard rechtstreeks uit de database want dan heb je maar 1 bestand met URL's, en niet dat je elke keer de database moet synchroniseren met je XML.

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
R4gnax schreef op vrijdag 30 mei 2014 @ 22:30:
[...]

Nope. Constraints worden eenmalig aangemaakt. Je geeft een object met voorgemaakte constraint instances mee aan de constraints parameter van de route.
Je hebt gelijk ik vergiste me hierin. Ik dacht dat deze bij iedere evaluatie opnieuw geinstantieerd werden.
[...]

Of de dependency gedisposed wordt of niet is, in elk geval in Unity, afhankelijk van de lifetime manager en de levensduur van de injection container zelf. Dat heeft bar weinig te maken met de plek in de request pipeline.
Ik verwoorde het verkeerd. Maar de rest is sowieso onzin aangezien mijn eerste veronderstelling al niet klopt.

Al heb ik het vermoeden dat zn database connectie onder zn voeten vandaan wordt getrokken op een of andere manier, en dat hij daarom die foutmelding krijgt.

Zou het kunnen dat de TS zn dbcontext gescoped heeft per HttpRequest?
[...]

Dit los je in elk geval niet met virtual path providers op. Die zijn bedoeld om statische of dynamische content van een geabstraheerd file system te trekken, niet om routering naar bepaalde pagina's te leggen.
Klopt, waarom ik dit voorstelde weet ik zo ook niet, het heeft in ieder geval niks met zijn probleem te maken.

Let wel op dat voor iedere request, iedere keer weer je routecollectie ge-evalueerd wordt, met een beetje pech (afhankelijk van de plaats van je page-route in je collectie) je iedere keer een database call gaat doen. Dit wil je op zn minst cachen.
Of op een andere manier oplossen, bv dmv een statische lijst welke telkens geraadpleegd wordt, en door je CMS up to date gehouden wordt, welke bij het opstarten geinitialiseerd wordt vanuit je database.

Mijn vorige reactie was duidelijk een gevalletje: 3x lezen voordat je post.

  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
Ik citeer "Zou het kunnen dat de TS zn dbcontext gescoped heeft per HttpRequest?"

Ik heb mijn dbcontext inderdaad op "InRequestScope" staan. Dit heb ik gedaan zodat als waardes in de database geupdate worden ze meteen meegenomen worden tevens heeft "Singleton" bij veel data wat performance problemen. Maar dit zou toch geen problemen moeten veroorzaken omdat ze in de zelfde request staan?

Ik hoor hier ook een oplossing met een statisch lijst bijhouden. Ik neem aan dat dit ook mte een statisch XML bestand kan?

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
kevinkrs schreef op zondag 01 juni 2014 @ 14:47:
Ik citeer "Zou het kunnen dat de TS zn dbcontext gescoped heeft per HttpRequest?"

Ik heb mijn dbcontext inderdaad op "InRequestScope" staan. Dit heb ik gedaan zodat als waardes in de database geupdate worden ze meteen meegenomen worden tevens heeft "Singleton" bij veel data wat performance problemen. Maar dit zou toch geen problemen moeten veroorzaken omdat ze in de zelfde request staan?
Natuurlijk, en dit is normaliter ook de manier waarop je het doet. Alleen het verschil is dat je routeconstraint buiten een normale httprequest om aangemaakt word. Tenminste, vanuitgaande dat je de RegisterRoutes aanroept in de application_start.

Ik gebruik NInject niet, en weet dus niet precies hoe die container zich gedraagt, maar ik heb het vermoeden dat de database connectie welke de dbcontext in je IContentPageConstraints gebruikt. Gedisposed wordt door je NInject container, aan het einde van een request. Hierdoor zou je wellicht het verschijnsel hebben dat het de eerste keer goed gaat, en daarna niet meer.

Maar hier kun je vrij snel achterkomen door je dbcontext met een debugger te inspecteren.
Ik hoor hier ook een oplossing met een statisch lijst bijhouden. Ik neem aan dat dit ook mte een statisch XML bestand kan?
Ja sure kan dat, maar wil je dat ook? Mij lijkt het veel praktischer om een ConcurrentDictionary te gebruiken oid.

[ Voor 4% gewijzigd door D-Raven op 01-06-2014 17:29 ]


  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
ConcurrentDictionary zou een oplossing zijn. Maar komen we dan niet weer terug op het punt dat er een database verbinding nodig is waardoor er problemen ontstaan?

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
Probeer eerst eens te achterhalen waarom je nou precies die foutmelding krijgt.

Zelfs al gebruik je geen in-memory lijst. Kun je altijd nog dit doen:

C#:
1
2
3
4
5
6
7
8
9
10
11
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (values[parameterName] != null)
            {
var myRouteService = DependancyResolver.Current.GetService<ICmsRouteCacheWhatever>();
                var permalink = values[parameterName].ToString();
                return myRouteService.RouteExists(permalink);
            }

            return false;
        } 

  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
Afbeeldingslocatie: http://www.dotnet-tricks.com/Content/images/mvc/ASP.NETMVC5Pipeline.png

Ik zat te kijken naar de afbeelding hierboven. Het lijkt er inderdaad op dat de routing op een andere pipeline staat dan de httprequest. Dit is de reden waarom er geen database verbinding opgezet kan worden met Depenendency Injection.

Dat zou betekenen dat ik een fysiek lijst moet bijhouden met de URL's om een connectie te voorkomen?

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
Jou plaatje hierboven zegt helemala niks over de HttpRequest. Het zegt alleen hoe de logische componenten in het framework op elkaar inhaken en bij elkaar horen.

Waarom denk je dat ik in mn voorbeeld voor de match functie handmatig de DependancyResolver aanroep? Dit is omdat deze functie wordt uitgevoerd als onderdeel van een httprequest.

Overigens wordt alles wat je in je afbeelding ziet, uitgevoerd binnen de scope van 1 httprequest.

  • kevinkrs
  • Registratie: Juni 2010
  • Laatst online: 21-11 11:52
Sorry ik had even over de functie heen gekeken. Maar je hebt gelijk, ik krijg op dit moment geen foutmeldingen meer ook al refresh ik de pagina (f5 een aantal minuten ingedrukt houden)

Het lijkt stabiel te werken. Maar nu begrijp ik ook wat je bedoeld :)
Moet het nog wel even omzetten naar nette code. Dit was voor mij een enorme leerervaring over Dependency Injection en Routeconfig.

Heel erg bedankt voor jullie moeite jongens!
Pagina: 1