Using CEFSharp ILifeSpanHandler interface to Handle Popups - cefsharp

I have an issue with handling popups. I have implemented ILifeSpanHandler and OnBeforeBrowse (amoungst others) from the IRequestHandler.
How do I know in the ILifeSpanHandler what URL is being called? I am unable to get it in either the OnAfterCreated or OnBeforePopup. Currently I see it first in OnBeforeBrowse.
I have no code as my question is a "How to". In OnBeforePopup I have checked targetUrl however it seems to be there for decoration as I have read that it is not implemented anyway. I have also looked at the browner/chromiumWebBrowser objects, browser and newBroswer seem to be nothing. One would expect in OnAfterCreated chromiumWebBrowser would return an object but it is nothing in my case.
I am testing with the following
Public Sub OnAfterCreated(chromiumWebBrowser As IWebBrowser, browser As IBrowser) Implements ILifeSpanHandler.OnAfterCreated
Try
Debug.Print(vbNewLine)
Debug.Print("OnAfterCreated")
Debug.Print(String.Concat("OnAfterCreated - MainFrame.Url "), browser.MainFrame.Url)
Debug.Print("OnAfterCreated")
Debug.Print(vbNewLine)
Catch ex As Exception
End Try
End Sub
And I have the following
Public Function OnBeforePopup(chromiumWebBrowser As IWebBrowser, browser As IBrowser, frame As IFrame, targetUrl As String, targetFrameName As String, targetDisposition As WindowOpenDisposition, userGesture As Boolean, popupFeatures As IPopupFeatures, windowInfo As IWindowInfo, browserSettings As IBrowserSettings, ByRef noJavascriptAccess As Boolean, ByRef newBrowser As IWebBrowser) As Boolean Implements ILifeSpanHandler.OnBeforePopup
Try
Debug.Print(vbNewLine)
Debug.Print("OnBeforePopup")
Debug.Print(String.Concat("OnBeforePopup - targetUrl "), targetUrl)
Debug.Print(String.Concat("OnBeforePopup - browser.MainFrame.Url "), browser.MainFrame.Url)
Debug.Print(String.Concat("OnBeforePopup - chromiumWebBrowser.Address "), chromiumWebBrowser.Address)
Debug.Print("OnBeforePopup")
Debug.Print(vbNewLine)
Catch ex As Exception
End Try
Return False
End Function

