Archive

Archive for the ‘MVC’ Category

WebAPI2 errors management in Android

In my last project is used an SQL Server 2014 Phones table, with an Android app that register the device itself in this table via an WebAPI2 call.

The table is simple:


We have an primarykey named “PK_Phones” on PhoneId.

The PhoneID is the IMEI code, if the device has phone capabilities and is installed an working SIM, or the Android ID.

There are other common fields for multiuser management as the timestamp which is very useful with the Entity Framework 6 used in this solution.

The Web counterpart (created with Visual Studio 2013) is a common MVC5 project using WebAPI2 with Breeze, Angular , Bootstrap, Modernizr, Moment, Ninject, Toastr…an huge blob installed from NuGet using the HotTowel package (Ninject , Jasmine for testing and other package needs to be installed by hand).

The first move was to create an ADO.NET Entity Data Model (here i don’t speak in detail of the project organization) and using “Code first from Database” i created the class representing my table:

namespace net.studioalessi.walkad.dl
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class Phones
    {
        [Key]
        [StringLength(20)]
        public string PhoneId { get; set; }

        [StringLength(30)]
        public string Manufacturer { get; set; }

        [StringLength(20)]
        public string Model { get; set; }

        [StringLength(150)]
        public string Notes { get; set; }

        [StringLength(20)]
        public string PhoneNumber { get; set; }

        public bool Banned { get; set; }

        [StringLength(50)]
        public string UtenteModifica { get; set; }

        public DateTime? OraModifica { get; set; }

        [StringLength(50)]
        public string UtenteInserimento { get; set; }

        public DateTime? OraInserimento { get; set; }

        [Column(TypeName = "timestamp")]
        [MaxLength(8)]
        [Timestamp]
        public byte[] SSMATimeStamp { get; set; }
    }
}

Then the necessary Controller (right click on Controllers folder->add new scaffolded item->


Web API 2 Controller with actions, using Entity Framework as in figure.

Using the previous Phones class we generate the Controller and the Context, in our case the context is the class PhonesContext:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace net.studioalessi.walkad.dl
{
    public class PhonesContext : DbContext
    {
        public PhonesContext()
            : base("name=WalkadConn")
        {
        }

        // no virtual ? in the previous EF version was generated as virtual...
        public DbSet<Phones> Phones { get; set; }

        // Also the code below was generated automatically in the previous version.
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteModifica)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteInserimento)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.SSMATimeStamp)
                .IsFixedLength();
        }
    }
}

And the Controller is changed from the generated version to an BreezeController

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Walkad.Models;
using net.studioalessi.walkad.dl;
using Breeze.WebApi2;

namespace Walkad.Controllers
{
    [BreezeController]
    public class PhonesController : ApiController
    {
        private IPhoneRepository _repo;

        public PhonesController(IPhoneRepository repo)
        {
            _repo = repo;
        }

        [HttpGet]
        public string Metadata()
        {
            return _repo.MetaData;
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<IHttpActionResult> RegisterImei(PhoneModel model)
        {
            PhonesContext objT = new PhonesContext();
            try
            {
                Phones objTrace = new Phones
                {
                    UtenteInserimento = "RemoteUser",
                    OraInserimento = DateTime.Now,
                    Model = model.Model,
                    Manufacturer = model.Manufacturer,
                    PhoneId = model.Imei
                };
                //
                objT.Phones.Add(objTrace);
                await objT.SaveChangesAsync();
                return Ok();
            }
            catch (Exception ex)
            {
                return BadRequest(ex.ToString());
            }
            finally
            {
                //
            }
        }
    }
}

The PhoneModel was created by hand and is the model representing the JSON string sent from the client (Web site or Android app, as in this case)

public class PhoneModel
{
    [Required]
    [StringLength(15)]
    public string Imei { get; set; }

    [StringLength(30)]
    public string Manufacturer { get; set; }

