2 // DynamicDataRouteHandler.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Marek Habersack <mhabersack@novell.com>
8 // Copyright (C) 2008-2009 Novell Inc. http://novell.com
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Collections
;
33 using System
.Collections
.Generic
;
34 using System
.Collections
.Specialized
;
35 using System
.Data
.Linq
.Mapping
;
36 using System
.Globalization
;
37 using System
.Security
.Permissions
;
38 using System
.Security
.Principal
;
39 using System
.Threading
;
40 using System
.Web
.Caching
;
41 using System
.Web
.Compilation
;
42 using System
.Web
.Hosting
;
43 using System
.Web
.Routing
;
46 namespace System
.Web
.DynamicData
48 [AspNetHostingPermission (SecurityAction
.LinkDemand
, Level
= AspNetHostingPermissionLevel
.Minimal
)]
49 [AspNetHostingPermission (SecurityAction
.InheritanceDemand
, Level
= AspNetHostingPermissionLevel
.Minimal
)]
50 public class DynamicDataRouteHandler
: IRouteHandler
52 static ReaderWriterLockSlim contextsLock
= new ReaderWriterLockSlim ();
54 static Dictionary
<HttpContext
, RouteContext
> contexts
= new Dictionary
<HttpContext
, RouteContext
> ();
55 Dictionary
<RouteContext
, IHttpHandler
> handlers
;
57 Dictionary
<RouteContext
, IHttpHandler
> Handlers
{
60 handlers
= new Dictionary
<RouteContext
, IHttpHandler
> ();
66 static RouteContext
GetOrCreateRouteContext (HttpContext httpContext
)
68 RouteContext rc
= null;
71 contextsLock
.EnterReadLock ();
73 if (contexts
.TryGetValue (httpContext
, out rc
) && rc
!= null)
77 contextsLock
.ExitReadLock ();
82 contextsLock
.EnterWriteLock ();
84 rc
= MakeRouteContext (new RequestContext (new HttpContextWrapper (httpContext
), new RouteData ()), null, null, null);
85 contexts
.Add (httpContext
, rc
);
88 contextsLock
.ExitWriteLock ();
94 public static RequestContext
GetRequestContext (HttpContext httpContext
)
96 if (httpContext
== null)
97 throw new ArgumentNullException ("httpContext");
99 return GetOrCreateRouteContext (httpContext
).Context
;
102 public static MetaTable
GetRequestMetaTable (HttpContext httpContext
)
104 if (httpContext
== null)
105 throw new ArgumentNullException ("httpContext");
110 contextsLock
.EnterReadLock ();
112 if (contexts
.TryGetValue (httpContext
, out rc
) && rc
!= null)
116 contextsLock
.ExitReadLock ();
122 public static void SetRequestMetaTable (HttpContext httpContext
, MetaTable table
)
124 // And tradiationally... some .NET emulation code
125 if (httpContext
== null)
126 throw new NullReferenceException ();
128 GetOrCreateRouteContext (httpContext
).Table
= table
;
131 public DynamicDataRouteHandler ()
135 public MetaModel Model { get; internal set; }
137 [MonoTODO ("Needs a working test")]
138 public virtual IHttpHandler
CreateHandler (DynamicDataRoute route
, MetaTable table
, string action
)
140 // .NET bug emulation mode
141 if (route
== null || table
== null || action
== null)
142 throw new NullReferenceException ();
144 // NOTE: all code below is a result of guessing as no tests succeed for this
147 IHttpHandler ret
= null;
149 // Give custom pages a chance
150 string viewName
= String
.IsNullOrEmpty (action
) ? route
.ViewName
: action
;
151 string path
= GetCustomPageVirtualPath (table
, viewName
);
153 // Pages might be in app resources, need to use a VPP
154 VirtualPathProvider vpp
= HostingEnvironment
.VirtualPathProvider
;
156 if (vpp
!= null && vpp
.FileExists (path
))
157 ret
= BuildManager
.CreateInstanceFromVirtualPath (path
, typeof (Page
)) as IHttpHandler
;
162 path
= GetScaffoldPageVirtualPath (table
, viewName
);
163 if (vpp
!= null && vpp
.FileExists (path
))
164 ret
= BuildManager
.CreateInstanceFromVirtualPath (path
, typeof (Page
)) as IHttpHandler
;
169 protected virtual string GetCustomPageVirtualPath (MetaTable table
, string viewName
)
171 // No such checks are made in .NET, we won't follow the pattern...
172 MetaModel model
= Model
;
173 if (table
== null || model
== null)
174 throw new NullReferenceException (); // yuck
176 // Believe it or not, this is what .NET does - pass a null/empty viewName
177 // and you get /.aspx at the end...
178 return model
.DynamicDataFolderVirtualPath
+ "CustomPages/" + table
.Name
+ "/" + viewName
+ ".aspx";
181 protected virtual string GetScaffoldPageVirtualPath (MetaTable table
, string viewName
)
183 // No such checks are made in .NET, we won't follow the pattern...
184 MetaModel model
= Model
;
185 if (table
== null || model
== null)
186 throw new NullReferenceException (); // yuck
188 // Believe it or not, this is what .NET does - pass a null/empty viewName
189 // and you get /.aspx at the end...
190 return model
.DynamicDataFolderVirtualPath
+ "PageTemplates/" + viewName
+ ".aspx";
193 IHttpHandler IRouteHandler
.GetHttpHandler (RequestContext requestContext
)
195 if (requestContext
== null)
196 throw new ArgumentNullException ("requestContext");
197 RouteData rd
= requestContext
.RouteData
;
198 var dr
= rd
.Route
as DynamicDataRoute
;
200 throw new ArgumentException ("The argument RequestContext does not have DynamicDataRoute in its RouteData");
201 string action
= dr
.GetActionFromRouteData (rd
);
202 MetaTable mt
= dr
.GetTableFromRouteData (rd
);
203 RouteContext rc
= MakeRouteContext (requestContext
, dr
, action
, mt
);
206 Dictionary
<RouteContext
, IHttpHandler
> handlers
= Handlers
;
207 if (handlers
.TryGetValue (rc
, out h
))
209 h
= CreateHandler (dr
, mt
, action
);
210 handlers
.Add (rc
, h
);
214 static RouteContext
MakeRouteContext (RequestContext context
, DynamicDataRoute route
, string action
, MetaTable table
)
219 rd
= context
.RouteData
;
220 route
= rd
.Route
as DynamicDataRoute
;
224 if (action
== null) {
226 rd
= context
.RouteData
;
227 action
= route
.GetActionFromRouteData (rd
);
232 rd
= context
.RouteData
;
234 table
= route
.GetTableFromRouteData (rd
);
238 return new RouteContext () {
245 sealed class RouteContext
247 public DynamicDataRoute Route
;
248 public string Action
;
249 public MetaTable Table
;
250 public RequestContext Context
;
252 public RouteContext ()
256 public override bool Equals (object obj
)
258 RouteContext other
= obj
as RouteContext
;
259 return other
.Route
== Route
& other
.Action
== Action
&& other
.Table
== Table
&& other
.Context
== Context
;
262 public override int GetHashCode ()
264 return (Route
!= null ? Route
.GetHashCode () << 27 : 0) +
265 (Action
!= null ? Action
.GetHashCode () << 19 : 0) +
266 (Table
!= null ? Table
.GetHashCode () << 9 : 0) +
267 (Context
!= null ? Context
.GetHashCode () : 0);