jQuery/ASP.NET concurrent Ajax calls - asp.net

I have a webpage where users can look for clients and select them. After selection they can be send to the webserver through an jQuery Ajax call. On the server database operations and another webservice is called, so this can take a while. That is why I wanted to present a progress bar to the user.
This progressbar is also updated by a Ajax call.
The problem seems to be that asp.net doesn't allow concurrent calls and the session state queues all calls. You can solve this in mvc by setteing the attribute [SessionState(SessionStateBehavior.ReadOnly)]
But I don't find to do this in my page-behind webmethods. Anyway, the worker method is using session state (for security, and updating the session variable for the progressbar).
The progress method is only reading and returning the session variable.
Is there a solution for it, or is another approach necessary?
I am using asp.net 4.

You could set the session mode to readonly at the #Page directive in your markup:
<%# Page Title="Home Page" EnableSessionState="ReadOnly" Language="C#" %>

I have found the solution:
1) The WebMethod only needs to receive the data and start a new thread:
<WebMethod()> _
Public Shared Function importContacts(ByVal contactGuids As String, ByVal campaignGuids As String) As String
Dim paramsList As New List(Of Object)
paramsList.Add(contactGuids)
paramsList.Add(campaignGuids)
paramsList.Add(HttpContext.Current.Session)
Dim th As New Threading.Thread(AddressOf processImport)
th.Start(paramsList)
Return ""
End Function
The Ajax call is ended quickly and on the browser you can start polling for progress.
2) The thread function needs to convert the parameters first, then you can use the session state:
Public Shared Sub processImport(params As Object)
Dim paramsList As List(Of Object) = params
Dim contactGuids As String = paramsList(0)
Dim campaignGuids As String = paramsList(1)
Dim _session As HttpSessionState = paramsList(2)
_session("EmailMarketingDatabase_progress") = 0
...
End Sub
3) The progress WebMethod looks like this:
<WebMethod()> _
Public Shared Function getProgressStatus() As Integer
Return HttpContext.Current.Session("EmailMarketingDatabase_progress")
End Function

Related

Access winform variable from browsercontrol