    [StringLength(20)]
    public string Model { get; set; }
}

At this point we can try the service from Fiddler


By clicking “Execute” we can see that our call was successful


And in SQL Management Studio we can see the new record:


Ok , we can create the Android app with Eclipse Luna (with the Android Developer Tools).

I don’t show the details, i created an simple Android app with an menu item that calls the routine named registerThisDevice:

private void registerThisDevice() {
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    String strBasePath = sharedPrefs.getString((String)getText(R.string.pref_httpbase), "http://192.168.0.77/Walkad");
    new WebApiRegisterImei().execute(strBasePath + "/api/Phones/RegisterImei");
}

Note the i’m using the IP , the tablet used for the test is in the same WiFi network but is not Windows, is not able to resolve the NETBIOS name.

Note also that we web address is registered in a Preference that defaults to my WiFi address.

In order to follow the best practices , the routine WebApiRegisterImei is an AsyncTask so is called with .execute() passing the server address as String argument (in every case , if we don’t use an AsyncTask we get an NetworkOnMainThread exception):

private class WebApiRegisterImei extends AsyncTask<String, Void, JSONObject> {
    ProgressDialog myProgressBar;
    public String ExportResult = "";
    public String LastException = "";
    public Exception LastExceptionObject;       

    protected JSONObject doInBackground(String... params) {
        BufferedInputStream  in = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(params[0]);
            urlConnection = (HttpURLConnection) url.openConnection();
            // the Android docs says that HttpURLConnection uses the GET method by default. It will use
            // POST if setDoOutput(true) has been called. Other HTTP methods
            // (OPTIONS, HEAD, PUT, DELETE and TRACE) can be used with
            // setRequestMethod(String). Anyway forcing POST as request method is useful.
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            //
            JSONStringer vm;
            vm = new JSONStringer()
                    .object().key("Imei").value(MainActivity.ImeiId)
                             .key("Manufacturer").value(android.os.Build.MANUFACTURER)
                             .key("Model").value(android.os.Build.MODEL)
                    .endObject();

            urlConnection.setFixedLengthStreamingMode(vm.toString().getBytes().length);
            urlConnection.setRequestProperty("Content-Type", "application/json");
            urlConnection.setRequestProperty("charset", "utf-8");
            DataOutputStream wr = new DataOutputStream (
                    urlConnection.getOutputStream ());
                    wr.writeBytes (vm.toString());
                    wr.flush ();
                    wr.close ();                           
            //
            int statusCode = urlConnection.getResponseCode();
            if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                // handle unauthorized (if service requires user login)
            } else if (statusCode != HttpURLConnection.HTTP_OK) {
                InputStream errorstream = urlConnection.getErrorStream();
                BufferedReader br = null;
                if (errorstream == null){
                    InputStream inputstream = urlConnection.getInputStream();
                    br = new BufferedReader(new InputStreamReader(inputstream));
                }else{
                    br = new BufferedReader(new InputStreamReader(errorstream));
                }
                String strResponse = "";
                String strTemp;
                while ((strTemp = br.readLine()) != null){
                    strResponse += strTemp;
                }   
                strResponse = strResponse.toLowerCase();
                if(strResponse.contains("pk_phones"))
                {
                    ExportResult = getErrorDuplicated();
                    return null;
                }
            }
            //
            ExportResult = getExportCompleted();
        } catch (Exception e) {
            Mint.logException(e);
            LastException = e.toString();
            LastExceptionObject = e;                
        } finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    Mint.logException(e);
                }
            }
            if(urlConnection != null){
                urlConnection.disconnect();  
            }
        }
        return null;
    }

    private String getErrorDuplicated() {
        return (String)getText(R.string.imeiduplicatederr);
    }

    private String getExportCompleted(){
        return (String)getText(R.string.registerimeidone);
    }           

    @Override
    protected void onPreExecute() {
        try {
            myProgressBar = new ProgressDialog(MainActivity.thisMainActivity, ProgressDialog.STYLE_SPINNER);    
            myProgressBar.setMessage((String)getText(R.string.registerimeiwait));
            myProgressBar.show();
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }

    @Override
    protected void onPostExecute(JSONObject result) {
        try {
            myProgressBar.dismiss();
            if(LastException.length() > 0){
                Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();   
                Mint.logException(LastExceptionObject);
            }else{
                Toast.makeText(MainActivity.thisMainActivity, ExportResult, Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }
}

Examining the code , we can note that onPreExecute and onPostExecute are the only places where we can interact with the main thread and relative controls (the progress bar) so in the background part we register the exceptions details in one string and one object, examining them on the onPostExecute.

Note the use of the JSONStringer: debugging the code you can see that is created the JSON string as used in Fiddler.

If all is ok, we can see that in SQL Server is written a new row in the Phones table.

The test for statusCode != HttpURLConnection.HTTP_OK was the difficult part to discover how it works, the subject of this post.

If you reissue the command from the Android app on the same device you violate the primary key, so the WebAPI code goes in the Exception and is sent to the client “BadRequest” (HTTP error 400) instead of “Ok”(200), sending as other info ex.ToString().

The management of the returned error is done as you can read in the code: we need to examine the returned stream , by reading this stream we can create a string that contains the original ex.ToString, for example

{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"System.Data.Entity.Infrastructure.DbUpdateException: 
An error occurred while updating the entries. See the inner exception for details. ---> 
System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. 
See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_Phones'. 
Cannot insert duplicate key in object 'dbo.Phones'. The duplicate key value is…..

So in the Android code we search if the string contains the name of the primary key , giving to the user an scoped message, or we give to the user an generic message and the code uses Splunk Mint Express for registering all infos about the unmanaged exception (that could be an network failure, and so on).

Advertisements

Breeze SaveChanges issue with Ie10

Every new technology is an hard path , an high mountain to climb.

I’m working to an MVC project with ASP.NET 4.5 , Breeze and Angular, authentication done with the new ASP.NET Identity, Entity Framework 6.

The big problem , for the average developer (me) , is:

  • For Microsoft, on MSDN you can find a lot of verbose pages about the syntax: an simple CRUD example using these technologies , add update delete of records, is hard to find.
  • For thirdy parties technologies you can find abstruse , overcomplexed samples ; but still there is a lack of a simple table management with CRUD operations.

In every case the samples simply ignore (at the least the ones that i discover with Google) the everyday questions with concurrency : another user could have deleted the record that you are trying to save, or you are trying to save a record of a table where there is an unique index for a field.

Ok , this was the rant, now the problem.

The code is about the update of a record in a Vehicle Classes table (ClassiMezzi).

Using Breeze, in a BreezeController (a class decorated with “[BreezeController]”) the typical sample for an update is

[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
    return _repo.SaveChanges(saveBundle);
}

This works with Ie11, the latest Firefox, Chrome, Safari; _repo is an repository object that at the end calls the Entity Framework context provider (in our sample EFContextProvider<ClassiMezziContext>)

But if you try to use this method for adding a new record on a table where there is an index UNIQUE, there are troubles with the error messages using Internet Explorer 10.

For these new apps i simply don’t consider Ie < 10; if you are still using Ie6 , Ie7.. time to upgrade …but to ignore the previous version is a bit excessive, for me: i test everything with the latest Firefox, Chrome also under Linux Android and Mac, for Ie to test the latest version and the previous seems reasonable.

And so, testing the code with multiuser issues (record deleted from another user, check constraints, UNIQUE indexes) i discovered the problem.

From the javascript side , the controller is called with an Ajax call with callbacks for success or failure:

function saveFailed(error) {
    try {
        manager.detachEntity(service.ClasseMezzoEntity);
    }
    catch (ex) { }
    service.ClasseMezzoEntity = null;
    if (error.httpResponse.status === 401 || error.httpResponse.status === 403) {
        logError(localize.localizeText("UnauthorizedOperation"), null, true);
        return;
    }
    //var strMsg = breeze.saveErrorMessageService.getErrorMessage(error).toLowerCase(); old version ok with Ie11 , not Ie10
    var strMsg = error.httpResponse.data.Errors[0].toLowerCase();
    if (strMsg.indexOf("entities may have been modified or deleted since entities were loaded") > 0) {
        logWarning(localize.localizeText("AnotherUsrHasChangeData"), null, true);
        return;
    }
    if (strMsg.indexOf("ix_classimezzi") > 0) {
        logWarning(localize.localizeText("ClassNameDuplicated"), null, true);
        return;
    }
    if (strMsg.indexOf("errdelete:anotheruser") > 0) {
        logWarning(localize.localizeText("AnotherUsrHasDeleteData"), null, true);
        return;
    }
    //  other code for unknown error management
    var msg = breeze.saveErrorMessageService.getErrorMessage(error);
    // etc.
};
 
function saveClasseMezzo() {
    service.isSaving = true;// see at the end of this module
    service.lastSaveSuccess = true;
    return manager.saveChanges()
        .catch(saveFailed)
        .then(saveClasseMezzoAttemptDone);
};

For example we try to add another ClasseMezzo (Vehicle class) with the same name of an existing item on the the db table.

With Ie11 and other latest browsers the first controller code is ok , and if our ajax call fails because the UNIQUE index the first

var strMsg = breeze…. commented code works perfectly.

But while with Ie11 etc. the error.message in javascript contains the string “Cannot insert duplicate key row …” with the index name that helps to manage the error (in this sample the index name is “ix_classimezzi”) in Ie10 we get


That is an anonymous “An error has occurred” that can’t help to manage the error, and no trace of the original SQL error in httpResponse: a big trouble if i would to say to the customer “you are trying to insert an duplicate item” because could be another thing , for example an SQL check violation.

The problem is the simple approach in the WebApi2 controller.

In order to manage correctly the errors coming from multiuser issues the WebAPI2 controller code must be:

[HttpPost]
[Authorize(Roles = "PowerUser")]
public SaveResult SaveChanges(JObject saveBundle)
{
    SaveResult objRes = null;
    List<object> objErr = null;
    var classimezziList = JsonConvert.DeserializeObject<List<ClassiMezzi>>(saveBundle.SelectToken("entities").ToString());
    try
    {
        objRes = _repo.SaveChanges(saveBundle);
        return objRes;
    }
    catch (Exception ex)
    {
        var keyMappings = new List<KeyMapping>();
        objErr = new List<object> { JsonConvert.SerializeObject(ex.Message) };
        return new SaveResult()
        {
            Entities = classimezziList.Cast<object>().ToList(),
            Errors = objErr,
            KeyMappings = keyMappings
        };
    }
}

Let’s examine the code.

We pass to the controller an JSON object , that we want deserialized in order to correctly manage the error; note in the catch management code: is explicitly created an SaveResult object , instead of the automatic creation.

Setting the Errors field to something != null , in our case the error message serialization, we cause the call of the javascript failure callback , and in the javascript failure code the message must be read as

var strMsg = error.httpResponse.data.Errors[0].toLowerCase();

because when the code is falling back we have this , now:


So we can manage the things as in the next rows.

This is valid also for Ie11 , and Firefox, Chrome under Windows, Linux, Mac, Android.

At least, this is the situation with the current latest Breeze (1.3.8)

The requested resource does not support http method ‘POST’

After a life of web sites developed with Web Forms, now i’m working with MVC.

Not a basic MVC, but SPA apps using Angular, Breeze, Twitter Bootstrap.. an huge jump.

In the Controller folder of my solution i was thinking to create an utilities controller, in this case for the logging of javascript errors, paging utilities, and so on.

So i initially created a class as:

public class ServicesController : ApiController
{
	[HttpPost]
	public void SubmitError(string jsRespoText, string jsstatus, string jssource)
	{
		ExceptionUtility.LogJsExceptionFile(jsRespoText, jsstatus, jssource);
	}

	[HttpGet]
	public JsonGenResponse CreatePagerStdTable(int startRowIndex, int totalCount, int pageSize, string jsLoadDataFunName, string Filter, string QueryType)
	{
		// this will be another post…
	}
}

For the HttpGet , no problem.

When i tried from Fiddler the HttpPost method i got the error written in the subject of this post.

A Controller decorated with [BreezeController] works as POST, and it was tested.. but it uses a JObject parameter, i noticed.

The POST problem pops out when you try to use primitive (string, int..) parameters in a Web API call instead of an object.

A solution could be to use [FromBody], with this attribute WebAPI search the attribute:value in the request body.

But there is a problem : is not possible to specify more than one “[FromBody]” , so is not possible to write:

public void SubmitError([FromBody]string jsRespoText, [FromBody]string jsstatus, [FromBody]string jssource)

The solution is to create a class, in my case

public class JsErrorData
{
    public string jsRespoText { get; set; }
    public string jsstatus { get; set; }
    public string jssource { get; set; }

}

And then write the method as

[HttpPost]
public void SubmitError(JsErrorData jsObjErr)
{
    ExceptionUtility.LogJsExceptionFile(jsObjErr.jsRespoText, jsObjErr.jsstatus, jsObjErr.jssource);
}

The javascript caller:

function logJsError(responseText, status, source, callback) {
    var ajaxImpl = breeze.config.getAdapterInstance("ajax");
    ajaxImpl.ajax({
        type: "POST",
        contentType: "application/json;charset=UTF-8",
        url: strBaseHttp + "api/Services/SubmitError",
        data: "{'jsRespoText': '" + responseText.replace(/'/g, '') + "','jsstatus':'" + status + "','jssource':'" + source + "'}",
        success: function (html) {
            callback(html);
        },
        async: true
    });
}

Now HttpPost is working.

Categories: .NET, JQuery, MVC, SPA

OData services

My previous post is half the story.

The original intent was to use Entity Framework and build an WCF data service, but with the latest EF 6.1.0 it seems that is no more possible.

Microsoft is now strongly oriented to the new OData religion: i discovered that there is a NUGet project for a bridge between WCF and EF6 in a alpha stage, and it seems abandoned from months.

I tried, but it installs an EF6 prerelease, and in every case i was not able to obtain something of working.

In my previous post i was able to create an EF model, and then i added an WCF Data Service 5.6.

But it was not working , trying to launch the .svc file i got

“The server encountered an error processing the request. See server logs for more details.”

I ended up with this code:

namespace NorthwindWeb
{
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class NorthwindCustomers : DataService<NorthwindEntities1>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.UseVerboseErrors = true;
            config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
        }
    }
}

The ServiceBehavior decoration gives detailed error infos , which was:

“The server encountered an error processing the request. The exception message is ‘Expression of type ‘System.Data.Entity.Core.Objects.ObjectContext’ cannot be used for return type ‘System.Data.Objects.ObjectContext”

It seems that i should use EntityFrameworkDataService<NorthwindEntities1> instead of DataService, but this object is apparently not available in EF6 object model or somewhere else, and i’m not the best WCF/EF expert.

I’m thinking to the ones (also me) that had written a lot of WCF code using EF…they should rewrite all? or the projects needs to be “closed”, no more NuGet updates ?

This is another case where “googling” i find that i’m not alone.

Ok , waiting for the next Microsoft change of mind (“throw away OData: is old, unsecure, blah blah.. now there is <new buzzword>”) i wrote the OData web service with Web API…sometimes i envy the COBOL programmers that are maintaining ancient software where the screens are painted with | and -.

So from Visual Studio 2013 new project ->ASP.NET Web Application


Then in the next wizard step choose the Empty template but add Web API:


This is the moment of adding via NuGet (TOOLS->NuGet Package Manager->Manage NuGet packages for Solution.. the Entity Framework and OData.


 
 


Now is recommended to clic on Updates and update all of the NuGet components:


Now if we want to add an Data Model there is still the trouble described in the previous post; so copy the sample connection string in web.config (also from the mentioned previous post) changing the name , data source, initial catalog and so on.

We still use Northwind as sample, so we add to the solution an ADO .NET Entity Data Model that in our sample we call NorthwindModel.edmx

In order to avoid mistakes , is better to leave checked the creation of a new connection string, that will be the one used:


In this sample we still use only the Customers table


With a Model Namespace = NorthwindModel

Do a Build->Rebuild all, otherwise in the next step Visual Studio complains about an error.

In the automatically generated Controllers folder (in the Visual Studio solution) right clic and add a controller:


We add “Web API2 OData with actions, using Entity Framework”.

In the next step by simply clicking on the drop downs and writing “CustomersEntity” in Controller name we can set the controller as in figure:


In the Controllers folder now we have a CustomersEntityController.cs class: Visual Studio and his template engine is so gently that is generated also an sample of code:


The commented code must be copied in WebApiConfig.cs class under the App_Start folder, so at the end we have

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http.OData.Builder;
using NorthwindSvc;
 
namespace NorthwindSvc
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
 
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Customers>("CustomersEntity");
            config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
        }
    }
}