I have seen different approaches in handling popups using ILifeSpanHandler interface. One approach that I've seen also here in Stack Overflow and was accepted as the correct answer to that particular question is to return true in the OnBeforePopup implementation of ILifeSpanHandler then pass the targetURL argument to a handler that creates the popup.
This approach is very unideal because you are destroying the link between the page that actually opened the popup and the popup itself. If you access via JavaScript the opener property of the window object inside the popup you would notice that it is null. And the page that opened the popup would never know that the popup was actually opened because returning true cancels the creation.
The other approach is to let Cef create the popup and the programmer just decides whether to show the browser as a popup window or a child to control (like in tabbed browsing). This is error-free and almost ideal. But the problem with this approach is that you are not able to listen to events such as FrameLoadStart, FrameLoadEnd, AddressChanged, TitleChanged, etc.
One approach that is tagged experimental by the Cef developers is to return a new IWebBrowser instance via newWebBrowser out parameter. This has so many many side effects. The page that opened the popup would, of course, recognize the popup as his although it was not the original browser (IBrowser) that it created. The page may just ignore it like btcclicks.com and in that case, there'd be no problem. But there are websites like drops.xyz that is so particular with his stuff and will discard everything that is not originally his. So this is a problem.
So what is the correct approach?
The ChromeWebBrowser control
Now I'm going to share with you an undocumented approach in handling popups. Speaking of ChromeWebBrowser control, it is very much of help that we know how it creates the webbrowser which, in reality, it doesn't. The control just hosts the webbrowser window handle. It has a private field called managedCefBrowserAdapter (ManagedCefBrowserAdapter class) that handles the actual creation of the web browser. ChromiumWEbBrowser implements the IWebBrowserInternal that has a method OnAfterBrowserCreated with a single parameter of type IBrowser. The control then invokes browser.GetHost().GetWindowHandle() to get the actual window handle (HWND) of the webbrowser it is being hosted. It is quite good.
The problem of the ChromeWebBrowser is that it won't have a constructor that accepts an IBrowser as an argument. It only has constructor that accepts HtmlString, string and IRequestContext arguments. These control waits for the
invocation of OnHandleCreated (a base class override) where it calls the managedCefBrowserAdapter.CreateBrowser after which it waits till its implementation of IWebBrowserInternal's OnAfterBrowserCreated is invoked.
Again, what is the approach that works?
Now, this approach that actually works is a product of long series of trial and error. One caution though is that I don't know why and how it works but I know it works.
First, I did not use ChromeWebBrowser. But I copied its code omitting the part where it creates .net control. In this case, I am targeting the browser's window handle (HWND) to be host by any object that exposes a HWND. Obviously I created a class (NativeCefWebBrowser) that uses the modified code. The ChromeWebBrowser orignal constructors were still there untouched becuase they are used to the create the parent webrowser. But I added one constructor that accept the following arguments: ICefBrowserParent parent (an interface I've created and IBrowser browser that receives the browser argument in the ILifeSpanHandler's OnBeforePopup. I also added a public method AttachBrowser that has a single parameter IBrowser that recieves the IBrowser argument in the ILifeSpanHandler's OnAfterCreated. It the browser that will be kept by CefNativeWebBrowser class.
Why didn't I keep the browser instance received form ILifeSpanHandler.OnBeforePopup but used the instance received from ILifeSpanHandler.OnAfterCreated when they are the same browser instance? This is one of those parts that I don't know why. One thing I noticed is that when I called browser.GetHost().GetWindowHandle() during ILiffeSpanHandler.OnBeforePopup, the first window handle I received was the different compared to when I invoked the method during ILifeSpanHandler.OnAfterCreatd. Because of that, I store the browser instance from the latter that I passed to the NativeCefWebBrowser.AttachBrowser for its safekeeping.
In the NativeCefWebBrowser(ICefBrowserParent parent, IBrowser browser) contructor, I set the private following fields to true: browsercreated, browserinitialized (chromewebbrwoser orginal fields) and isAttachingBrowser (added private field). You don't call the ManagedCefBrowserAdapter's CreateBrowser in this contructor in instead call its OnAfterBrowserCreated passing the browser instance. You don't much in this constructor as you will wait the ILifeSpanHandler implementor to pass you the browser instance it will receive during its OnAfterCreated method. Take note that when calling the ManagedCefBrowserAdapter's OnAfterBrowserCreated method, ManagedCefBrowserAdapter will still invoke IWebBrowserInternal implementation of OnAfterBrowserCreated that when happens you have to exit immediately when isAttachingBrowser is true as the following code will no sense no more.
After calling the NativeCefWebBrowser(ICefBrowserParent, IBroser) construct, you can normally set event listeners as you will normally do.
And that's it.
The following are parts of the code that I wrote
The ICefBrowserParent interface
public interface ICefBrowserParent
{
IntPtr Handle { get; }
Size ClientSize { get; }
bool Disposing { get; }
bool IsDisposed { get; }
bool InvokeRequired { get; }
IAsyncResult BeginInvoke(Delegate d);
object Invoke(Delegate d);
event EventHandler Resize;
}
As you would notice, the methods, properties and events in this interface are already implemented by the System.Windowns.Forms.Control class. So if you implementing this from class inhering Control class, you would not need to implement this anymore. This interface is only for non-Control class.
class NativeCefWebBrowser
{
public NativeCefWebBrowser(ICefBrowserParent, IBroser)
{
requestContext = browser.GetHost().RequestContext;
this.parent = parent; // added field
HasParent = true; // IWebBrowserInternal. I don't know what's this for
mustSetBounds = true; // added field
browserCreated = true;
isAttachingBrowser = true; // added field
InitializeFieldsAndCefIfRequired();
managedCefBrowserAdapter.OnAfterBrowserCreated(browser);
}
}
ILifeSpanHandler.OnBeforePopup(..., out IWebBrowser newWebBrowser)
{
CefNativeWebBrowser b = new CefNativeWebBrowser
(
parent, // defined else where
browser
);
// Attach event handlers
b.TitleChanged...;
newWebBrowser = b;
}
ILifeSpanHandler.OnAfterCreated(...)
{
((CefNativeWebBrowser)webBrowser).AttachBrowser(browser);
}

Related

How to send stream from ViewModel to the MediaElement on the XAML page?