I have an application in ASP.Net Ajax. I want to open it via a browsercontrol from a winform, and I wish to access a variable (username) that the user used to log in to the webform with. On load I would like to read that username and perform the rest of my webpage code on that browsercontrol using that username.
My ASP.Net Ajax has been published to a internal web server and the browsercontrol loads that IP address.
Is there any way to achieve this at all?
EDIT:
I have discovered the javascript extension: window.external
And I can call a C# procedure from the webpage using javascript with it, which is a start, but I need to retrieve a varaible from c# - this is where the problem comes in. I have tried the
var name = function window.external.GetGlobalVariable(MyGlobalProcedure, "Cannot Get Value");
But javascript error says the method cannot be applied to the object.
Your answer should be as follows:
Public Class Form1
Dim GlobalVar As String = "Your Name"
Dim YourBrowser As New WebBrowser
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
YourBrowser.Url = New Uri("Your URL address")
AddHandler YourBrowser.DocumentCompleted, AddressOf PageLoadComplete
End Sub
'The invokescript will only work once the HTML has finished loading itself into your WebBrowser
Sub PageLoadComplete()
'Must declare the string inside an array as the invokescript only allows an object to be sent
Dim VarToSend As String() = {GlobalVar}
YourBrowser.Document.InvokeScript("yourJavascriptfunction", VarToSend)
End Sub
The javascript section should look as follows:
<script type="text/javascript" language="javascript">
function userNameSet(name) {
$(document).ready(function() {
//variable now exists inside your WebBrowser client and can be used accordingly now
alert(name);
});
}
</script>
References for answer: http://www.dotnetcurry.com/showarticle.aspx?ID=194
"Store that name in a session variable and access the session in your ajax call"
In your ASP.Net application create a hidden field (or if it's somewhere on the UI in some control that works also). Put the username or whatever information you want to share into that field.
From your WinForms program you can request that field through the WebBrowser control like this:
MessageBox.Show(WebBrowser1.Document.GetElementById("txtUsername").GetAttribute("value"))
The above assumes you have some HTML element called txtUsername with the value attribute set.

Public shared function and cookies or sessions asp.net vb

I have implement a Public shared function in my aspx.vb page (not in class or web service) to use it with javascript but I had problem with creating a cookies and sessions
How can I create cookies or sessions in that shared function? any ideia!
Thank you all.
<WebMethod> _ Public Shared Sub Test(text As String)
Dim ctx As HttpContext = System.Web.HttpContext.Current
ctx.Session("Test") = text
ctx.Response.Cookies("TestCookie").Value = text
End Sub

How to manitain session values during HttpWebRequest?

In my code I'm sending a HttpWebRequest to a page in my website.
When request sends to this page, It doesn't maintain the Session values.
Below is the code, from where I'm generating the web request:
Public Overloads Shared Function ReadURL(ByVal sUrl As String) As String
Dim sBody As String
Dim oResponse As HttpWebResponse
Dim oRequest As HttpWebRequest
Dim oCookies As New CookieContainer
oRequest = WebRequest.Create("http://localhost:64802/inventory/purchase_order.aspx?id=5654")
oRequest.CookieContainer = oCookies
oResponse = oRequest.GetResponse()
Dim oReader As New StreamReader(oResponse.GetResponseStream())
sBody = oReader.ReadToEnd
oReader.Close()
oResponse.Close()
Return sBody
End Function
Below is the code written on Page_Load of Purchaseorder.aspx.vb:
iDomains_ID = Session("Domains_ID")
iLogin_ID = Session("Login_ID")
sPage = Request.Path
If Request.QueryString.Count > 0 Then sPage &= "?" & Request.QueryString.ToString()
sPage = shared01.Encrypt(sPage, Application("PK"))
If Not User.Identity.IsAuthenticated Or iLogin_ID = 0 Then
Response.Redirect("/login.aspx?page=" & sPage)
Exit Sub
End If
Above code doesn't gets the session values and it redirects to the login page.
So, how i can maintain the session on both pages during HttpWebRequest.
Looking for your replies.
EDIT
I've tried to use CookieContainer class as you can see in above code. But it doesn't work at all.
As an alternative, assuming the calling and called pages are in the same application, you could use the Server.Execute method to load the content of the page without making a separate request to the site:
Public Overloads Function ReadURL(ByVal sUrl As String) As String
Using writer As New StringWriter()
Server.Execute("~/inventory/purchase_order.aspx?id=5654", writer, False)
Return writer.ToString()
End Using
End Function
If I've understood you correctly, you're making a request from one page in your site to another, and you want to send the cookies from the current HttpRequest with your WebRequest?
In that case, you'll need to manually copy the cookies to the CookieContainer:
For Each key As String In Request.Cookies.AllKeys
Dim sourceCookie As HttpCookie = Request.Cookies(key)
Dim destCookie As New Cookie(sourceCookie.Name, sourceCookie.Value, sourceCookie.Path, "localhost")
destCookie.Expires = sourceCookie.Expires
destCookie.HttpOnly = sourceCookie.HttpOnly
destCookie.Secure = sourceCookie.Secure
oCookies.Add(destCookie)
Next
NB: You'll either need to make the ReadUrl function non-Shared, or pass the current HttpRequest as a parameter.
You'll also need to make sure the calling page has EnableSessionState="false" in the <%# Page ... %> directive, otherwise the page you're calling will hang trying to obtain the session lock.
Your code seems like you will need to make a request and a post. The first request will redirect you to your login page. The second will be a request where you post to the login page, which will start the session and (?) store information into the session variables. That post (to the login page) will then redirect you to the page you want.
I used code in this example http://www.codeproject.com/Articles/145122/Fetching-ASP-NET-authenticated-page-with-HTTPWebRe (I tweaked it a bit) to write an application to do this.

Instantiating a class within WCF

I'm writing a WCF WebMethod to upload files to, of which I taken snippets from around the web. The WCF interface looks like this:
<ServiceContract()>
Public Interface ITransferService
<OperationContract()>
Sub UploadFile(ByVal request As RemoteFileInfo)
End Interface
<MessageContract()>
Public Class RemoteFileInfo
Implements IDisposable
<MessageHeader(MustUnderstand:=True)>
Public FileName As String
<MessageHeader(MustUnderstand:=True)>
Public Length As Long
<MessageBodyMember(Order:=1)>
Public FileByteStream As System.IO.Stream
Public Sub Dispose() Implements IDisposable.Dispose
If FileByteStream IsNot Nothing Then
FileByteStream.Close()
FileByteStream = Nothing
End If
End Sub
End Class
Within ASP.NET, when the web method is consumed, for some reason it only works when the interface is used as part of the instantiation of RemoteFileInfo:
Protected Sub btn_Click(sender As Object, e As EventArgs) Handles btn.Click
If fu.HasFile Then
Dim fi As New System.IO.FileInfo(fu.PostedFile.FileName)
' this is the line in question --------------
Dim cu As ServiceReference1.ITransferService = New ServiceReference1.TransferServiceClient()
' -------------------------------------------
Dim uri As New ServiceReference1.RemoteFileInfo()
Using stream As New System.IO.FileStream(fu.PostedFile.FileName, IO.FileMode.Open, IO.FileAccess.Read)
uri.FileName = fu.FileName
uri.Length = fi.Length
uri.FileByteStream = stream
cu.UploadFile(uri)
End Using
End If
End Sub
Can anyone advise why it is not possible to create an instance of TransferService using the following approach:
Dim cu As New ServiceReference1.TransferServiceClient()
If I try the above, it breaks this line:
cu.UploadFile(uri)
...and UploadFile must be called with three parameters (FileName, Length, FileByteStream) even there is no method that uses this signature.
Why is the Interface required when creating an instance of this class please?
When you create the proxy for your service with the "Add Service Reference" dialog, by default the proxy creation code will "unwrap" message contracts, like the one you have. If you want the message contract to appear as you defined on the server side on your proxy, you need to select the "Advanced" tab, and check the "Always generate message contracts" option. With that you'll get the message contract in your client as well.
The issue is that when a MessageContract is encountered as a parameter, the WCF client generation assumes by default that you want to implement a messaging-style interface, and provides the discrete properties from the message contract as part of the client-side interface.
The Using Messaging Contracts article in MSDN contains a very detailed description of what can be done with a messaging contract and I suspect that Microsoft chose this default behavior because of some of the "games" that you can play with the messages.
However, if you examine the code generated for your UploadFile on the client side, there are some interesting tidbits that help to explain what is going on.
The first is the comments for the UploadFile method in the interface:
'CODEGEN: Generating message contract since the operation UploadFile is neither RPC nor document wrapped.
...
Function UploadFile(ByVal request As ServiceReference1.RemoteFileInfo) As ServiceReference1.UploadFileResponse
This implies that the contract would have been generated differently if the message contract had a different implementation.
The second is that you will see that there is nothing special about the code that is used to actually make the service call:
Public Sub UploadFile(ByVal FileName As String, ByVal Length As Long, ByVal FileByteStream As System.IO.Stream)
Dim inValue As ServiceReference1.RemoteFileInfo = New ServiceReference1.RemoteFileInfo()
inValue.FileName = FileName
inValue.Length = Length
inValue.FileByteStream = FileByteStream
Dim retVal As ServiceReference1.UploadFileResponse = CType(Me,ServiceReference1.ITransferService).UploadFile(inValue)
End Sub
So in this case, your code is doing exactly what the generated code does. However, if the MessageContract were more complex, I suspect that this would no longer be the case.
So, for your question:
Can anyone advise why it is not possible to create an instance of
TransferService using the following approach...
There is no reason not to take this approach as long as you verify that the implementation of the method call is functionality equivalent to your code.
There are a couple of options for changing the default generation of the method in the client:
1) Remove the MessageContract attribute from the RemoteFileInfo class.
2) Although it seems to be counter-intuitive, you can check the Always generate message contracts checkbox in the Configure Service Reference Dialog Box.