Ok, now we can launch the site.

Tipically this does nothing, complaining that a default document is not configured for the requested URL, and directory browsing is not enabled on the server.

But in CustomersEntityController.cs we can see examples of calling the web service, so if we add /odata/CustomersEntity to the browser address (for example http://localhost:11775/odata/CustomersEntity)

we get an JSON file with the records of the Customers table:


This with Google Chrome, Internet Explorer requires to download the JSON file.

Observing the generated code we can read

// GET odata/CustomersEntity(5)
[Queryable]
public SingleResult<Customers> GetCustomers([FromODataUri] string key)
{
    return SingleResult.Create(db.Customers.Where(customers => customers.CustomerID == key));
}

This means that we can write /odata/CustomersEntity(‘ALFKI’) and we get only the corresponding record.

With OData are coming for free many interesting features regarding the querystring

For example we can write


That is we filter our data for Country = Mexico and we need only the company name and the fax; the result:


The condition could be complex, for example we can specify &filter=Country eq ‘Mexico’ and CustomerID eq ‘ANTON’

A reference of the OData query options at these addresses:

http://msdn.microsoft.com/en-us/library/gg312156.aspx

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options

Categories: .NET, MVC, OData, Vs2013

Upgrade of Durandal projects

I’m working with Durandal , and i need to upgrade some old (old..2013…) code.
But this code was written when there was Durandal 1.x , now with NuGet in a Visual Studio 2013 project the version is 2.01 , and are changed a lot of things.

Fortunately in this Durandal site page and in this Papa’s page there are useful guidelines.

Categories: .NET, Durandal, JQuery, MVC, SPA

RequireJS with Visual Studio 2013 & WebAPI

Now that the SPA is the mainstream technology of the moment, i was wondering how really are useful Knockout, Durandal, RequireJS and so on.

These new buzzwords are javascript frameworks, founded (RequireIS no) on the well knowed jQuery.

I have done many web applications with jQuery and SPA concepts , using web forms (.aspx) as html/json generators and then decoding/evaluating the resulted html or JSON by hand.

So i started a lot of tests, trying to convert some old web applications in “à la page” SPA apps.

This is not a simple task , because the technologies are rapidly changing and evolving (as the jQuery versions): so the sample probably working at the time of the publications three months after is not working because jQuery changes, new library versions and so on.

I tried to evaluate RequireJs, the first step was to create with Visual Studio 2013 a new ASP.NET Web Application, using the WebAPI template:


Note that i explicity chosen Web API.

Then the first thing that seems obvious is to add a WebAPI controller:


naming it “TestController”

The generated TestController.cs contains

namespace RequireJsTest.Controllers
{
    public class TestController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<controller>/5
        public string Get(int id)
        {
            return "hi this is the test " + id.ToString();
        }
........


In this case i changed the default “value” string with “hi this is the test ” + id.ToString().

By adding the WebAPI there is a new class WebApiConfig in App_Start, called in the global.asax.

Note the comment above // GET api/<controller>/5 : this will be be the path for the call, with the template defined in the WebApiConfig class.

If you try http://localhost:<yourport>/api/test/5 you get a file named 5.json , that contains “hi this is the test 5” (using the default Visual Studio web server).

Now, in the past in a complex web application with many .js sources it was requested a manual and complex management in order to provide the requested js modules in a certain situation, for example we are deleting a db record and it is necessary to provide the infrastructure for the ajax call vs the db and the messaging framework to the user.

With RequireJS the dependencies management in javascript is simplified, the pages http://requirejs.org/docs/start.html and http://requirejs.org/docs/api.html#data-main explains very well the concepts.

So we can install RequireJS from TOOLS->Library Package Manager->Manage NuGet Packages for Solution…

and search online:


Done this , to the HomeController.cs we can add a new method

public ActionResult Test()
{
    ViewBag.Title = "Test Page";
    return View();
}

Right clicking this method we can Add View for this Controller method,


That creates an Test.cshtml file under Views/Home :


By default there is only an <h2> , we change to

@{
    ViewBag.Title = "Test";
}

<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<div id="msgplaceholder"></div>
<script data-main="/app/main" src="@Url.Content("~/Scripts/require.js")"></script>

Require.js is the script added from RequireJS, and now we see what is the “data-main” attribute.

A SPA app typically uses a folder named “app” , immediately under the root, as the main placeholder of the SPA structure.

And another commonly used standard is to use a main.js file as the SPA core.

As the requireJS site write, “The data-main attribute is a special attribute that require.js will check to start script loading”.

So the last <script> loads a file named main.js using the require.js library.

At this point we need to create the main.js , and a structure for the separation of the concepts.

In this case:


In main.js we define some aliases for other js modules, specifying the path:

(function () {
    requirejs.config(
        {
            paths: {
                'testview': '/app/viewmodels/test',
                'datasvc': '/app/services/testsvc',
                'config': '/app/configs/config'
            }
        }
    );

    require(['testview'],
        function (testvw) {
            //debugger;
            var id = 10;
            testvw.showMessage(id);
        }
    );
})();

Here the function decorated with require([‘testview’] is automatically executed, if you place another function for example

require(
    ['testview'],
    function (testvw) {
        //debugger;
        var id = 10;
        testvw.showMessage(id);
    }
);

require(
    ['testview'],
    function (testvw) {
        var id = 20;
        testvw.showMessage(id);
    }
);

is automatically executed after the first has finished (the result depend from the callbacks timings).

Note that the function define as required the module that we aliased with ‘testview’, that points to /app/viewmodels/test.js

Here in test.is we can write

define('testview',
    ['jquery', 'datasvc'],
    function ($, datasvc) {
        var showMessage = function (id) {
            datasvc.getTest(id, function (message) {
                $("#msgplaceholder").html(message);
            });
        };

        return {
            showMessage: showMessage
        };
    }
);

That is we define that the current module will be publicy knowed as “testview” with “define”, and so in main.js we can write “require([testview]”

In test.js we note that this module is defined with the public name “testview”, and internally requires jQuery , which is already publicy knowed with the $ symbol, and our other module “datasvc”,

Here we can define some functions, the ones that we need public are defined public with the “return” keyword (a sample of Module Pattern).

We know now that datasvc is the alias defined in /app/services/testsvc.js where we can write something as

define('datasvc',
    ['jquery', 'config'],
    function ($, config) {
           var callWebApi = function (url, type, callbackfunc) {
                //debugger;
                $.ajax({
                    url: url,
                    type: type,
                    dataType: 'json',
                    success: function (data) {
                        callbackfunc(data);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        callbackfunc(jqXHR.responseText);
                    }
                });
            },

            getTest = function (id, callback) {
                url = config.baseUrl + id;
                callWebApi(url, 'GET', callback);
            };

        return {
            getTest: getTest
        };
    }
);

That requires “config” defined in /app/configs/config.js

define('config',
    [],
    function () {
        var baseUrl = '/api/test/';  //important ! Verify that the name must be the same of the controller

        return {
            baseUrl: baseUrl
        };
    }
);

Note that config.js is not requiring other libraries, so we can write define(‘config’, []

That is nothing in the square brackets.

if we try to run our app and point the browser to /home/test ( for example http://localhost:29404/home/test)


Conclusion: effectively some of these new frameworks are really useful and RequireIS leads to a less “spaghetti” javascript code.

Categories: .NET, JQuery, MVC, SPA, Vs2013

Errors updating an MVC 4 project

An MVC project by default uses NuGet as package updater, a concept similar to the java (and Android SDK) packages.

This is good, but sometimes leads to problems.

If you start an MVC 4 project from scratch with Visual Studio 2012, it uses an old version of Entity Framework (by default for MVC 4 is EF5).

Using the NuGet package updater there are a lot of updates , from jQuery to EF which is migrated to EF6.

This morning i updated an MVC4 project , and nothing was working…

Launching the project from Visual Studio i got

[A]System.Web.WebPages.Razor.Configuration.HostSection cannot be cast to [B]System.Web.WebPages.Razor.Configuration.HostSection. Type A originates from ‘System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ in the context ‘Default’ at location ‘C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll’. Type B originates from ‘System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ in the context ‘Default’ at location ‘C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\4cf795a8\95e0656c\assembly\dl3\84c052c7\541b674e_ffdece01\System.Web.WebPages.Razor.dll’.

I tried to delete the Windows.Microsoft.NET (2 and 4, either 32 and 64 bit) Temporary ASP.NET Files , clean the project, delete all possible temp files…nothing.

The solution was to insert in the main web.config these lines:

<runtime>
	<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
		<dependentAssembly>
			<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" />
			<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
		</dependentAssembly>
	</assemblyBinding>
</runtime>

Well, at this point the site was complaining something about Microsoft.Data.Edm ..deleted from bin directory dll and xml file, it will be recreated.

Another launch , this time the error is

Inheritance security rules violated by type: ‘System.Web.Mvc.MvcWebRazorHostFactory’. Derived types must either match the security accessibility of the base type or be less accessible

This is corrected changing in the Views folder web.config the version in the below line from 4 to 5 , so we have

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

Finally the web site is working.

Categories: .NET, MVC, Vs2012

MVC Ajax with IIS 7.5 and with VS Server

I’m not an Visual Studio 2012 Development Server (also of IIS Express…) fan, for a lot of motivations.

An example is a difference in the default paths for an MVC jQuery Ajax call , if is done from the VS dev server or from Windows 7 IIS 7.5 (i will investigate on IIS under Windows 8…) , which is near identical to the IIS on the average, real production server.

In order to see this difference create an simple MVC4 project (as Internet Application) named HelloWorld ; in the Models folder create a class UserData , as

namespace HelloWorld.Models
{
    public class UserData
    {
        public string UserName { get; set; }
    }
}

in the HomeController.cs created by default (using the Internet application template) place a method GetMessage

[HttpGet]
public JsonResult GetMessage(UserData myData)
{
    myData.UserName = "Hello " + myData.UserName ;
    return Json(myData, JsonRequestBehavior.AllowGet);
}

In the Scripts folder place an HelloWorld.js script:

var myData = {
    UserName: "Bill Gates"
};
$(function () {
    $.ajax({
        type: "GET",
        url: "/Home/GetMessage",
        data: myData,
        contentType: "application/json",
        success: processResult
    });
});
 
function processResult(returnedData) {
    window.alert("Returned value : " + returnedData.UserName);
}

We are using the HomeController so we use “Home” (without “Controller”) as address.

Our javascript can be referenced in Index.cshtml adding after the last line

@section scripts{
   <script src="~/Scripts/HelloWorld.js">
   </script> 
}

Launching the site we receive


At this point in the solution properties the web server we change from the Visual Studio Development Server to IIS (not Express) , creating in IIS a new Web Application that uses the ASP.NET v4.0 Application Pool


This time launching the application is no more used the address localhost:4560 but the IIS application, and our script is no more working : using Fiddler we can see that there is an 404 error.

If in the javascript we change the URL from

url: “/Home/GetMessage”,

To

url: “/HelloWorld/Home/GetMessage”,

(that is we add the application name) our app turns back to working, but does not work if we revert the solution to use the Visual Studio Development Server.

There is a trick, changing the VirtualPath to from the single “/” to “/HelloWorld” (the name given to the IIS 7.5 web application and used in the Ajax url)


The Visual Studio web application is still working as the true IIS version.

Categories: .NET, JQuery, MVC, Vs2012

jQuery not working in MVC4 (JSON undefined)

After a long time i’m coming back to work in MVC, obviously MVC4 with Entity Framework 5.

I updated an old project started from the classic Internet application template, the first thing was to update the packages via NuGet so the current jQuery version in my project is 2.03

First launch, and surprise: apparently jQuery is not working; this on my HP laptop with Windows 7 64 bit, Internet Explorer 10.

From the other hand also working in SharePoint i encountered problems with the jQuery upgrading

I tried to create a completely new MVC4 project, launched the project activating the debug in Ie and immediately popped out an error in jQuery:


JSON is undefined ??

With this error, JQuery naturally is not loaded:


I noticed that was active the icon for the compatibility view


By activating the compatibility voilà jQuery working..ok but i can’t’ tell to the customers to activate the compatibility…

The solution is to add in the MVC _Layout.cshtml file this line in the <head> section:

<meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1" />

With this correction jQuery is working and the compatibility icon is no longer present.

Categories: Javascript, JQuery, MVC, Vs2012