Just need to speak a text string from the ViewModel (inherited from MVVM Light ViewModelBase) to the MediaElement on the XAML page.
var synthesisStream = await synthesizer.SynthesizeSsmlToStreamAsync(text);
media.AutoPlay = true;
media.SetSource(synthesisStream, synthesisStream.ContentType);
media.Play();
The code above has no separation of ViewModel. We see media is directly handled in code-behind.
In my ViewModel, I stopped at
var synthesisStream = await synthesizer.SynthesizeSsmlToStreamAsync(text);
var msg=new PlaySpeechSynthesisStreamMessage(synthesisStream);
Messenger.Default.Send<PlaySpeechSynthesisStreamMessage>(msg);
For the message:
public class PlaySpeechSynthesisStreamMessage
{
public SpeechSynthesisStream Stream { get; set; }
public PlaySpeechSynthesisStreamMessage(SpeechSynthesisStream stream)
{
Stream = stream;
}
}
Is Messenger the right way to handle this situation? How can we write a RelayCommand or something to pass the stream to media?
A related article MVVM pattern violation: MediaElement.Play() seems to address this issue, but it is not in MVVM Light and there is no way to pass the stream, either.
I think a message is a good solution to handle this kind of situation.
You just have to complement the sending of the message in the ViewModel with the handling of it in the View:
Messenger.Default.Register<PlaySpeechSynthesisStreamMessage>(this, msg => {
media.AutoPlay = true;
media.SetSource(msg.Stream, msg.Stream.ContentType);
media.Play();
});
Alternatively, you can use the event approach described in the question you cited. In this case you will have to define a class that inherits from EventArgs with a property of type SpeechSynthesisStream, then define your event as follow:
public event EventHandler<YourEventArgsClass> PlaySpeechSynthesisStreamEvent;
and raise it this way:
var synthesisStream = await synthesizer.SynthesizeSsmlToStreamAsync(text);
var eventArgs = new YourEventArgsClass(synthesisStream);
if (PlaySpeechSynthesisStreamEvent != null)
PlaySpeechSynthesisStreamEvent(this, eventArgs);
In this case of course you will have to handle the event in the View.
I find the solution with the event handler a little bit trickier than the one with messages, because you will have to wire the event handling to the DataContext of the View and, depending of how the application is structured, the DataContext property of the View could not always be available from the beginning of the View lifetime: for example, in many cases I tend to set it via a Navigation Service and / or Bootstrapper during the navigation to the view: in this case, DataContext is null in the costructor of the View so it is impossible to wire the event handler there. So, you have to find another place to wire it, remembering that methods such OnNavigatedFrom (e.g., in Windows 10 UWP apps) can be called more than once in the life cycle of the view and surely we don't want to wire the event handler more than once.
If the framework exposes it (such in Windows 10 UWP), the DataContextChanged event could be a good place to wire event handlers related to the ViewModel (and possibly to remove previous ones, if an instance of a View can be used with different instance of the ViewModel class during the lifetime of the application).

Solution for asynchronous notification upon future completion in GridGain needed