ASP.NET Server Control - Is shared Viewstate between controls possible

If I have a master 'composite custom server control', with several child custom controls, is it possible, for the master control to share its viewstate with the child controls, which are also custom composite server controls,(all of the controls are composite custom server controls)?
To expand a little more, say I have a Person control, Address control, Phone control and Notes control. Address, Phone and Notes can exist as either independant controls, or as part of the Person control. Since each control is responsible for its own viewstate, and stores required data in it, so it can render/postback etc, it ends up that there is a lage duplication in the viewstate, since the Person control stores all the data, and then each child control stores its own data again. Also, to further complicate things, the Person Control adds the child controls dynamically, so its possible to add a 2nd address/phone number/note etc,which can cause yet a larger viewstate(up to 1MB).
In the case of when all the Address/Phone/etc controls are children of the the Person Control, is it possible for me to somehow share common viewstate data, so I dont have 2/3/4 copies of some stuff in the viewstate, and when they are not components, just act as normal?
I do have one idea for a solution, but its fairly nasty imo, where I could modify the Person control, expose its viewstate or the data, and then in the child control, would check the control hierarchy, and if the child control is part of a Person, dont use to its own viewstate, use the exposed one. This would require me to rework alot of the existing code, which I'd rather avoid if possible.
The first question I'd ask is, "How much of this data do you really need to keep on the ViewState?" For example, is there enough information in a typical page request to be able to rebuild all of the information that is in some of these controls?
I find that ASP.NET typically uses ViewState far more than I really need it to, and if you're getting a 1MB viewstate, I suspect the same is probably true for you. I highly recommend reading this article to get a more full understanding of how ViewState works, what causes values to get saved to ViewState, and how to avoid using it where it's not necessary. You may be able to solve the bulk of your problem simply by doing more of your work in Page_Init instead of Page_Load, for example.
You can "share" a ViewState between any number of objects by passing it around as a StateBag type, or by using delegate functions that return the ViewState that needs to be shared. The usage of this, however; should be limited to very specific circumstances, because typically controls use Properties to expose their ViewState data to other objects (See the link #StriplingWarrior posted here). That being said, here is some example code:
User Control: SharedState.ascx
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="SharedState.ascx.vb" Inherits="TestSite.SharedState" %>
User Control: SharedState.ascx.vb
Public Class SharedState
Inherits System.Web.UI.UserControl
Public Delegate Function GetStateDelegate() As StateBag
Public Const SharedValueKey = "The key used to access the ViewState dictionary"
Public Property GetState() As GetStateDelegate
' Different ways to get values
Public Function GetTypedValue(Of TValue)() As TValue
Return CTypeDynamic(GetValue(), GetType(TValue))
End Function
Public Function GetValue() As Object
' Use the delegate to get the view state from the parent
Return GetState.Invoke()(SharedValueKey)
End Function
Public Function GetValue(state As StateBag) As Object
Return state(SharedValueKey)
End Function
' Different ways to set values
Public Sub SetTypedValue(Of TValue)(value As TValue)
Me.SetValue(value)
End Sub
Public Sub SetValue(value As Object)
GetState.Invoke()(SharedValueKey) = value
End Sub
Public Sub SetValue(state As StateBag, value As Object)
state(SharedValueKey) = value
End Sub
' Set ViewState value using a key and the delegate
Public Sub SetStateWithKey(key As String, value As Object)
GetState.Invoke()(key) = value
End Sub
' Get ViewState value using a key and the delegate
Public Function GetStateWithKey(key As String) As Object
Return GetState.Invoke()(key)
End Function
End Class
Page: SharedStatePage.aspx
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="SharedStatePage.aspx.vb" Inherits="TestSite.SharedStatePage" %>
<%# Register src="SharedState.ascx" tagname="SharedState" tagprefix="uc1" %>
<uc1:SharedState ID="SharedState1" runat="server" />
<uc1:SharedState ID="SharedState2" runat="server" />
Page: SharedStatePage.aspx.vb
Public Class SharedStatePage
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Set up SharedState 1 & 2 to use the delegate '
SharedState1.GetState = AddressOf GetState
SharedState2.GetState = AddressOf GetState
' Set the view state from the page '
ViewState(SharedState.SharedValueKey) = 23
' Read the view state from the user controls '
Dim sharedInt As Integer
sharedInt = SharedState1.GetTypedValue(Of Integer)()
sharedInt = SharedState1.GetValue()
sharedInt = SharedState1.GetValue(ViewState)
sharedInt = SharedState2.GetValue()
' Set the view state from one of the user controls '
SharedState2.SetTypedValue(Of String)("Hello")
Dim sharedStr As String = ViewState(SharedState.SharedValueKey)
sharedStr = SharedState1.GetTypedValue(Of String)()
sharedStr = SharedState2.GetValue()
' Use a different key to set and get view state data '
ViewState("MyKey") = New With {.Name = "Some Object", .Value = 46}
Dim myObj = SharedState1.GetStateWithKey("MyKey")
SharedState2.SetStateWithKey("MyKey", "New Value")
myObj = ViewState("MyKey")
End Sub
Private Function GetState() As StateBag
Return ViewState
End Function
End Class
Use with caution, or don't use with abandon.

Resources