Showing posts with label asp.net-mvc-4. Show all posts
Showing posts with label asp.net-mvc-4. Show all posts

Monday, April 16, 2018

Creating HttpClient in older ASP.NET MVC app without Startup

Leave a Comment

I have an old version of ASP.NET MVC app that doesn't have a Startup.cs. I wanted to implement a clean way to have an HttpClient that I would use for my API calls to third parties.

Here's what I've done so far based on some ideas/recommendations I've received for this question. The problem is that when I make the API call, it goes nowhere. I put it in a try catch but I'm not even getting an exception. The API provider tells me that they're not seeing the search parameter.

First, I created this HttpClientAccessor for lazy loading.

public static class HttpClientAccessor {    public static Func<HttpClient> ValueFactory = () =>    {       var client = new HttpClient();        client.BaseAddress = new Uri("https://apiUrl.com");       client.DefaultRequestHeaders.Accept.Clear();       client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));       client.DefaultRequestHeaders.TryAddWithoutValidation("APIAccessToken", "token1");       client.DefaultRequestHeaders.TryAddWithoutValidation("UserToken", "token2");         return client;    };     private static Lazy<HttpClient> client = new Lazy<HttpClient>(ValueFactory);     public static HttpClient HttpClient    {       get { return client.Value; }    } } 

I then created an API client of my own so that I can have the API call functions in one place which looks like this:

public class MyApiClient {     public async Task GetSomeData()    {        var client = HttpClientAccessor.HttpClient;        try        {            var result = await client.GetStringAsync("somedata/search?text=test");            var output = JObject.Parse(result);        }        catch(Exception e)        {            var error = e.Message;        }     } } 

Then in my ASP.NET Controller action, I do this:

public class MyController : Controller {    private static readonly MyApiClient _apiClient = new MyApiClient ();     public ActionResult ApiTest()    {        var data = _apiClient.GetSomeData().Wait();    } } 

Any idea where my mistake is?

UPDATE: This simple approach works fine:

public class MyController : Controller {    private static readonly HttpClient _client = new HttpClient();     public ActionResult ApiTest()    {        _client.BaseAddress = new Uri("https://apiUrl.com");        _client.DefaultRequestHeaders.Accept.Clear();        _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));        _client.DefaultRequestHeaders.TryAddWithoutValidation("APIAccessToken", "token1");        _client.DefaultRequestHeaders.TryAddWithoutValidation("UserToken", "token2");         var response = _client.GetStringAsync("somedata/search?text=test").Result;    } } 

4 Answers

Answers 1

As mentioned, dependency injection is not being utilized so technically there is no need for a composition root where these things would have been initialized.

If there is no need to actually initialize the client on start up you could consider using a Lazy singleton approach.

An example

public static class HttpClientAccessor {    public static Func<HttpClient> ValueFactory = () => {       var client = new HttpClient();        client.BaseAddress = new Uri("https://apiUrl.com");       client.DefaultRequestHeaders.Accept.Clear();       client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));       client.DefaultRequestHeaders.TryAddWithoutValidation("APIAccessToken", "token1");       client.DefaultRequestHeaders.TryAddWithoutValidation("UserToken", "token2");         return client;    };     private static Lazy<HttpClient> client = new Lazy<HttpClient>(ValueFactory);     public static HttpClient HttpClient {       get { return client.Value; }    } } 

The factory delegate of the Lazy<HttpClient> can be made more complex if additional settings are needed on the client.

And where ever the client is needed you call the service

var client = HttpClientAccessor.HttpClient;  var response = await client.GetStringAsync("{url}"); 

the client will be initialized on first use and you will get the same instance on subsequent calls for the instance.

As used in your controller, you are mixing async calls with blocking calls line .Wait() or .Result. This can lead to deadlocks and should be avoided.

