Showing posts with label webforms. Show all posts
Showing posts with label webforms. Show all posts

Monday, July 30, 2018

asp.net while first request processing allow second request on click

Leave a Comment

In Asp.net Web Form app, I have Generate Report button and a Cancel button to cancel report generation process, if it takes a long time.

When I Click Generate Report it does the heavy task, after 3 seconds I try to cancel this heavy task by clicking the Cancel button.

But the server side code for Cancel button click is called after some time delay.

I even tried window.stop() in JavaScript to stop page loading and hit server code fast, but still, there is a delay.

Code:

protected void btnExportExcel_Click(object sender, EventArgs e) {  // Doing Heavy Task to Generate Report  }   protected void btnCancel_Click(object sender, EventArgs e) {     if (Response.IsClientConnected)     {         HttpContext.Current.ApplicationInstance.CompleteRequest();      } }         <asp:Button ID="btnCancel" runat="server" Text="Cancel Request"     OnClientClick="return StopPageLoading();" OnClick="btnCancel_Click" />    function StopPageLoading() {       try      {        window.stop();      } catch (exception)       {        document.execCommand('Stop'); // for IE and other browsers      }    } 

How can I allow to start another request on click fast, while current request is in processing?

How to allow UI to be responsive?

Update:

To reproduce the situation:

  1. I click Export To Excel , it takes 7 minutes to process.
  2. While still processing I click Cancel button , it takes another 5 minutes to cancel.

I read that Concurrent request is not possible in Asp.NET because of session state makes exclusive locks.

So How to cancel fast ?

Will making my methods async help to overcome session state exclusive locks issue ?

4 Answers

Answers 1

As in another question from you, you can do it this way

public partial class Contact : Page {      private static CancellationTokenSource tokenSource = new CancellationTokenSource();       protected void Page_Load(object sender, EventArgs e)     {      }      protected async void btnExportExcel_Click(object sender, EventArgs e)     {         CancellationToken cToken = tokenSource.Token;         cToken.Register(() => cancelNotification());          try         {              await Task.Run(() =>             {                 cToken.ThrowIfCancellationRequested();                  GenerateReport(sender, cToken);              }, cToken);         }         catch(OperationCanceledException)         {          }     }      private void GenerateReport(object sender, CancellationToken ct)     {         // Just to Simulate Export to excel          Thread.Sleep(70000);     }       protected void btnCancel_Click(object sender, EventArgs e)     {          tokenSource.Cancel();      }       private static void cancelNotification()     {         // Do your stuff when the user is canceld the report generation      } } 

And in your Page.aspx

Need something like this

<%@ Page Async="true" EnableSessionState="false" %> 

I hope this helps you!

Answers 2

Though, you can do it or not (I am not sure), it is generally not a good idea to create a long running task within ASP.NET, especially if they are background tasks. So there are some helper libraries that are for this purpose.

  1. HangFire http://docs.hangfire.io Hangfire allows you to kick off method calls outside of the request processing pipeline in a very easy, but reliable way. These method invocations are performed in a background thread and called background jobs. this support using cancellation tokens to cancel a background job
  2. Quarz.NET I have never tryed Quarz.NET
  3. Or you have to do it yourself. I know some projecs that take some dedicate servers just for run background and time consuming tasks. So the website is just a frontend layer that set some flags that control the task is cancel or not

Answers 3

The problem is in IIS (or any other Web Server) architecture. The server returns a response to a client (browser) and forget it. Your first request (btnExcelExport_Click) must be finished before it returns response to the browser. The click on btnCancel starts a new request which knows nothing about the first one.
The workaround is to send a long job to another thread. It is possible to communicate to that thread using state object sent to the new thread. Something like this.

<%@ Page Language="C#" %>  <!DOCTYPE html>  <script runat="server">      protected void btnRun_Click(object sender, EventArgs e)     {         var jobState = new StateInfo()         {             Id = 1,             Counter = 0,             Content = "Start the job",             Cancelled = false,             Completed = false         };         Session["job"] = jobState; //Save state between requests         System.Threading.ThreadPool.QueueUserWorkItem(             new System.Threading.WaitCallback(LongJob),             jobState             );//returns immediately         lblState.Text += "<br />" + jobState.Counter.ToString()              + " Completed: " + jobState.Completed.ToString()             + " Cancelled: " + jobState.Cancelled.ToString()             + "<br />" + jobState.Content;         btnCancel.Visible = true;         btnCheck.Visible = true;     }      protected void btnCancel_Click(object sender, EventArgs e)     {         var jobState = Session["job"] as StateInfo;         if (!jobState.Completed)             jobState.Cancelled = true;         System.Threading.Thread.Sleep(1000);//wait for the next loop to complete         lblState.Text += "<br />" + jobState.Counter.ToString()             + " Completed: " + jobState.Completed.ToString()             + " Cancelled: " + jobState.Cancelled.ToString()             + (jobState.Completed || jobState.Cancelled ? "<br />" + jobState.Content : "");     }      protected void btnCheck_Click(object sender, EventArgs e)     {         var jobState = Session["job"] as StateInfo;         lblState.Text += "<br />" + jobState.Counter.ToString()             + " Completed: " + jobState.Completed.ToString()             + " Cancelled: " + jobState.Cancelled.ToString()             + (jobState.Completed || jobState.Cancelled ? "<br />" + jobState.Content : "");     }      private void LongJob(object state)     {         var jobState = state as StateInfo;         do         {             System.Threading.Thread.Sleep(1000);             jobState.Counter++;             if (jobState.Counter >= 100)             {                 jobState.Completed = true;                 jobState.Content = "The job is completed";             }             else if (jobState.Cancelled)                 jobState.Content = "The job is cancelled";         }         while (!jobState.Cancelled && !jobState.Completed);     }     [Serializable]     class StateInfo     {         public int Id { get; set; }         public int Counter { get; set; }         public string Content { get; set; }         public bool Cancelled { get; set; }         public bool Completed { get; set; }     }  </script>  <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">     <title>Long Job</title> </head> <body>     <form id="form1" runat="server">         <div>             <asp:Label ID="lblState" runat="server"></asp:Label><br />             <asp:Button runat="server" ID="btnRun" OnClick="btnRun_Click" Text="Run" />             <asp:Button runat="server" ID="btnCheck" OnClick="btnCheck_Click" Text="Check State" Visible="false" />             <asp:Button runat="server" ID="btnCancel" OnClick="btnCancel_Click" Text="Cancel" Visible="false" />         </div>     </form> </body> </html> 

Answers 4

Did you try using the async/await operation in the code instead of synchronous

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

when the report generation call is asynchronous then redirect to UI once the report generation process is kicked off. Now the cancel button can be clicked when needed.

Does this help?

Read More

Thursday, April 19, 2018

Template field disappears

Leave a Comment

I have a gridview used to display records based on a selected value in a drop down.

Within the gridview, there are a dynamic number of bound field columns added in code behind, followed by a drop down list in a template field and a button in a second template field.

The problem is, when I click on the button, the template fields disappear.

GRID VIEW

<asp:UpdatePanel ID="upnlDetail" runat="server" UpdateMode="Conditional">     <ContentTemplate>         <asp:GridView ID="gvDetails" runat="server"             AutoGenerateColumns="false"              SkinID="gridviewGray"              CellPadding="3"              OnRowCommand="gvDetails_RowCommand"              OnRowDataBound="gvDetails_RowDataBound"              AllowSorting="true">             <Columns>                 <asp:TemplateField>                     <ItemTemplate>                         <asp:DropDownList ID="ddlStatus" runat="server"                             AutoPostBack="true"                              OnSelectedIndexChanged="ddlStatus_SelectedIndexChanged">                         </asp:DropDownList>                     </ItemTemplate>                 </asp:TemplateField>                 <asp:TemplateField>                     <ItemTemplate>                         <asp:Button ID="btnSave" runat="server"                             CommandName="Save"                              CommandArgument="<%# ((GridViewRow) Container).RowIndex %>"                              Text="Save" />                     </ItemTemplate>                 </asp:TemplateField>             </Columns>         </asp:GridView>                                     </ContentTemplate> </asp:UpdatePanel> 

CODE BEHIND

while (gvDetails.Columns.Count > 2)  //Don't remove the rightmost columns {      gvDetails.Columns.RemoveAt(0); }  gvDetails.Columns.Insert(0, GridViewTools.CreateBoundField(                                 "Request.Amount", "Amount", "Money", 50)); gvDetails.Columns.Insert(0, GridViewTools.CreateBoundField(                                 "Request.CustomerName", "Customer", "comment", 200)); gvDetails.Columns.Insert(0, GridViewTools.CreateBoundField(                                 "Request.BillAccountFormatted", "Account", "text", 100)); gvDetails.Columns.Insert(0, GridViewTools.CreateBoundField(                                 "Request.Id", "Request", "int", 50));  gvDetails.DataSource = dt; gvDetails.DataBind(); 

Method GridViewTools.CreateBoundField is a custom method that sets my default boundfield attributes and returns the BoundField object.

public static BoundField CreateBoundField(...){...} 

In testing it, the gridview populates as desired on the initial load. However, the RowCommand event does not fire and both template fields disappear when the button is clicked. The next gridview.RowDataBound event then throws a null object error because the drop down list is no longer found in the row.

If I remove lines adding the bound fields [Columns.Insert], the RowCommand fires as expected when btnSave is clicked and the template fields persist. If I add even a single bound field column again, the RowCommand does not fire and the template fields disappear.

Any suggestions? Why would adding a new column to a gridview invalidate the RowCommand event for a pre-existing button and make the template fields disappear?

UPDATES I've removed the template fields from the Grid View declaration and added the controls to a bound field instead, but still get the same result. The show on the initial load, but disappear once clicked.

I've also looked at the grid view in the Page_Unload event, and it still contains the columns at that point, even though they don't show on screen. Is there any event I can capture after Page_Unload?

2 Answers

Answers 1

This is interesting. It would appear to happen only when the BoundFields are Inserted. When adding BoundFields to the end of the Grid, it does work.

gvDetails.Columns.Add(GridViewTools.CreateBoundField("Request.Amount", "Amount", "Money", 50)); 

But you probably want the DropDown and the Button at the end of the GridView. So you could make those dynamic as well, instead of just adding data to the DropDownList. So the first thing to do is add 2 extra dummy BoundField columns to hold the Button and DropDownList.

gvDetails.Columns.Add(GridViewTools.CreateBoundField("", "DropDownList", "int", 50)); gvDetails.Columns.Add(GridViewTools.CreateBoundField("", "Button", "int", 50)); 

Now change the RowDataBound event to add the controls.

protected void gvDetails_RowDataBound(object sender, GridViewRowEventArgs e) {     //check if the row is a datarow     if (e.Row.RowType == DataControlRowType.DataRow)     {         //create a dropdownlist and add some data         DropDownList ddl = new DropDownList();         ddl.ID = "ddlStatus";         ddl.Items.Add(new ListItem() { Text = "Option 1", Value = "1" });         ddl.Items.Add(new ListItem() { Text = "Option 2", Value = "2" });         ddl.Items.Add(new ListItem() { Text = "Option 3", Value = "3" });          //create a save button         Button btn = new Button();         btn.ID = "btnSave";         btn.CommandName = "save";         btn.Text = "Save";         btn.CommandArgument = e.Row.RowIndex.ToString();          //add the controls to the last 2 cells         e.Row.Cells[e.Row.Cells.Count - 2].Controls.Add(ddl);         e.Row.Cells[e.Row.Cells.Count - 1].Controls.Add(btn);     } } 

But for this to work you need to place the DataBind() of the GridView outside the IsPostBack check, but keep the adding of the BoundFields inside it.

protected void Page_Load(object sender, EventArgs e) {     if (IsPostBack == false)     {          //add the boundfields here     }      //bind gridview on every postback     gvDetails.DataSource = dt;     gvDetails.DataBind(); } 

The ASPX now looks like this

<asp:GridView ID="gvDetails" runat="server" AutoGenerateColumns="false"    OnRowDataBound="gvDetails_RowDataBound" OnRowCommand="gvDetails_RowCommand"></asp:GridView>    

Well, you now have a fully dynamically created GridView.

Answers 2

Build everything in code behind. It is not a matter of preference. Please read Dynamic Web Server Controls and View State

WebForm:

<asp:GridView ID="gvDetails" runat="server"     AutoGenerateColumns="false"      SkinID="gridviewGray"      CellPadding="3"      OnRowDataBound="OnRowDataBound"     OnRowCommand="gvDetails_RowCommand"     AllowSorting="true"> </asp:GridView> 

Code behind:

public class GridViewDropDownListTemplate : ITemplate {     public void InstantiateIn(Control container)     {         DropDownList ddlStatus = new DropDownList();         ddlStatus.ID = "ddlStatus";         ddlStatus.Items.Add(new ListItem("Status 1"));         ddlStatus.Items.Add(new ListItem("Status 2"));         ddlStatus.Items.Add(new ListItem("Status 3"));         ddlStatus.Items.Add(new ListItem("Status 4"));         ddlStatus.AutoPostBack = true;         ddlStatus.SelectedIndexChanged += ddlStatus_SelectedIndexChanged;         container.Controls.Add(ddlStatus);     }      public void ddlStatus_SelectedIndexChanged(object sender, EventArgs e)     {     } }  public class GridViewButtonTemplate : ITemplate {     public void InstantiateIn(Control container)     {         Button btnSave = new Button();         btnSave.ID = "btnSave";         btnSave.Text = "Save";         container.Controls.Add(btnSave);     } }  public partial class Default : System.Web.UI.Page {     object[] data = new[]     {         new { Amount = 12500.00, Account = "1234-567-89" },         new { Amount = 87000.00, Account = "0000-999-88" }     };      protected void Page_Load(object sender, EventArgs e)     {         if (!IsPostBack)         {             BuildBoundFields();         }          BuildTemplateFields();         BindData();     }      protected void BuildBoundFields()     {         // Amount bound field         BoundField boundFieldAmount = new BoundField();         boundFieldAmount.DataField = "Amount";         boundFieldAmount.HeaderText = "Amount";         boundFieldAmount.SortExpression = "Amount";         boundFieldAmount.ItemStyle.Width = Unit.Pixel(100);         gvDetails.Columns.Add(boundFieldAmount);          // Account bould field         BoundField boundFieldAccount = new BoundField();         boundFieldAccount.DataField = "Account";         boundFieldAccount.HeaderText = "Account";         boundFieldAccount.SortExpression = "Account";         boundFieldAccount.ItemStyle.Width = Unit.Pixel(250);         gvDetails.Columns.Add(boundFieldAccount);          // ...     }      protected void BuildTemplateFields()     {          // Status template field         TemplateField statusTemplateField = new TemplateField();         statusTemplateField.ItemTemplate = new GridViewDropDownListTemplate();         gvDetails.Columns.Add(statusTemplateField);          // Save template field         TemplateField saveTemplateField = new TemplateField();         saveTemplateField.ItemTemplate = new GridViewButtonTemplate();         gvDetails.Columns.Add(saveTemplateField);     }      protected void BindData()     {         gvDetails.DataSource = data;         gvDetails.DataBind();     }      protected void OnRowDataBound(object sender, GridViewRowEventArgs e)     {         if (e.Row.RowType == DataControlRowType.DataRow)         {             Button btnSave = (Button)e.Row.FindControl("btnSave");             if (btnSave != null)             {                 btnSave.CommandArgument = e.Row.RowIndex.ToString();             }         }     }      protected void gvDetails_RowCommand(object sender, GridViewCommandEventArgs e)     {     } } 
Read More

Thursday, December 7, 2017

How to prevent [Authorize (Roles = “xxx”)] to verify in openidconnect when using webform authentification

Leave a Comment

In my login web page, I have a webform (Identity 2.0) you can use to create an account in a database or you can use Azure Active Directory to authenticate with a corporate email. (External auth)

I've put the [Authorize] attribute to decorate the Index() action in UserController. My List() action in the same controller is decorated like this : [Authorize (Roles = "Admin")]

When logged in with my webform login, if I go to /MyController/List/ I'm redirected to the Microsoft account Login page. When going to /MyController/Index I'm not redirected.

What is causing this behavior? I don't want to check in Azure when the user is logged in with webform. How can I prevent this from happening?

Here's my Startup.Auth.cs

    public partial class Startup {     private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];     private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];     private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];     private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];     private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];      public static readonly string Authority = aadInstance + tenantId;      // This is the resource ID of the AAD Graph API.  We'll need this to request a token to call the Graph API.     string graphResourceId = "https://graph.windows.net";       // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864     public void ConfigureAuth(IAppBuilder app)     {          // Configure the db context, user manager and role manager to use a single instance per request         app.CreatePerOwinContext(ApplicationDbContext.Create);         app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);         app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);         app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);          // Enable the application to use a cookie to store information for the signed in user         // and to use a cookie to temporarily store information about a user logging in with a third party login provider         // Configure the sign in cookie         app.UseCookieAuthentication(new CookieAuthenticationOptions         {             AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,             LoginPath = new PathString("/Account/Login"),             Provider = new CookieAuthenticationProvider             {                 // Enables the application to validate the security stamp when the user logs in.                 // This is a security feature which is used when you change a password or add an external login to your account.                   OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(                     validateInterval: TimeSpan.FromMinutes(30),                     regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))             }         });          // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.         app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));          // Enables the application to remember the second login verification factor such as phone or email.         // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.         // This is similar to the RememberMe option when you log in.         app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);          // Pour Azure         app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions         {             ClientId = clientId,             Authority = Authority,             PostLogoutRedirectUri = postLogoutRedirectUri,             AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,              Notifications = new OpenIdConnectAuthenticationNotifications()             {                 // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.                 AuthorizationCodeReceived = (context) =>                 {                     var code = context.Code;                     ClientCredential credential = new ClientCredential(clientId, appKey);                     string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;                     AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));                     Task<AuthenticationResult> result = authContext.AcquireTokenByAuthorizationCodeAsync(                     code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);                      return Task.FromResult(0);                 }             }         });         app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);           // Uncomment the following lines to enable logging in with third party login providers         //app.UseMicrosoftAccountAuthentication(         //    clientId: "",         //    clientSecret: "");          //app.UseTwitterAuthentication(         //   consumerKey: "",         //   consumerSecret: "");          //app.UseFacebookAuthentication(         //   appId: "",         //   appSecret: "");          //app.UseGoogleAuthentication(         //    clientId: "",         //    clientSecret: "");     } } 

EDIT

Here's the controller code

using System.Web.Mvc;  namespace MyApp.Controllers {      public class AccueilController : Controller     {         [Authorize]         public ActionResult Index()         {             return View();         }          [Authorize(Roles = "Admin")]         public ActionResult List()         {             return View();         }     } } 

I'm logged in with Cookie Authenticatoin (email/password).

  1. I hit the Index action, I see the content of that page inside my app.
  2. If I hit the List() action, I'm redirected to OpenIdConnect login page.

1 Answers

Answers 1

The webforms user doesn't have the Admin role, but your AD user seems to have the Admin role. You can verify this by quickly querying the AspNetUserRoles table.

Decorating an action with [Authorize(Roles="Admin")] will make it accessible only to users with the role. The default behaviour for unauthorized users (irrespective of whether the user's authentication status) is to redirect them to the login page.

Your options are:

  1. Subclass the AuthorizeAttribute and override the default behavior.
  2. Redirect the user somewhere else from the login page if he's authenticated.

As far as I know, the default redirect behavior cannot be overriden without subclassing the AuthorizeAttribute like you can in ASP.NET Core.

Read More

Wednesday, October 18, 2017

Asp.Net ScriptManager causing issues with jQuery Widget

Leave a Comment

I've been using ScriptManager and the jQuery slider widget together on sites for years, but I recently came up against an issue which stopped the widget from working. I managed to resolve the problem, but it was more out of luck than expertise. I'm hoping that someone can provide reasoning behind the issue and that the fix might prove useful to others with the same issue.

I use a script aggregator which combines my scripts together - here is what it contains - all code snippets have been paired down for brevity:

vendor/Modernizr.min.js vendor/jQuery.3.0.0.min.js vendor/jQuery-UI.1.12.1.min.js vendor/jQuery-UI.TouchPunch.min.js propriertary/LoanSelector.js DefaultInit.js 

Here is the contents of DefaultInit.js:

$(document).ready(function () {     loanSelector.init(); }); 

Here are the bare bones of LoanSelector.js

var loanSelector = function () {      var pub = {}, $ctl = $("#loan-selector"),         $sliderAmount = $ctl.find(".slider-amount:first"),         $widgetAmount = $sliderAmount.find(".widget:first");      function initControl() {         initWidgetAmount(100, 2000, 100, $("#hfStartAmount").val());     }      function initWidgetAmount(min, max, step, initialVal) {         $widgetAmount.slider({         });     }      pub.init = function () {         initControl();     };      return pub; } (); 

Now this works - but only without ScriptManager on the page.

When script manager is added, the slider widget doesn't load - no errors - nothing.

The cached elements are permanent fixtures on the page - so it's not a case of the elements not existing at a point in time.

Here's the ScriptManager code:

    <asp:ScriptManager ID="SM" runat="server"         EnableCdn="true"         LoadScriptsBeforeUI="false"         EnablePageMethods="true">     </asp:ScriptManager> 

If I make changes, I can get the widget to load with the ScriptManager on the page - here's another version of LoanSelector.js that works:

var loanSelector = function () {      var pub = {}, $ctl, $sliderAmount, $widgetAmount;      function cacheElements() {         $ctl = $("#loan-selector"),         $sliderAmount = $ctl.find(".slider-amount:first"),         $widgetAmount = $sliderAmount.find(".widget:first");     }      function initControl() {         // This is new         cacheElements();          initWidgetAmount(100, 2000, 100, $("#hfStartAmount").val());     }      function initWidgetAmount(min, max, step, initialVal) {         $widgetAmount.slider({         });     }      pub.init = function () {         initControl();     };      return pub; } (); 

So, can anyone throw some light on why it might not be working and why the fix does work?

1 Answers

Answers 1

The first method populates variables as it executes:

var pub = {}, $ctl = $("#loan-selector"),     $sliderAmount = $ctl.find(".slider-amount:first"),     $widgetAmount = $sliderAmount.find(".widget:first"); 

So #loan-selector, .slider-amount:first and .widget:first need to be rendered in the page's DOM before this is called.

The second variant populates when loanSelector.init() is called, and by the place in your code where you call it the DOM is loaded.

<asp:ScriptManager> gives you a server side collection of scripts controls can add to easily, but it renders to a list of <script> tags and order can be important - another control in the page can mean that the DOM you're relying on hasn't loaded by the time your code runs. Scripts run in order but don't wait for the DOM render to finish.

jQuery has it's own component structure you can use instead, and a DOM ready method that you can rely on - you're already using it in DefaultInit.js, which is why the loanSelector.init() can access the DOM while the directly executed code does not.

You already have a fix, and your second script is better practice because it's not dependent on time of execution.

Another alternative would be to call your loanSelector function in the $(document).ready:

$(function(){     // In this DOM ready function your elements should be ready to find     var loanSelector = ...     ...      // And you may not need this, as you can init in the actual function     loanSelector.init(); }); 
Read More

Friday, April 28, 2017

Create custom User Control for Acumatica

Leave a Comment

I am attempting to create a custom User Control that is usable in the Acumatica Framework... Documentation is very limited so I was hoping someone may have some experience/examples of how best to implement?

It appears possible by creating a WebControl derived from PXWebControl & creating a global JS function with a matching name.

0 Answers

Read More

Create custom User Control for Acumatica

Leave a Comment

I am attempting to create a custom User Control that is usable in the Acumatica Framework... Documentation is very limited so I was hoping someone may have some experience/examples of how best to implement?

It appears possible by creating a WebControl derived from PXWebControl & creating a global JS function with a matching name.

0 Answers

Read More

Create custom User Control for Acumatica

Leave a Comment

I am attempting to create a custom User Control that is usable in the Acumatica Framework... Documentation is very limited so I was hoping someone may have some experience/examples of how best to implement?

It appears possible by creating a WebControl derived from PXWebControl & creating a global JS function with a matching name.

0 Answers

Read More

Create custom User Control for Acumatica

Leave a Comment

I am attempting to create a custom User Control that is usable in the Acumatica Framework... Documentation is very limited so I was hoping someone may have some experience/examples of how best to implement?

It appears possible by creating a WebControl derived from PXWebControl & creating a global JS function with a matching name.

0 Answers

Read More

Create custom User Control for Acumatica

Leave a Comment

I am attempting to create a custom User Control that is usable in the Acumatica Framework... Documentation is very limited so I was hoping someone may have some experience/examples of how best to implement?

It appears possible by creating a WebControl derived from PXWebControl & creating a global JS function with a matching name.

0 Answers

Read More

Monday, April 3, 2017

Asp.net redirect to page based Azure AD user

Leave a Comment

Hello, I have asp.net web forms project with Azure AD authentication, now in this project i have two forms "webfrom1.aspx" and "webform2.aspx" now i need that, if user "it" is login then redirect to "web form 1.aspx" or if user "finance" login then redirect to "web form 2.aspx". please help me! Thanks

2 Answers

Answers 1

As part of your authentication configuration, you could specify a RedirectUri to a page that could make the decision on where to redirect (webform1.aspx or webform2.aspx) based on the user.

Answers 2

if user "it" is login then redirect to "web form 1.aspx" or if user "finance" login then redirect to "web form 2.aspx".

You cannot redirect to different URLs from Azure AD based on user's information. Instead, you will have place the logic at landing page of your application such as Home page URL.

enter image description here

Then you direct user to desired page based on your business logic. For example,

protected void Page_Load(object sender, EventArgs e) {     if (!IsPostBack)     {         if (User != null && User.Identity.IsAuthenticated)         {             switch (User.Identity.Name.ToLower())             {                 case "it":                     Response.Redirect("~/WebForm1.aspx");                     break;                 case "finance":                     Response.Redirect("~/WebForm2.aspx");                     break;             }         }     } } 

Please feel free to take a look at my working sample code written in ASP.NET Core using Visual Studio 2017 at GitHub.

Read More

Sunday, July 24, 2016

Share Authentication Across Secure and Nonsecure

Leave a Comment

I am in the situation where the site I am working on, the manager wants to allow the user to log in and not worry if they logged in through http or https. Based on another SO question (how can I share an asp.net session between http and https) I thought this would be possible if I set secure = false on the cookie. To add to this, we use a subdomain for the secure part of the site. So for http we use site.com, while https uses secure.site.com. So I tried setting the domain for the authentication in the web.config.

<authentication mode="Forms">      <forms loginUrl="/account/login"     protection="All" timeout="30" name=".ASPXAUTH" path="/"     requireSSL="false" slidingExpiration="true" defaultUrl="/"     cookieless="UseDeviceProfile" domain="site.com"     enableCrossAppRedirects="false" />  </authentication> 

Am I doing this all wrong? I understand there are some security concerns and I was going to address them when a request is made. I just want to allow the user to log in once and be remembered across http and https. Thanks.

1 Answers

Answers 1

I think you have wrong domain in your web.config. You should change it to

domain=".site.com" 

So you're allowing your forms auth cookie to live both on ssl.site.com and no-ssl.site.com domain for example.

All that being said any kind of security starts with https:// over all your solution - otherwise you're open into man-in-the-middle attacks (web proxy can inject inappropriate content into your solution, they can steal your authorization cookie & use it in flow on https://ssl.site.com etc.

Read More

Tuesday, June 21, 2016

Create custom User Control for Acumatica

Leave a Comment

I am attempting to create a custom User Control that is usable in the Acumatica Framework... Documentation is very limited so I was hoping someone may have some experience/examples of how best to implement?

It appears possible by creating a WebControl derived from PXWebControl & creating a global JS function with a matching name.

0 Answers

Read More

Friday, April 29, 2016

How to make a aspxdocumentviewer direction rtl?

Leave a Comment

My goal is to have a right justified report. So I changed the text align from right to middle center justified because right justified alignment is not supported in document viewer I think. Now my text is justified but the direction is ltr. How to make it rtl?

I tried to set a rtl style for created html from document viewer by css and jquery but it ignores styles.

I am using devexpress report suit 15.2 with asp.net webforms.

How to do it?

1 Answers

Answers 1

Finally I fount it.

By adding the following code to the aspxdocumentviewer I was able to write css codes for it.

<dx:ASPxDocumentViewer ID="ASPxDocumentViewer1" runat="server">     <ToolbarItems>         <dx:ReportToolbarButton ItemKind="Search" />         <dx:ReportToolbarSeparator />     </ToolbarItems>     <SettingsReportViewer UseIFrame="false" /> </dx:ASPxDocumentViewer>  <style type="text/css">     body{         direction:rtl;     } </style> 
Read More

Friday, April 1, 2016

Pass a sql parameter value to devexpress report?

Leave a Comment

I want to pass a sql parameter from user form to my report class but it is not working and it does not create the report and when I open the report designer tab again after adding ID argument to the report class it refresh the report and delete my components.

What is the problem?

Here is my report class:

public SodoorZemanatName(long ID)     {         InitializeComponent(ID);     }      protected override void Dispose(bool disposing)     {         if (disposing && (components != null))         {             components.Dispose();         }         base.Dispose(disposing);     }      #region Designer generated code     private void InitializeComponent(long ID)     {             this.components = new System.ComponentModel.Container();             DevExpress.DataAccess.Sql.CustomSqlQuery customSqlQuery1 = new DevExpress.DataAccess.Sql.CustomSqlQuery();             DevExpress.DataAccess.Sql.QueryParameter queryParameter1 = new DevExpress.DataAccess.Sql.QueryParameter();             System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SodoorZemanatName));             this.topMarginBand1 = new DevExpress.XtraReports.UI.TopMarginBand();             this.detailBand1 = new DevExpress.XtraReports.UI.DetailBand();             this.bottomMarginBand1 = new DevExpress.XtraReports.UI.BottomMarginBand();             this.sqlDataSource2 = new DevExpress.DataAccess.Sql.SqlDataSource(this.components);             ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();             this.topMarginBand1.HeightF = 100F;             this.topMarginBand1.Name = "topMarginBand1";             this.detailBand1.HeightF = 100F;             this.detailBand1.Name = "detailBand1";             this.bottomMarginBand1.HeightF = 100F;             this.bottomMarginBand1.Name = "bottomMarginBand1";             this.sqlDataSource2.ConnectionName = "Context";             this.sqlDataSource2.Name = "sqlDataSource2";             customSqlQuery1.Name = "Query";             queryParameter1.Name = "ID";             queryParameter1.Type = typeof(long);             queryParameter1.ValueInfo = "0";             queryParameter1.Value = ID;             customSqlQuery1.Parameters.Add(queryParameter1);             customSqlQuery1.Sql = "select * from LG_Garanti where ID=@ID";             this.sqlDataSource2.Queries.AddRange(new DevExpress.DataAccess.Sql.SqlQuery[] {             customSqlQuery1});             this.sqlDataSource2.ResultSchemaSerializable = resources.GetString("sqlDataSource2.ResultSchemaSerializable");     this.Bands.AddRange(new DevExpress.XtraReports.UI.Band[] {     this.topMarginBand1,     this.detailBand1,     this.bottomMarginBand1});     this.ComponentStorage.AddRange(new System.ComponentModel.IComponent[] {     this.sqlDataSource2});     this.DataSource = this.sqlDataSource2;     this.Version = "15.2";     ((System.ComponentModel.ISupportInitialize)(this)).EndInit(); } #endregion 

And here is my calling:

SodoorZemanatName report = new SodoorZemanatName(1); ASPxDocumentViewer1.ReportTypeName = "SodoorZemanatName"; ASPxDocumentViewer1.Report = report; 

1 Answers

Answers 1

I guess you want to (1) click the button, (2) pass an ID then (3) open the report has content depends on that ID. So this is the way I did it, (I'm not sure is there any other way because devexpress is not open source):

  1. Design your dataset, it contains only the info you want to show using the dataset tool of Visual Studio. i.e. id, name, address, ..... Name that dataset = ReportDataset. That dataset has 1 table named MyTable.
  2. Design your report named MyReport using the GUI tool (remember to choose XtraReport), and select the datasource = that ReportDataset, do not edit the code generated by the GUI tool. Just use the GUI, click & click to add labels, add value from the ReportDataset.
  3. In you form, window form or whatever, the below should be inside the function triggered by button_click event (your "calling" in the last snippet of your question):

    DataSet new_ds = new DataSet(); ReportDataset.MyTable runtime_data = new ReportDataset.MyTable(); //get data from your database according to ID, Row by row  //then add them to the runtime_data table //in your case, add all the result of "select * from LG_Garanti where ID=@ID"; runtime_data.Rows.Add(new object[] {.blah blah..});// just add row, use whatever method you like new_ds.Tables.Add(runtime_data); //from this point, new_ds contains runtime data of the row(s) you want ID. //now just link that dynamic dataset to your designed report MyReport my_report = new MyReport(); my_report.DataSource = new_ds; // Show the print preview or do whatever you want  ReportPrintTool printTool = new ReportPrintTool(my_report); printTool.ShowRibbonPreviewDialog(); 

The above is to make it more flexible since the report can use its own dataset with the mixture of different tables,.... You can make it easier by reuse your own dataset in step 1. Hope this helps.

Read More

Thursday, March 10, 2016

CORS endpoints on asp.net Webforms [WebMethod] endpoints

Leave a Comment

I am trying to add some [WebMethod] annotated endpoint functions to a Webforms style web app (.aspx and .asmx).

I'd like to annotate those endpoints with [EnableCors] and thereby get all the good ajax-preflight functionality.

VS2013 accepts the annotation, but still the endpoints don't play nice with CORS. (They work fine when used same-origin but not cross-origin).

I can't even get them to function cross-origin with the down and dirty

HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*"); 

approach -- my browsers reject the responses, and the cross-origin response headers don't appear.

How can I get CORS functionality in these [WebMethod] endpoints?

6 Answers

Answers 1

I recommend double-checking you have performed all steps on this page: CORS on ASP.NET

In addition to:

Response.AppendHeader("Access-Control-Allow-Origin", "*"); 

Also try:

Response.AppendHeader("Access-Control-Allow-Methods","*"); 

/ Try adding directly in web config:

   <httpProtocol>      <customHeaders>        <add name="Access-Control-Allow-Methods" values="*" />        <add name="Access-Control-Allow-Headers" values="Content-Type" />      </customHeaders>    <httpProtocol> 

Failing that, you need to ensure you have control over both domains.

Answers 2

If you need the preflight request, e.g. so you can send authenticated requests, you are not able to set Access-Control-Allow-Origin: *. It must be a specific Origin domain.
Also you must set the Access-Control-Allow-Methods and Access-Control-Allow-Headers response headers, if you are using anything besides the defaults.
(Note these constraints are just how CORS itself works - this is how it is defined.)

So, it's not enough to just throw on the [EnableCors] attribute, you have to set values to the parameters:

[EnableCors(origins: "https://www.olliejones.com", headers: "X-Custom-Header", methods: "PUT", SupportsCredentials = true)] 

Or if you want to do things manually and explicitly:

HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "https://www.olliejones.com"); HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Headers", "X-Custom-Header"); HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", "PUT"); HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Credentials", "true"); 

One last thing - you do have to call .EnableCors() on initiation. In e.g. MVC or WebAPI, you would call this on HttpConfiguration, when registering the config and such - however I have no idea how it works with WebForms.

Answers 3

Here is a good read. It sounds like you are trying to use jQuery and json-p. I had a problem with this as well while building an API and trying to access it from a different domain.

JSONP is inherently read-only. You want to use CORS.

Here is the difference. So, JSONP or CORS?

Here is how to implement CORS in jQuery. http://www.eriwen.com/javascript/how-to-cors/

Answers 4

I think your code looks good, but IIS does not send the header entity alone with response expectedly. Please check whether IIS is configured properly.

If CORS doesn't work for your particularity problem, maybe jsonp is another possible way.

Answers 5

If you use the AppendHeader method to send cache-specific headers and at the same time use the cache object model (Cache) to set cache policy, HTTP response headers that pertain to caching might be deleted when the cache object model is used. This behavior enables ASP.NET to maintain the most restrictive settings. For example, consider a page that includes user controls. If those controls have conflicting cache policies, the most restrictive cache policy will be used. If one user control sets the header "Cache-Control: Public" and another user control sets the more restrictive header "Cache-Control: Private" via calls to SetCacheability, then the "Cache-Control: Private" header will be sent with the response.

You can create a httpProtocol in web config for customHeaders.

<httpProtocol>      <customHeaders>        <add name="Access-Control-Allow-Methods" values="*" />              </customHeaders>    <httpProtocol> 

Answers 6

You can do like this in MVC

[EnableCors(origins: "*", headers: "*", methods: "*")] public ActionResult test() {      Response.AppendHeader("Access-Control-Allow-Origin", "*");      return View(); } 
Read More