We are evaluating Grid Gain 6.5.5 at the moment as a potential solution for distribution of compute jobs over a grid.
The problem we are facing at the moment is a lack of a suitable asynchronous notification mechanism that will notify the sender asynchronously upon job completion (or future completion).
The prototype architecture is relatively simple and the core issue is presented in the pseudo code below (the full code cannot be published due to an NDA). *** Important - the code represents only the "problem", the possible solution in question is described in the text at the bottom together with the question.
//will be used as an entry point to the grid for each client that will submit jobs to the grid
public class GridClient{
//client node for submission that will be reused
private static Grid gNode = GridGain.start("config xml file goes here");
//provides the functionality of submitting multiple jobs to the grid for calculation
public int sendJobs2Grid(GridJob[] jobs){
Collection<GridCallable<GridJobOutput>> calls = new ArrayList<>();
for (final GridJob job : jobs) {
calls.add(new GridCallable<GridJobOutput>() {
#Override public GridJobOutput call() throws Exception {
GridJobOutput result = job.process();
return result;
}
});
}
GridFuture<Collection<GridJobOutput>> fut = this.gNode.compute().call(calls);
fut.listenAsync(new GridInClosure<GridFuture<Collection<GridJobOutput>>>(){
#Override public void apply(GridFuture<Collection<GridJobOutput>> jobsOutputCollection) {
Collection<GridJobOutput> jobsOutput;
try {
jobsOutput = jobsOutputCollection.get();
for(GridJobOutput currResult: jobsOutput){
//do something with the current job output BUT CANNOT call jobFinished(GridJobOutput out) method
//of sendJobs2Grid class here
}
} catch (GridException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
return calls.size();
}
//This function should be invoked asynchronously when the GridFuture is
//will invoke some processing/aggregation of the result for each submitted job
public void jobFinished(GridJobOutput out) {}
}
}
//represents a job type that is to be submitted to the grid
public class GridJob{
public GridJobOutput process(){}
}
Description:
The idea is that a GridClient instance will be used to in order to submit a list/array of jobs to the grid, notify the sender how many jobs were submitted and when the jobs are finished (asynchronously) is will perform some processing of the results. For the results processing part the "GridClient.jobFinished(GridJobOutput out)" method should be invoked.
Now getting to question at hand, we are aware of the GridInClosure interface that can be used with "GridFuture.listenAsync(GridInClosure> lsnr)"
in order to register a future listener.
The problem (if my understanding is correct) is that it is a good and pretty straightforward solution in case the result of the future is to be "processed" by code that is within the scope of the given GridInClosure. In our case we need to use the "GridClient.jobFinished(GridJobOutput out)" which is out of the scope.
Due to the fact that GridInClosure has a single argument R and it has to be of the same type as of GridFuture result it seems impossible to use this approach in a straightforward manner.
If I got it right till now then in order to use "GridFuture.listenAsync(..)" aproach the following has to be done:
GridClient will have to implement an interface granting access to the "jobFinished(..)" method let's name it GridJobFinishedListener.
GridJob will have to be "wrapped" in new class in order to have an additional property of GridJobFinishedListener type.
GridJobOutput will have to be "wrapped" in new class in order to have an addtional property of GridJobFinishedListener type.
When the GridJob will be done in addition to the "standard" result GridJobOutput will contain the corresponding GridJobFinishedListener reference.
Given the above modifications now GridInClosure can be used now and in the apply(GridJobOutput) method it will be possible to call the GridClient.jobFinished(GridJobOutput out) method through the GridJobFinishedListener interface.
So if till now I got it all right it seems a bit clumsy work around so I hope I have missed something and there is a much better way to handle this relatively simple case of asynchronous call back.
Looking forward to any helpful feedback, thanks a lot in advance.
Your code looks correct and I don't see any problems in calling jobFinished method from the future listener closure. You declared it as an anonymous class which always has a reference to the external class (GridClient in your case), therefore you have access to all variables and methods of GridClient instance.

Notifying that all properties have changed on a ViewModel

I am working on a Silverlight application using V3 SP1 of MVVM Light Toolkit.
My application is fully French/English. All UI elements (buttons, labels, etc.) and all the data (models). I need dynamic language switching and this is fully implemented and works with anything coming from a resource file. What I am struggling with is the ViewModels.
The Models have language specific prperties (DescriptionEn, DescriptionFr) and an additional property call LocalizedDescription which uses the current culture to return call the language specific property.
When the language changes (via a button click) I raise and broadcast (via the Messenger) a property changed event.
In each of my ViewModels, I register to receive the property changed message for the language swap.
I want to notify all the properties of the ViewModel that something has changed.
From: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
However, since the toolkit abstracts the raising of the changed event with RaisePropertyChanged(...) I cannot get this to work. I have also examined the source of the tookit and discovered that RaisePropertyChanged calls VerifyPropertyName(..) which in turn returns an error is the property does not belong to the ViewModel. I also noticed that the VerifyPropertyName method is attributed with Conditional("DEBUG"), but even if I choose the Release configuration, the ArgumentException("Property not found") is still raised.
Does anyone know of a way to get this to work using the toolkit aside from manually calling RaisePropertyChanged for every property of the ViewModel?
Follow-up:
Based on the comment from Simon, I attempted to create my own class that extends ViewModelBase. I looked at the source on CodePlex and decided to create a single method called RaiseAllPropertyChanged(). It would simply be a copy of the RaisePropertyChanged(string propertyName) but without the parameter and without the call to VerifyPropertyName(...). I cannot get it to work. Here is what I have.
public class ViewModelBaseExtended : ViewModelBase
{
protected void RaiseAllPropertyChanged()
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(String.Empty));
}
}
}
But I get a compiler error: The event 'GalaSoft.MvvmLight.ViewModelBase.PropertyChanged' can only appear on the left hand side of += or -=. This is a copy of the code that is used in the ViewModelBase.
Can someone offer some advice as to how to get this to work?
Solution:
I copied all the code from ViewModelBase into a new class. I then added the method RaisePropertyChanged() mentioned above which instantiates the PropertyChangedEventArgs class with String.Empty. This is now the new subclass for my ViewModels.
Thanks again to Simon for leading the way!
In case you're reading this in 2016, you can use ObservableObject and notify that all of the properties have changed by doing:
RaisePropertyChanged(string.Empty);
Unfortunately this is not possible with the current code-base of MVVMLight
In the short term your have 2 options:
User your own custom base class. And by custom base class I mean "Do not inherit from the MVVMLight class".
Download and compile MVVMLight in Release mode. This will force the "VerifyPropertyName" method to be excluded. Of course then you don't get the value of property name checks.
I am sure Laurent Bugnion will have this fixed soon.
A lighter solution to this problem would have been to override RaisePropertyChanged(string propertyName) in your class :
protected override void RaisePropertyChanged(string propertyName)
{
if (propertyName != null)
{
base.RaisePropertyChanged(propertyName);
}
else
{
var handler = PropertyChangedHandler;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(null));
}
}
}