public class MyController : Controller {     private static readonly MyApiClient _apiClient = new MyApiClient ();      public async Task<ActionResult> ApiTest() {         var data = await _apiClient.GetSomeData();          //...     } } 

Code should be async all the way through.

Reference Async/Await - Best Practices in Asynchronous Programming

Answers 2

The Application_Start() method is the right place. But I would have to ask: why you have to create the HttpClient instance when the "application starts"? In general, HttpClient is some "resource" and you can just create it when you want to use it. And also it's no need to set it as "Singleton". Just wrap it in the using block. (Maybe you want to make the API wrapper as Singleton?)

public class APICaller {      //make the APICaller singleton in some way here     //...       // the api calling method:     public string CallAPI(string someParameter)     {         var response = "";         using (var client = new HttpClient())         {             //calling the API         }         return response;     } } 

Answers 3

The main issue is incorrect asynchronous code.

You are using Task.Wait() which alongside asynchronous MyApiClient.GetSomeData() causes a deadlock on ASP.NET request context. That is a very common issue, see An async/await example that causes a deadlock on StackOverflow. Code with Task.Result property call is working because HttpClient.GetStringAsync() probably takes preventative measures against deadlocks. See Task.ConfigureAwait() page on MSDN and Best practice to call ConfigureAwait for all server-side code discussion on StackOverflow.

There are multiple options to write a singleton using C#. See Implementing the Singleton Pattern in C# article by Jon Skeet for a detailed overview.

Answers 4

As you mentioned, you can just use a static class member on the controller. HttpClient only needs to be setup once; so do this in the static constructor of the controller. Also, make sure that you use async/await for async methods, especially with long running http requests. IOC and an abstraction layer would make sense depending on your needs.

using System; using System.Net.Http; using System.Threading.Tasks;  namespace TestApi {     public class MyController : Controller     {         private const string ApiUrlString = "https://apiUrl.com";         private static readonly Uri ApiUri = new Uri(ApiUrlString);         private static readonly HttpClient RestClient;          static MyController()         {             this.RestClient = new HttpClient{                 BaseAddress = ApiUri             }             this.RestClient.DefaultRequestHeaders.Accept.Clear();             this.RestClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));             RestClient.DefaultRequestHeaders.TryAddWithoutValidation("APIAccessToken", "token1");             RestClient.DefaultRequestHeaders.TryAddWithoutValidation("UserToken", "token2");         }          public async Task<IActionResult> ApiTest()         {             return this.Ok(await this.RestClient.GetStringAsync("somedata/search?text=test"));         }     } } 
Read More

Sunday, November 5, 2017

Validating MVC model extracted from session

Leave a Comment

I’m making an MVC online web shopping app, so after the shopping cart page I’ve got ProcessStepOne action in which the user should fill his data.

[Authentication]  public ActionResult ProcessStepOne() {      ProcessOrderViewModel model = GetOrderData();                 return View("ProcessOrderStepOne", model);  }   private ProcessOrderViewModel GetOrderData() {     ProcessOrderViewModel model = (ProcessOrderViewModel)Session["Process"];     if(model==null)     {         model = new ProcessOrderViewModel();     }     return model; } 

My ProcessOrderViewModel model is:

public class ProcessOrderViewModel  {     public ProcessOrderViewModel()     {         this.PrivateIndividualData = new PrivateIndividualModel();         this.OrderDiscoutPrice = new OrderDiscountPriceModel();     }     public PrivateIndividualModel PrivateIndividualData { get; set; }     public OrderDiscountPriceModel OrderDiscoutPrice { get; set; } } 

And my View ProcessOrderStepOne is:

@model  ProcessOrderViewModel  <form id="formOrderData" method="post" action="@Url.Action("ProcessStepTwo")">                <div class="row">         <div class="col-xs-12 col-sm-6">             <div class="form-group">                 <label> First Name </label>                 @Html.TextBoxFor(x => x.PrivateIndividualData.FirstName, new { @class = "form-control" })                 @Html.ValidationMessageFor(x => x.PrivateIndividualData.FirstName)             </div>         </div>          <div class="col-xs-12 col-sm-6">             <div class="form-group">                 <label> Last Name </label>                 @Html.TextBoxFor(x => x.PrivateIndividualData.Family, new { @class = "form-control" })                 @Html.ValidationMessageFor(x => x.PrivateIndividualData.Family)             </div>         </div>          <div class="col-xs-12 col-sm-6">             <div class="form-group">                 <label>Email</label>                 @Html.TextBoxFor(x => x.PrivateIndividualData.Email, new { @class = "form-control" })                 @Html.ValidationMessageFor(x => x.PrivateIndividualData.Email)             </div>         </div>          <div class="col-xs-12 col-sm-6">             <div class="form-group">                 <label for="tel">@StringResources.GetResourceString("UserManagement", "Register_PhoneLabel")</label>                 @Html.TextBoxFor(x => x.PrivateIndividualData.Telephone, new { @class = "form-control" })                 @Html.ValidationMessageFor(x => x.PrivateIndividualData.Telephone)             </div>         </div>     </div> </form> 

So, my second step is just checking the values the user has entered and then show him for verification. If everything is OK I save the data in the session so the user can return to the previous ProcessStepOne action or proceed to making order with the MakeOrder action.

public ActionResult ProcessStepTwo(ProcessOrderViewModel model) {     if (ModelState.IsValid)     {         Session["Process"] = model;         return View("ProcessOrderStepTwo", model);     }     return View("ProcessOrderStepOne", model);  } 

And the view is:

@model ProcessOrderViewModel  <section class="form-section">                         <p>         <a href='@Url.Action("ProcessStepOne")'>CHANGE</a>         <span class="text-semibold">Name:</span> @Model.PrivateIndividualData.FirstName <br>         <span class="text-semibold">Last Name:</span> @Model.PrivateIndividualData.Family <br>         <span class="text-semibold">E-mail:</span>  @Model.PrivateIndividualData.Email<br>         <span class="text-semibold">Телефон:</span>@Model.PrivateIndividualData.Telephone <br>                                </p> </section> <a href='@Url.Action("MakeOrder")'>PROCEED TO MAKE ORDER</a> 

And, here is my last action which just gets the data from the session:

public ActionResult MakeOrder() {     var data = (ProcessOrderViewModel)System.Web.HttpContext.Current.Session["Process"];        // make order with this data     return View("Thank You"); } 

So my questions are: in the MakeOrder action I just take the model from the session without any verification. How can I validate it again and also is it right storing the data in session so I can return to make modifications or proceed to make an order. Is this the right thing to do because I can't think of another way.

1 Answers

Answers 1

In the MakeOrder action I just take the model from the session without any verification. How can I validate it again?

You can use TryValidateModel in your controller to re-validate your model after retrieving it from Session. However, from my testing it appears that this method only validates one level deep in the model. With this in mind, you code would be something like the following:

public ActionResult MakeOrder() {     var data = (ProcessOrderViewModel)System.Web.HttpContext.Current.Session["Process"];       bool individualDataValid = TryValidateModel(data.PrivateIndividualData);     bool discountDataValid = TryValidateModel(data.OrderDiscoutPrice);      if (individualDataValid && discountDataValid)     {         // make order with this data         return View("Thank You");     }      //Error condition     return View("Error"); } 

Is it right storing the data in session so I can return to make modifications or proceed to make an order. Is this the right thing to do because I can't think of another way.

Personally, I avoid using Session if I can. However, I think it is perfectly justifiable for a "Shopping Basket" requirement such as yours. What I would say is that from a maintenance and testing perspective, it may be better if you create a wrapper object for Session access. See Session variables in ASP.NET MVC.

This has the following benefits:

  • All your Session access is in one place. Not scattered liberally across controllers.
  • No "Magic Strings" used across your solution for session access.
  • As an added bonus, because you have abstracted session access out into an object, your controllers can be more "testable" (provided it is not a static wrapper).
Read More

Tuesday, September 26, 2017

ASP.net MVC Razor view on Mono Mac

Leave a Comment

I created an ASP.net MVC application using Visual Studio Community Edition for Mac. When I ran the default HomeController and Index view work fine. Then I made a BookController and inside views added a folder Book and Index.cshtml file. However I keep getting the following error:

System.InvalidOperationException The view found at '~/Views/book/Index.cshtml' was not created.

Description:

HTTP 500.Error processing request.

Details:

Non-web exception. Exception origin (name of application or object): System.Web.Mvc. Exception stack trace: at System.Web.Mvc.BuildManagerCompiledView.Render (System.Web.Mvc.ViewContext viewContext, System.IO.TextWriter writer) [0x00061] in :0 at System.Web.Mvc.ViewResultBase.ExecuteResult (System.Web.Mvc.ControllerContext context) [0x00080] in :0 at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult (System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ActionResult actionResult) [0x00000] in :0 at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive (System.Collections.Generic.IList1[T] filters, System.Int32 filterIndex, System.Web.Mvc.ResultExecutingContext preContext, System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ActionResult actionResult) [0x0000b] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive (System.Collections.Generic.IList1[T] filters, System.Int32 filterIndex, System.Web.Mvc.ResultExecutingContext preContext, System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ActionResult actionResult) [0x0009b] in :0 at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters (System.Web.Mvc.ControllerContext controllerContext, System.Collections.Generic.IList1[T] filters, System.Web.Mvc.ActionResult actionResult) [0x0000a] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass21+<>c__DisplayClass2b.<BeginInvokeAction>b__1c () [0x0008a] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass21.<BeginInvokeAction>b__1e (System.IAsyncResult asyncResult) [0x00041] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncResult1[TResult].CallEndDelegate (System.IAsyncResult asyncResult) [0x00000] in :0 at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncResultBase1[TResult].End () [0x00029] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult] (System.IAsyncResult asyncResult, System.Object tag) [0x00007] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction (System.IAsyncResult asyncResult) [0x00000] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Controller.<BeginExecuteCore>b__1d (System.IAsyncResult asyncResult, System.Web.Mvc.Controller+ExecuteCoreState innerState) [0x00000] in <cc73190bab9d435c831510ff295c572a>:0 at (wrapper delegate-invoke) System.Web.Mvc.Async.EndInvokeVoidDelegate1[System.Web.Mvc.Controller+ExecuteCoreState]:invoke_void_IAsyncResult_TState (System.IAsyncResult,System.Web.Mvc.Controller/ExecuteCoreState) at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncVoid1[TState].CallEndDelegate (System.IAsyncResult asyncResult) [0x00000] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncResultBase1[TResult].End () [0x00029] in :0 at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult] (System.IAsyncResult asyncResult, System.Object tag) [0x00007] in :0 at System.Web.Mvc.Async.AsyncResultWrapper.End (System.IAsyncResult asyncResult, System.Object tag) [0x00000] in :0 at System.Web.Mvc.Controller.EndExecuteCore (System.IAsyncResult asyncResult) [0x00000] in :0 at System.Web.Mvc.Controller.b__15 (System.IAsyncResult asyncResult, System.Web.Mvc.Controller controller) [0x00000] in :0 at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncVoid1[TState].CallEndDelegate (System.IAsyncResult asyncResult) [0x00000] in <cc73190bab9d435c831510ff295c572a>:0 at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncResultBase1[TResult].End () [0x00029] in :0 at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult] (System.IAsyncResult asyncResult, System.Object tag) [0x00007] in :0 at System.Web.Mvc.Async.AsyncResultWrapper.End (System.IAsyncResult asyncResult, System.Object tag) [0x00000] in :0 at System.Web.Mvc.Controller.EndExecute (System.IAsyncResult asyncResult) [0x00000] in :0 at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute (System.IAsyncResult asyncResult) [0x00000] in :0 at System.Web.Mvc.MvcHandler.b__5 (System.IAsyncResult asyncResult, System.Web.Mvc.MvcHandler+ProcessRequestState innerState) [0x00000] in :0 at (wrapper delegate-invoke) System.Web.Mvc.Async.EndInvokeVoidDelegate1[System.Web.Mvc.MvcHandler+ProcessRequestState]:invoke_void_IAsyncResult_TState (System.IAsyncResult,System.Web.Mvc.MvcHandler/ProcessRequestState)
at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncVoid
1[TState].CallEndDelegate (System.IAsyncResult asyncResult) [0x00000] in :0 at System.Web.Mvc.Async.AsyncResultWrapper+WrappedAsyncResultBase`1[TResult].End () [0x00029] in :0 at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult] (System.IAsyncResult asyncResult, System.Object tag) [0x00007] in :0 at System.Web.Mvc.Async.AsyncResultWrapper.End (System.IAsyncResult asyncResult, System.Object tag) [0x00000] in :0 at System.Web.Mvc.MvcHandler.EndProcessRequest (System.IAsyncResult asyncResult) [0x00000] in :0 at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest (System.IAsyncResult result) [0x00000] in :0 at System.Web.HttpApplication.async_handler_complete_cb (System.IAsyncResult ar) [0x00015] in /private/tmp/source-mono-2017-02/bockbuild-2017-02/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class

I Googled around and people said I have to make sure the web.config inside views folder is as follows:

<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 

That is it matches the version of system.web.mvc and looking inside system.web.mvc the version is:

5.2.3

And the default view in HomeController works. I am trying out ASP.net MVC on Mac first time and never had this type of problem on Windows.

Any help is welcome.

Update Controller code:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TestMvcFromMac.Models;  namespace TestMvcFromMac.Controllers {  public class BookController : Controller  {      //Category category = new Category();       public ActionResult Index()     {         return View ();     }    } } 

2 Answers

Answers 1

I am guessing that the problem might happen because you are creating all the stuff manually and not letting template engine generate something for you like in visual studio in windows (Personally did not try Mac version so I dont know exactly how it looks like). Anyway lets suppose you have created your controller and then Book folder. But there is something to configure manually as well if its not configured, and that is routing for current controller.

You have to set routing logic in RouteConfig file class, so you would have something like this:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");          routes.MapRoute(             name: "Default",             url: "{controller}/{action}/{id}",             defaults: new { controller = "Book" , action = "Index", id = UrlParameter.Optional }         ); 

Refere to this question for more details here.

Another version I guess is setting the view name inside return statement itself like this:

return View("Index") 

If this is not the case please let me know so I can see any other alternative for this problem...

Answers 2

medium Visual Studio for Mac 2017 MVC version 5.3.2

I had the exact same problem. My Index views was working and other actions was not, giving the same error. I don't know why but when I add a route attribute manually in the beginning of the action in the controller, it was the solutions for my case

steps 1) add following line to the routeconfig.cs file

 routes.MapMvcAttributeRoutes(); 

2) add route attribute to the action manually like

  [Route("ViewName/ActionName/")]     public ActionResult ActionName()     {....   return View(the_model);    } 
Read More

Wednesday, September 20, 2017

REST API not working with MySQL

Leave a Comment

I am creating a REST API using MySQL DB in visual studio 2015 in asp.net mvc 4.5. I have done each and every step which in needed to run the API using MySQL, but I am getting this exception.

{"Message":"An error has occurred.","ExceptionMessage":"Format of the initialization string does not conform to specification starting at index 121.","ExceptionType":"System.ArgumentException","StackTrace":" at System.Data.Common.DbConnectionOptions.GetKeyValuePair(String connectionString, Int32 currentPosition, StringBuilder buffer, Boolean useOdbcRules, String& keyname, String& keyvalue)\r\n at System.Data.Common.DbConnectionOptions.ParseInternal(Hashtable parsetable, String connectionString, Boolean buildChain, Hashtable synonyms, Boolean firstKey)\r\n at System.Data.Common.DbConnectionOptions..ctor(String connectionString, Hashtable synonyms, Boolean useOdbcRules)\r\n at System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(String value)\r\n at MySql.Data.MySqlClient.MySqlConnectionStringBuilder..ctor(String connStr)\r\n at MySql.Data.MySqlClient.MySqlConnection.set_ConnectionString(String value)\r\n at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.b__18(DbConnection t, DbConnectionPropertyInterceptionContext1 c)\r\n at System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action2 operation, TInterceptionContext interceptionContext, Action3 executing, Action3 executed)\r\n at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.SetConnectionString(DbConnection connection, DbConnectionPropertyInterceptionContext1 interceptionContext)\r\n at System.Data.Entity.Internal.LazyInternalConnection.InitializeFromConnectionStringSetting(ConnectionStringSettings appConfigConnection)\r\n at System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig(String name, AppConfig config)\r\n at System.Data.Entity.Internal.LazyInternalConnection.Initialize()\r\n at System.Data.Entity.Internal.LazyInternalConnection.CreateObjectContextFromConnectionModel()\r\n at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()\r\n at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)\r\n at System.Data.Entity.Internal.Linq.InternalSet1.Initialize()\r\n at System.Data.Entity.Internal.Linq.InternalSet1.GetEnumerator()\r\n at System.Data.Entity.Infrastructure.DbQuery1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()\r\n at System.Collections.Generic.List1..ctor(IEnumerable1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)\r\n at RestWithMySQL.Controllers.ProductsController.Get() in D:\Work\DOT NET API\RestWithMySQL\RestWithMySQL\Controllers\ProductsController.cs:line 19"}

I think there is a problem in my connection string. I have searched for it but couldn't find the exact solution.

<connectionStrings>     <!--<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-RestWithMySQL-20170911031521.mdf;Initial Catalog=aspnet-RestWithMySQL-20170911031521;Integrated Security=True" providerName="System.Data.SqlClient" />-->     <add name="ProductEntities" connectionString="metadata=res://*/ProductsModel.csdl|res://*/ProductsModel.ssdl|res://*/ProductsModel.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot; server=localhost;user id=root;database=accurate_dev;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="MySql.Data.MySqlClient"/>     <!--<remove name="LocalMySqlServer" /><add name="LocalMySqlServer" connectionString="" providerName="MySql.Data.MySqlClient" />--> </connectionStrings> 

Any help would be highly appreciated.

3 Answers

Answers 1

Strip down your connection string to the bare minimum. Add options back and see what causes it to fail. For example integrated security=true is not going to work with MySQL. You will need to add a password instead.

Form here: ASP.NET MVC 4 EF5 with MySQL

Try something closer to this:

<add name="DefaultConnection" providerName="MySql.Data.MySqlClient" connectionString="Data Source=localhost;port=3306;Initial Catalog=api_db;User Id=root;password=''"/>

Answers 2

Shouldn't your connection string have another quote in it at the end?

<connectionStrings>     <add name="ProductEntities" connectionString="metadata=res://*/ProductsModel.csdl|res://*/ProductsModel.ssdl|res://*/ProductsModel.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot; server=localhost;user id=root;database=accurate_dev;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="MySql.Data.MySqlClient"/> </connectionStrings> 

Further: In order to use the MySQL provider, consider these steps provided in this answer

Answers 3

You are missing a closing &quot; at the end of your connection string.

Read More

Friday, June 23, 2017

MVC WebGrid paging action changing upon navigation

Leave a Comment

I'm working in an MVC app that is using a webgrid with paging. The grid itself is rendered in a partial view called _Results.cshtml and is rendered in a div on the index.cshtml page using

Html.RenderPartial("_Results", Model.Results); 

The partial grid as well as a few other form controls on index.cshtml are wrapped in a form called ResultsAction using:

@using (Ajax.BeginForm("ResultsAction", "Results", new AjaxOptions..... 

When intially navigating to the index.cshtml, the grid populates as expected and hovering over any of the paging links correctly display:

http://localhost/ResultsAction?page=<page#> 

Each row in the grid has a link to a detail page. This works as expected and the detail page has a link to return to the result grid using:

@Html.ActionLink("Return To Results", "Index", "Results") 

Now the problem. This redirects me back to the Index.cshtml just fine but now when I hover over any of the paging links in the grid, they incorrectly are using:

http://localhost/Index?page=<page#> 

Which is the wrong controller action so paging no longer functions. My understanding was the paging links should issue a Get using the form name as the action, yet it's being overridden somehow when I navigate to detail then back again. Does anyone know what's causing this behavior or how I can specify the paging links to always use the same controller action?

EDIT: Posting code of partial view as requested:

@model IEnumerable<ispPR_GetInquiryRecords_Result>  @{     Layout = null; }  <input id="searchListCnt" type="hidden" value="@Model.Count()" />  <div id="gridSearch">  @{      var grid = new WebGrid(selectionFieldName: "SelectedRow", canSort: false, canPage: true, rowsPerPage: 10, ajaxUpdateContainerId: "gridSearch");  var virtualCount = Model != null && Model.Count() > 0 ? Model.First().VirtualCount : 0;  grid.Bind(Model, rowCount: (int)virtualCount, autoSortAndPage: false); }      <div id="gridContent">     @grid.GetHtml(     htmlAttributes: new { id = "inqgrid" },     tableStyle: "webGrid",                   fillEmptyRows: false,                   footerStyle: "gridFooter",                   displayHeader: true,                   alternatingRowStyle: "alt",                   selectedRowStyle: "select",                   mode: WebGridPagerModes.All,                   columns: grid.Columns(                   grid.Column("PriceStatus",header:"Price Status"),                       grid.Column("CustomerName","Customer Name"),                       grid.Column("EndUserName", "End User"),                       grid.Column("ContractNumber","Contract"),                       grid.Column("PriceLevel", "Level"),                       grid.Column("ProductDescription", "Product Code"),                         grid.Column(                                              header: "Break Qty",                                                                        format: @<text>@item.QuantityBreak.ToString() / @item.QuantityBreakUOM </text>                                          ),                        grid.Column("BeginDate", "Begin Date", format: item =>string.Format("{0:d}", item.BeginDate)),                       grid.Column("EndDate","End Date",format: item =>string.Format("{0:d}", item.EndDate)),                          grid.Column(                                         header: "Price in PricingUOM",                                         format: item =>                                             {                                                 var res = Html.FormatToDecimals((decimal)item.PriceInPricingUOM, (int)item.Numdecimals);                                                  switch ((bool)@item.HasDetail)                                                 {                                                     case true:                                                           return Html.ActionLink(res + " / " + (string)item.PricingUOM, "InquiryDetails", new { @id = @item.PriceMasterID }, new { @class = "item-link2", @id = "lnk_" + @item.PriceMasterID });                                                     case false:                                                         return Html.ActionLink(res+ " / " + (string)item.PricingUOM, null, null, new { onclick = "return NoDetailsDialog('" + @item.NoDetailReason + "')" });                                                   }                                                  return null;                                             }                                             ),                        grid.Column(                                             header: "Price Alt UOM",                                             format: @<text>@Html.FormatToDecimals((decimal)item.PriceInOrderUOM, (int)item.Numdecimals) / @item.OrderUOM </text>                                          ),                        grid.Column("Rolling12", "Rolling 12 Sales", format: @<text>@String.Format("{0:c0}", @item.Rolling12) </text>),                       grid.Column("CMPercent", "Net CM ", format: @<text>@String.Format("{0:0.00} %", @item.CMPercent * 100) </text>)       ))   </div>     </div>  <script type="text/javascript">     function NoDetailsDialog(message) {          alert(message);         return false;     } </script> 

2 Answers

Answers 1

You can use datatables. Please let me know how you progress with datatables and I can be available to help you through it, I can even assist with razor syntax:

nuget DataTabes.net jquery plugin

bundles.Add(new StyleBundle("~/Content/CssGrid").Include(                     "~/Content/DataTables/css/jquery.dataTables.min.css"));  bundles.Add(new ScriptBundle("~/bundles/JSGrid").Include(                     "~/Scripts/DataTables/jquery.dataTables.min.js")); 

JavaScript:

//perform tasks like initialize fields, show popup, and post to server function DeleteMe() function EditMe() function Add() 

Page:

$(document).ready(function() {     $('#theDataTable').DataTable();  } );  </script>  //button to show popup for add/edit here <table id="theDataTable" class="display table table-striped table-bordered">                 <thead>                     <tr>                         <th>Field Name A                         </th>                         <th>Field Name B                         </th>                         <th>Field Name C                         </th>                          </th>                         <th>Delete                         </th>                         <th>Edit                         </th>                 </thead>                 <tbody>                     <%  int rowCount = 0;                         foreach (AClass item in Model.AClassList)                         { %>                     <tr id="<%:rowCount%>">                         <td><%:item.FieldA%></td>                         <td><%:item.FieldB%></td>                         <td><%:item.FieldC%></td>                         <td>                             <a href="#" title="Delete" class="btn btn-default btn-xs btn-block"                                 onclick="return DeleteMe('<%:item.Id%>')">Delete</a>                         </td>                         <td>                             <a href="#" title="Edit" class="btn btn-default btn-xs btn-block"                                 onclick="return EditMe('',                                       '<%:item.FieldA %>',                                      '<%: Html.Encode(item.FieldB) %>',                                       '<%: Html.Encode(item.FieldC) %>')">Edit</a>                         </td>                         <% rowCount++;                         } %>                     </tr>                 </tbody>             </table> 

Example that we can make prettier

Answers 2

I have come across the same problem before. In my case it ended up being something to do with the MVC view resolver being scoped to the wrong folder due to calling a different controller than the one that had been used to construct the view which I was making the call in.

I know that's not much help, and it does seem peculiar as you have explicitly stated the controller name in your BeginForm statement. I had my mentor resolve the issue for me in the end, he did so by trial and error just commenting out the various lines until the problem was isolated.

Read More

Tuesday, April 25, 2017

In ASP.Net applications, can I get which security protocol (SSL3/TLS) the request contains?

Leave a Comment

I am trying to check from asp.net perspective what security protocol the client is using when trying to interact with my application. I have different applications hosted asp.net web application, asmx webservices, asp.net mvc, wcf services. Please suggest me how to know if the request is through ssl/ tls protocol.

My intention is to tell my application users to use tls and not to use ssl3 because of poodle vulnerability.

1 Answers

Answers 1

Basically protocal i.e. SSL3 or TSL Doesn't depend on application but mainly depends on the Framework they built on . So in your Question i couldn't find the much information about the Framework on which your applications built on.

My intention is to tell my application users to use tls and not to use ssl3 because of poodle vulnerability.

Your users might be using your Webservices or the asmx service that was written and deployed in your server , so i would say that can't ask the user to change their protocal because its mainly dependent on the framework they are using i.e the framework they built on. so in order to achieve your goal try to by default make your applications run on .Net Framework 4.5 and assign to use the tls as follows

ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 

Please find that Following SecurityProtocalType Refernce

   Ssl3 = 48,    Tls = 192,    Tls11 = 768,    Tls12 = 3072, 

In this way instead of telling your Users , you are actually making to use the tls when they are requesting your services

Read More

Thursday, March 23, 2017

User.Identity.Name returns guid

Leave a Comment

For some (obscure) reason my MVC 4 application returns a guid when I run:

var name = User.Identity.Name; 

I have also tested this in a brand new MVC 4 application and this is new behavior to me. When I look up the documentation on IIdentity it states (as I remembered) that Identity.Name should

Gets the name of the current user.

Why is it returning a guid in my case?

From the web.config

<authentication mode="Forms">     <forms loginUrl="~/Account/Login" timeout="2880" /> </authentication> 

More relevant information: The application is also deployed on another machine, talking to the same database. We use the value of "User.Identity.Name" in that database and when it's not there for a user (new user) we add a new user with that guid.

Now, what is strange: when you switch applications (from localhost to the one deployed on T) you need to log in again. The User.Identity.Name will then be set to a new guid.

(with the starter default application we don't talk to the DB of course, but the same thing happens; User.Identity.Name returns a guid)

1 Answers

Answers 1

You'll need to find the generic principal object which corresponds to the current user.

using System.Security.Principal; ...  GenericPrincipal genericPrincipal = GetGenericPrincipal(); GenericIdentity principalIdentity = (GenericIdentity)genericPrincipal.Identity; string fullName = principalIdentity.Name; 
Read More

Monday, February 6, 2017

Asp.Net MVC Outputcache in base controller not working

Leave a Comment

I am using output cache attribute in asp.net mvc base controller but it is being called every time as it is in OnActionExecuting. Is there any option to call the method only onetime to load all default values?

    protected override void OnActionExecuting(ActionExecutingContext filterContext)     {         GetDefaults();         base.OnActionExecuting(filterContext);     }      [OutputCache(Duration = 60000)]     private ActionResult GetDefaults()     {      //code to load all my default values      // Viewdata values used in all pages in my aplication      // Viewdata values used in all pages in my aplication      // Viewdata values used in all pages in my aplication      return null;     } 

Any other best practice to load all default values for all pages and cache them?

2 Answers

Answers 1

You can use either Application_Start() or Session_Start() in the Global.asax.cs for storing data only once depending on whether you want the data to be refreshed on application start or per session start. That would be up to what your application needs to do.

Having a base controller like you did is good if you need something to be done for every action. The most common being the [Authorize] filter that you need to do for every action to see if the user is authorized for security reasons. Another way to do this is also writing your own ActionFilterAttribute and doing the caching part you need to do. Then all you need to do is to add this new Action filter to any of the actions that you need to do this caching. Pleas see here for how to do this : https://msdn.microsoft.com/en-us/library/dd410056(v=vs.98).aspx But since you want the data to load only once I think an action filter may NOT be the right place to do caching.

Answers 2

There is no way to leverage the the cache engine here. The reason is obvious if you consider why it (caching) actually works, when an action is called it is never the user code that's calling it, it is always the mvc framework that does. That way it has a chance to apply caching. In your example the user code is calling the method directly, there is no indirection here, just a plain old call - no caching is involved.

Read More

Wednesday, January 25, 2017

ASP.NET MVC with Async Action

Leave a Comment

I need to send an asynchronous email from an Async action. I do not understand why the following error is happening, being that I use this same class in other projects and use the same form only without errors, everything quiet.

Error:

The asynchronous action method 'EsqueciMinhaSenhaAsync' returns a Task, which cannot be executed synchronously.

Action:

        [AllowAnonymous]         [HttpPost, ValidateAntiForgeryToken]         public async Task<ActionResult> EsqueciMinhaSenhaAsync(UsuarioEsqueciMinhaSenhaViewModel vModel)         {             if (ModelState.IsValid)             {                 var conteudo = "este é o conteudo do email";                 var nomeRemetente = "esse é o nome do remetente";                  if(await EmailService.SendAsync(Language.PasswordRecovery, conteudo, vModel.EmailOuUsername, nomeRemetente))                 {                     TempData["MensagemRetorno"] = Language.EmailSendedWithSuccess;                     return View("login");                 }             }              TempData["MensagemRetorno"] = Language.ErrorSendingEmail;             return View("EsqueciMinhaSenha");         } 

My Email Service:

    public static async Task<bool> SendAsync(string assunto, string conteudo, string destinatario, string nomeDestinatario)     {         // Habilitar o envio de e-mail         var appSetting = ConfigurationManager.AppSettings;          if (appSetting != null && appSetting.Count >= 7 && !string.IsNullOrEmpty(assunto) && !string.IsNullOrEmpty(conteudo) && !string.IsNullOrEmpty(destinatario) && !string.IsNullOrEmpty(nomeDestinatario))         {             int port = 0;             bool useSSl = false;              using (var msg = new MailMessage             {                 From = new MailAddress(appSetting["EmailFrom"], appSetting["EmailNameFrom"]),                 Body = WebUtility.HtmlEncode(conteudo)             })             {                 int.TryParse(appSetting["EmailPort"], out port);                 bool.TryParse(appSetting["EmailUseSSL"], out useSSl);                   msg.ReplyToList.Add(destinatario);                 msg.To.Add(new MailAddress(destinatario, nomeDestinatario));                 msg.Subject = assunto;                 msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Plain));                 msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Html));                  using (var smtpClient = new SmtpClient(appSetting["EmailServer"], port))                 {                     var credentials = new NetworkCredential(appSetting["EmailUserName"], appSetting["EmailPassword"]);                     smtpClient.Credentials = credentials;                     smtpClient.EnableSsl = useSSl;                     await smtpClient.SendMailAsync(msg);                      return await Task.FromResult(true);                 }             }         }          return await Task.FromResult(false);     } 

2 Answers

Answers 1

I was having same sort of issue, and when I made all possible path awaitable then issue resolved.

Please make changes to your action EsqueciMinhaSenhaAsync

currently your last line is:

return View("EsqueciMinhaSenha");

change it to

//It looks async/wait pattern expects all paths to have awaitable if async is used

return await View("EsqueciMinhaSenha");

Answers 2

1) Why don't you add sync API version based on SmtpClient.SendMail? https://msdn.microsoft.com/en-us/library/swas0fwc(v=vs.110).aspx

public static bool SendSync(string assunto, string conteudo, string destinatario, string nomeDestinatario) {     // Habilitar o envio de e-mail     var appSetting = ConfigurationManager.AppSettings;      if (appSetting != null && appSetting.Count >= 7 && !string.IsNullOrEmpty(assunto) && !string.IsNullOrEmpty(conteudo) && !string.IsNullOrEmpty(destinatario) && !string.IsNullOrEmpty(nomeDestinatario))     {         int port = 0;         bool useSSl = false;          using (var msg = new MailMessage         {             From = new MailAddress(appSetting["EmailFrom"], appSetting["EmailNameFrom"]),             Body = WebUtility.HtmlEncode(conteudo)         })         {             int.TryParse(appSetting["EmailPort"], out port);             bool.TryParse(appSetting["EmailUseSSL"], out useSSl);               msg.ReplyToList.Add(destinatario);             msg.To.Add(new MailAddress(destinatario, nomeDestinatario));             msg.Subject = assunto;             msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Plain));             msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(msg.Body, null, MediaTypeNames.Text.Html));              using (var smtpClient = new SmtpClient(appSetting["EmailServer"], port))             {                 var credentials = new NetworkCredential(appSetting["EmailUserName"], appSetting["EmailPassword"]);                 smtpClient.Credentials = credentials;                 smtpClient.EnableSsl = useSSl;                 smtpClient.SendMail(msg);                  return true;             }         }     }      return false; } 

2) Another option is to use IAsyncOperation as a returning result of your email service. That way, you can use this as both async way (via await) and sync way like that:

var asyncOp = EmailService.SendAsync(some-params-here); var task = asyncOp.AsTask(); task.Wait(); return task.Result; // TODO it's nice to care about exceptions too! 
Read More

Wednesday, August 10, 2016

Passing non-ASCII characters between two asp.net MVC web applications will not be recognized

Leave a Comment

I have two asp.net mvc-4 and mvc-5 web applications,, now inside my first asp.net mvc i have the following WebClient to call an action method (Home/CreateResource) on the second web application :-

using (WebClient wc = new WebClient())           {           var data = JsonConvert.SerializeObject(cr);                        string url = scanningurl + "Home/CreateResource";           Uri uri = new Uri(url);           wc.Headers.Add(HttpRequestHeader.ContentType, "application/json");           wc.Headers.Add("Authorization", token);           output = wc.UploadString(uri, data);          } 

now inside the data object which is being transferred to the second action method, it contain a value for password and this value in my case is ££123 which have 2 non-ASCII characters ..

now on the second action method it will accept the above value as follow:-

enter image description here

so can anyone adivce if there is a way to pass non-ASCII characters between the 2 action methods ? i check that on the first action method the password is being Serialized well , and also the password is being passed correctly from the view to the action method. but the problem is somewhere inside how the data is being transferred inside the network or how the model binder on the second action method is accepting the incoming data object??

4 Answers

Answers 1

To summarize:

You need to specify the WebClient's Encoding property if you want to transmit non-ASCII (or high-ASCII) characters such as £.

The order in which to set the property values is not important in this case, provided it is set before calling UploadString.

using (WebClient wc = new WebClient()) {     var data = JsonConvert.SerializeObject(cr);     string url = scanningurl + "Home/CreateResource";     Uri uri = new Uri(url);     wc.Headers.Add(HttpRequestHeader.ContentType, "application/json");     wc.Headers.Add("Authorization", token);     wc.Encoding = Encoding.UTF8;     output = wc.UploadString(uri, data); } 

In case you wish to download data from a website using WebClient, make sure to set the encoding to the value used by that website.

Answers 2

simply change wc.Headers.Add(HttpRequestHeader.ContentType, "application/json"); to wc.Headers.Add(HttpRequestHeader.ContentType, "application/json;charset=utf-8");

Answers 3

I had a similar problem a while back. The solution was to use UploadDataAsync

using (WebClient client = new WebClient()) {     client.Headers.Add(HttpRequestHeader.ContentType, "application/json");     client.Headers.Add(HttpRequestHeader.Accept, "application/json");     client.UploadDataAsync(new Uri(apiUrl), "POST", Encoding.UTF8.GetBytes(jsonString)); } 

Answers 4

Although UTF-8 theoretically should be capable of encoding any characters you need (as it has a muti-byte encoding scheme to handle extended chracters), an alternative solution:

Base 64 encode your text.

To encode s:

string encoded = Convert.ToBase64String(Encoding.ASCII.GetBytes(s)); 

To decode encoded:

string s = Encoding.ASCII.GetString(Convert.FromBase64String(encoded)); 
Read More

Monday, May 2, 2016

MVC Render Action on Post

Leave a Comment

I have a page that calls another partial view. The page loads fine, but when there is a validation error, it appears to call the post method multiple times.

The code that is causing the issue is here:

<div>     @{Html.RenderAction("ViewUploadedDocs", "TrackingHome", new { number = @Model.Id.ToString() });} </div> 

This should call the following method in the controller.

    public ActionResult ViewUploadedDocs(string number)     {         return PartialView();     } 

It is not decorated with [HttpGet] or [HttpPost]. The method that keeps getting called is below which is the post method of the page.

    [HttpPost]     [MultipleButton(Name = "action", Argument = "Save")]     public ActionResult Edit(EditScreenModelValidation model)     {         if (ModelState.IsValid)         {             return RedirectToAction("UserWorkflows", "Home", new { Area = "Workflow" });         }         return View("Edit", model);     } 

I have read on stackoverflow where people have the page calling the post method that they are trying to get, but mine is calling the post method of my main page and not the page that I am trying to get. If I remove the renderAction line in my main page, then the page works correctly, and the action does not call the Edit page in it.

1 Answers

Answers 1

RenderAction invokes the specified child action method and renders the result inline in the parent view (it calls the action). You should use RenderPartial if you need to pass the current ViewDataDictionary object or Partial if you need the specified view to be rendered as an HTML-encoded string, depending on what you're trying to accomplish.

Read More

Saturday, April 2, 2016

JQuery Datatables makeEditable() issues with large dataset

Leave a Comment

I'm following this tutorial to implement cell editing in JQuery datatables with MVC4.

Links to the plugins used are:

  1. jQuery DataTables plug-in v1.7.5., including the optional DataTables CSS style-sheets used for applying the default styles on the page
  2. jQuery Jeditable plug-in v1.6.2., required for inline cell editing
  3. jQuery validation plug-in v1.7., for implementation of client-side validation
  4. jQuery DataTables Editable plug-in that integrates all these mentioned plug-ins into a fully functional editable datatable.

To achieve the effect of creating the editable datatable you simply have to include the following as part of your script

<script>     $(document).ready(function () {        $('#myDataTable').dataTable().makeEditable();     }); </script> 

The Problem

For each column present in the grid an event is created in the DOM to allow editing.

Where the dataset is very large this has proven to cause significant issues even crashing my browser.


The overall question

Is it possible to only call the edit logic when the user selects the appropriate column rather than trying to build up a large amount of events in the DOM?

5 Answers

Answers 1

I don't use makeEditable() with very large datasets, but you might get a performance benefit from an uplift of some of your versions. I am using:

  • jquery 1.6.4
  • datatables 1.8.2
  • jeditable 1.7.3
  • jQuery Validation Plugin 1.11.1
  • datatables.editable 2.3.1

Answers 2

One alternative is add the event when the user clicking in the td.

$(document).ready(function() {      oTable = $('#example').dataTable();      $("#example td").on("click",function(){         $(this).editable();     })  }); 

Example: https://jsfiddle.net/cmedina/7kfmyw6x/32/

Now, if you do not want to edit all the columns you can assign the event editable only for some columns per class

var oTable = $('#table_id').dataTable(     {          "bSort": false,          "sPaginationType": "full_numbers",     });  $('td.editable_class', oTable.fnGetNodes()).editable('editable.php', { "callback": function( sValue, y ) {     var aPos = oTable.fnGetPosition( this );     oTable.fnUpdate( sValue, aPos[0], aPos[1] ); }, "submitdata": function ( value, settings ) {     return {         "row_id": $(this).data('id'),         "column": $(this).data('column'),     }; }, "height": "17px", "width": "100%", }); 

Answers 3

You can make the td editable on click:

$("#example td").on("click",function(){     $(this).editable(); }) 

Answers 4

In addition to @CMedina 's answer, please read:

.on() - Direct and delegated events

In addition to their ability to handle events on descendant elements not yet created, another advantage of delegated events is their potential for much lower overhead when many elements must be monitored.

On a data table with 1,000 td elements in #example, this example attaches a handler to 1,000 elements:

$("#example td").on("click",function(){     $(this).editable(); }) 

An event-delegation approach attaches an event handler to only one element, the #example, and the event only needs to bubble up one level (from the clicked td to #example):

$("#example").on("click", "td", function(){     $(this).editable(); }) 

Answers 5

I'm not familiar with this library, however i suggest to check if the views and stored procedure is supported, after that you can customize the number of columns required.

Read More