Why does my ASP.Net static function's "context" crossover between user sessions?

I think I need some help understanding how static objects persist in an ASP.Net application. I have this scenario:
someFile.cs in a class library:
public delegate void CustomFunction();
public static class A {
public static CustomFunction Func = null;
}
someOtherFile.cs in a class library:
public class Q {
public Q() {
if (A.Func != null) {
A.Func();
}
}
}
Some ASP.Net page:
Page_Init {
A.Func = MyFunc;
}
public void MyFunc() {
System.IO.File.AppendAllText(
"mydebug.txt", DateTime.Now.ToString("hh/mm/ss.fff", Session.SessionID));
}
Page_Load {
Q myQ = new Q();
System.Threading.Thread.Sleep(20000);
mQ = new Q();
}
The idea is that I have a business object which does some operation based on a callback function at the UI level. I set the callback function to a static variable on Page_Init (in the real code version, in the Master page, if that makes a difference). I thought that every execution of the page, no matter what user session it came from, would go through that function's logic but operate on its own set of data. What seems to be happening instead is a concurrency issue.
If I run one user session, then while it is sleeping between calls to that callback function, start another user session, when the first session comes back from sleeping it picks up the session ID from the second user session. How can this be possible?
Output of mydebug.txt:
01/01/01.000 abababababab (session #1, first call)
01/01/05.000 cdcdcdcdcdcd (session #2, first call - started 5 seconds after session #1)
01/01/21.000 cdcdcdcdcdcd (session #1 returns after the wait but has assumed the function context from session #2!!!!!)
01/01/25.000 cdcdcdcdcdcd (session #2 returns with its own context)
Why is the function's context (meaning, its local data, etc.) being overwritten from one user session to another?
Each request to an asp.net site comes in and is processed on it's own thread. But each of those threads belong to the same application. That means anything you mark as static is shared across all requests, and therefore also all sessions and users.
In this case, the MyFunc function that's part of your page class is copied over top of the static Func member in A with every page_init, and so every time any user does a page_init, he's replacing the A.Func used by all requests.
Static data is shared among the entire application domain of your webapp.
In short, it's shared among all the threads serving requests in your webapp, it's not bound to a session/thread/user in any way but to the webapp as a whole.(unlike e.g. php where each request lives in its own isolated environment bar a few knobs provided - such as the session variable.)
I won't try to improve on the other answers' explanations of static members, but do want to point out another way to code around your immediate problem.
As a solution, you could make an instance-oriented version of your class A, store it in a page-level variable, and pass it to Q's constructor on page load:
public class MyPage: Page {
private A2 _a2;
// I've modified A2's constructor here to accept the function
protected Page_Init() { this._a2 = new A2(MyFunc); }
protected Page_Load() {
Q myQ = new Q(this._a2);
// etc..
}
}
In fact, if there's no pressing need to declare A2 earlier, you could just instantiate it when you create your instance of Q in Page_Load.
Edit: to answer the question you raised in other comments, the reason the variables are being shared is that the requests are sharing the same delegate, which has only a single copy of its variables. See Jon Skeet's The Beauty of Closures for more details.
One solution you might consider is using [ThreadStatic].
http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(VS.71).aspx
It will make your statics per thread. There are cavaets however so you should test.
If you want the data to persist only for the current request, use HttpContext.Items:
http://msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx
If you want the data to persist for the current user's session (assuming you have session state enabled), use HttpContext.Session:
http://msdn.microsoft.com/en-us/library/system.web.httpcontext.session.aspx

Access/use the same object during a request - asp.net

i have a HttpModule that creates an CommunityPrincipal (implements IPrincipal interface) object on every request. I want to somehow store the object for every request soo i can get it whenever i need it without having to do a cast or create it again.
Basically i want to mimic the way the FormsAuthenticationModule works.
It assigns the HttpContext.User property an object which implements the IPrincipal interface, on every request.
I somehow want to be able to call etc. HttpContext.MySpecialUser (or MySpecialContext.MySpecialUser - could create static class) which will return my object (the specific type).
I could use a extension method but i dont know how to store the object so it can be accessed during the request.
How can this be achieved ?
Please notice i want to store it as the specific type (CommunityPrincipal - not just as an object).
It should of course only be available for the current request being processed and not shared with all other threads/requests.
Right now i assign my CommunityPrincipal object to the HttpContext.User in the HttpModule, but it requires me to do a cast everytime i need to use properties on the CommunityPrincipal object which isnt defined in the IPrincipal interface.
I'd recommend you stay away from coupling your data to the thread itself. You have no control over how asp.net uses threads now or in the future.
The data is very much tied to the request context so it should be defined, live, and die along with the context. That is just the right place to put it, and instantiating the object in an HttpModule is also appropriate.
The cast really shouldn't be much of a problem, but if you want to get away from that I'd highly recommend an extension method for HttpContext for this... this is exactly the kind of situation that extension methods are designed to handle.
Here is how I'd implement it:
Create a static class to put the extension method:
public static class ContextExtensions
{
public static CommunityPrinciple GetCommunityPrinciple(this HttpContext context)
{
if(HttpContext.Current.Items["CommunityPrinciple"] != null)
{
return HttpContext.Current.Items["CommunityPrinciple"] as CommunityPrinciple;
}
}
}
In your HttpModule just put the principal into the context items collection like:
HttpContext.Current.Items.Add("CommunityPrincipal", MyCommunityPrincipal);
This keeps the regular context's user property in the natural state so that 3rd party code, framework code, and anything else you write isn't at risk from you having tampered with the normal IPrincipal stroed there. The instance exists only during the user's request for which it is valid. And best of all, the method is available to code as if it were just any regular HttpContext member.... and no cast needed.
Assigning your custom principal to Context.User is correct. Hopefully you're doing it in Application_AuthenticateRequest.
Coming to your question, do you only access the user object from ASPX pages? If so you could implement a custom base page that contains the cast for you.
public class CommunityBasePage : Page
{
new CommunityPrincipal User
{
get { return base.User as CommunityPrincipal; }
}
}
Then make your pages inherit from CommunityBasePage and you'll be able to get to all your properties from this.User.
Since you already storing the object in the HttpContext.User property all you really need to acheive you goal is a Static method that acheives your goal:-
public static class MySpecialContext
{
public static CommunityPrinciple Community
{
get
{
return (CommunityPrinciple)HttpContext.Current.User;
}
}
}
Now you can get the CommunityPrinciple as:-
var x = MySpecialContext.Community;
However it seems a lot of effort to got to avoid:-
var x = (CommunityPrinciple)Context.User;
An alternative would be an Extension method on HttpContext:-
public static class HttpContextExtensions
{
public static CommunityPrinciple GetCommunity(this HttpContext o)
{
return (CommunityPrinciple)o.User;
}
}
The use it:-
var x = Context.GetCommunity();
That's quite tidy but will require you to remember to include the namespace where the extensions class is defined in the using list in each file the needs it.
Edit:
Lets assume for the moment that you have some really good reason why even a cast performed inside called code as above is still unacceptable (BTW, I'd be really interested to understand what circumstance leads you to this conclusion).
Yet another alternative is a ThreadStatic field:-
public class MyModule : IHttpModule
{
[ThreadStatic]
private static CommunityPrinciple _threadCommunity;
public static CommunityPrinciple Community
{
get
{
return _threadCommunity;
}
}
// Place here your original module code but instead of (or as well as) assigning
// the Context.User store in _threadCommunity.
// Also at the appropriate point in the request lifecyle null the _threadCommunity
}
A field decorated with [ThreadStatic] will have one instance of storage per thread. Hence multiple threads can modify and read _threadCommunity but each will operate on their specific instance of the field.

Resources