Archive

Archive for the ‘Vs2013’ Category

Windows 10 Mobile update crash

On my Lumia 925, originally sold with Windows 8.1, i installed the official Windows 10: at a certain point i made an hard reset and the phone automatically downloaded Windows 10.
The hard reset was done when i was not able to install updates on the Windows 10 preview (from Windows Insider); now there were an update for the official Windows 10 ending for 107, if i remember well, but the phone was not able to complete the install.
I noticed that continuously tapping on the screen, in order to avoid the phone standby, the procedure was able to end, but after the reboot the screen showed 2 gears and then a big sad smile , as this ->     : – (
No way to reset hard or soft, at every reboot the gears and then after a few seconds the dreaded smile: i was already thinking to buy a new phone.
But i discovered the Windows Device Recovery Tool, that after the downloading of 1,65 gb has installed a working Windows 8.1 on my phone; and then using the Windows Phone Developer Registration 8.1 (installed with Windows Phone SDK 8.1) i re-registered the device in order to publish on it my apps developed with Visual Studio 2013.
Thanks to Google sync i lost nothing of interest, but anyway i will be suspicious about updating the 925 to Windows 10..

Advertisements
Categories: Vs2013, WinPhone

Firefox needs UTF8 for GeoXml3


I’m developing an WebAPI2 solution with Angular that receives, from Android devices, GPS points.
So Latitude , Longitude , Speed etc. are saved in a SQL Server 2014 table; from the web site the headquarter would follow the real time navigation.
The chosen approach was to write an KML file from the db data, and show this KML in a GoogleMaps window.
Having local files (not on another domain) the best solution is to use the GeoXml3 library, which is relatively easy to use (examples here and here).
For the javascript part, no problem: there is an simple setTimeout that calls via jQuery an WebAPI2 method that re-write the kml (the name is the record ID), and then adding an random number to the querystring the boss can follow the persons wandering somewhere, seeing the kml line that slowly progress on the map.
But there was an problem: On IE10 , 11, Chrome, Edge all ok; instead on Firefox (Windows or Ubuntu or Android tablet) no kml shown.
Sometimes the things are discovered in a very casual manner…i tried to save the KML with another name using Notepad++ , and change the code in order to not use the API call and instead try to show immediately this second file where i wanted to test some change, in order to understand what was wrong : bingo, KML immediately visible!
This was a real head scratching…the files (the generated from the system, and the one saved from the previous) in Notepad++ was absolutely the same, no visible difference.
So i tried to compare them with WinMerge and surprise, the generated file was full of strange characters before every normal character, immediately i realized “oops, the encoding?” and only after i noticed in Notepad++ that in the Encoding menu the file saved from Notepad++ itself was UTF8 encoded, and the one produced from the C# code was without encoding.
In the WebAPI2 method effectively i used Unicode: so now, all the browsers shows the KML writing the file as:

private async Task WriteTextAsync(string filePath, string text)
{
    //byte[] encodedText = Encoding.Unicode.GetBytes(text);
    byte[] encodedText = Encoding.UTF8.GetBytes(text);
    //
    using (FileStream sourceStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 1048576, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}

Error creating an Azure Cloud Service with 2.7 SDK

From some days is available the Azure 2.7 SDK, and i’m trying it with Visual Studio 2013: when Visual Studio was alerting that were available this update i installed it from inside Visual Studio.
Creating an Azure Cloud Service i got the error “Microsoft Azure Tools: Error: The installed Microsoft Azure Compute Emulator does not support the role binaries. Please install the latest Microsoft Azure Compute Emulator and try again.”.
I don’t know if is the real solution valid for all the cases, but anyway re-downloading the Microsoft Azure SDK for .NET – 2.7  from http://www.microsoft.com/en-us/download/details.aspx?id=48178 but installing only the MicrosoftAzureComputeEmulator-x64.exe (x86 if you are using an 32 bit environment, but who has today an 32 bit pc?) finally the project has started.

Categories: .NET, Azure, Vs2013

Angular modal problem after update

I was already prepared to the head banging.
I had a .NET 4.5 project with Angular 1.3.15, reopening for some update i tried an NuGet update and voilà, Angular 1.4.1 available: but no updates for Bootstrap, still 3.3.4 … troubles to the horizon…
The site is essentially an employees management, with add update delete done using modal windows.
After the update apparently all still working until i tried an update (of a record).
The original code was

modalInstance = $modal.open({
    templateUrl: 'app/partials/formemployees.html',
    controller: 'modalController',
    backdrop: "static",
    keyboard: true,
    modalFade: true,
    scope: $scope
})

With this code the modal window was appearing, inside the window no problem for fields editing, but after closing the window by the ESC on the keyboard or the Save button, on the browser canvas a grey background was covering everything: only after an F5 the site was working.
The problem seems related to the BootStrap version not aligned to Angular for the “modal” part; after some struggle i found that the problem is relative to the animation feature.
By adding the clause “animation: false, ” :

modalInstance = $modal.open({
    animation: false,
    templateUrl: 'app/partials/formemployees.html',
    controller: 'modalController',
    backdrop: "static",
    keyboard: true,
    modalFade: true,
    scope: $scope
})

Now the modal is working correctly.

Categories: .NET, Angular, SPA, Vs2013

Oracle development notes

After many years (Oracle 8..) i’m back to develop on Oracle (using Microsoft Windows). So, after some problem, here a little quick guide for starting with the current versions (April 2015). Before of all, must be installed the java JDK, the 64 bit package jdk-8u40-windows-x64.exe or 32 (jdk-8u40-windows-i586.exe) depending from your system. Then must be installed , as already written in this post, the ODAC component: in this page for the 64 bit version (is recommended the version with installer, not the Xcopy version) ; the 32 bit version here. The main product for the developer is Oracle SQL Developer from here. The Oracle SQL Developer is installed by default in c:\app, the folder c:\app\client\<your_windows_user_name>\product\12.1.0\client_1\Network\Admin is the default place where to place the tnsnames.ora file, needed for the Oracle connections. We have 2 servers, one for development and the other is the official database:

VJSVIL =
  (DESCRIPTION =
        (ADDRESS =
          (COMMUNITY = tcp.world)
          (PROTOCOL = TCP)
          (Host = 10.86.13.xxx )
          (Port = 1521)
        )
    (CONNECT_DATA = (SID = VJSVIL)
    )
  )

VJ4SAP =
  (DESCRIPTION =
     (ADDRESS = (PROTOCOL = TCP)(HOST = srv-vrj)(PORT = 1521))
     (CONNECT_DATA =
       (SERVER = DEDICATED)
       (SERVICE_NAME = XE)
     )
   )

Note that the development entry is using the IP , instead for the official database is used the hostname. If must be used the hostname for some reason (as in this case) could be useful to map the hostname to the ip in C:\Windows\System32\drivers\etc\hosts, in our case:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#        127.0.0.1       localhost
#        ::1             localhost
10.86.13.xxx        srv-vrj

In order to work, it must be verified that the 1521 port is opened in the firewalls. Launching SQLDeveloper there are 2 problems that you can encounter. The first is the language , for example the software is working in French or Italian and in the preferences menu there is nothing for the language. The language setting in English is done by adding the line AddVMOption -Duser.language=en In the file <path where you unzipped SQL developer>\sqldeveloper\bin\sqldeveloper.conf; another strange problem could be the error ORA-01882: timezone region not found.
This is resolved by adding in sqldeveloper.conf AddVMOption -Duser.timezone=CET.
In SQL Developer we can add a connection selecting the TNS item: 02-04-2015 15-51-19 And we can expand and see the list of tables, views, etc. below the connection name node. 02-04-2015 16-15-14

Categories: .NET, Oracle, Vs2013

maxJsonLength property

I was working on a asp:DataGrid table , where in every cell there is an asp:Textbox for immediate editing (think to a sort of Excel).
Then with an html button is called the javascript method save:

function save() {
    $get('ContentPlaceHolder1_UpdateProgress1').style.display = 'block';
    var strSql = "";
    var strPartSql = "";
    $("input.editqt").each(function () {
        var strHtmlId = this.id;
        var arrValues = strHtmlId.split("_");
        var strId = arrValues[1];
        var strValue = $("#" + strHtmlId).val();
        if (strValue.trim() === "")
            strValue = "0";
        strPartSql = "UPDATE TSG123_VoyageRes SET Qt = " + strValue + " WHERE ID_Voyage_Res = " + strId + "; ";
        strSql += strPartSql;
    });
    PageMethods.SaveMatrix(strSql, saveCallback, saveErrorCallback, "save");
}

function saveCallback(result, response, context) {
    $get('ContentPlaceHolder1_UpdateProgress1').style.display = 'none';
    window.alert("Save success.");
}

function saveErrorCallback(result, response, context) {
    $get('ContentPlaceHolder1_UpdateProgress1').style.display = 'none';
    if (result._timedOut)
        window.alert("Timeout saving");
    else {
        if (result !== null)
            window.alert(result.get_message());
        else
            window.alert("Error saving.");
    }
}

The javascript code calls an PageMethod (the web site uses ASP.NET 4, VB.NET , Microsoft Ajax Toolkit), defined as:

<System.Web.Script.Services.ScriptMethod(), System.Web.Services.WebMethod()> _
Public Shared Sub SaveMatrix(ByVal sql As String)
    UserFrameworkClass.AccessDB.Database.SetData(sql, Data.CommandType.Text)
End Sub

In practice from the javascript side is generated (not the best method..) a long SQL string, in the Page method is launched “as is” into SQL Server.
At a certain point the datagrid became bigger, and trying to save , crash: error from javascript:
“Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.”
The solution was to add these lines in web.config:

<system.web.extensions>
  <scripting>
    <webServices>
      <jsonSerialization maxJsonLength="2147483647"/>
    </webServices>
  </scripting>
</system.web.extensions>

No more errors, until these pages will be rewritten in a more solid manner.

Categories: .NET, Ajax, VB.NET, Vs2013

Arbitrary values in asp:Datagrid

Working at a ships loading planner, i received this request: we can have more work turns, for every turn one or more squads (from 1 to 5).

But there should be an optional special squad , the “0” squad, that must be represented with the “SA” string; note also that only on the first row for every turn must be visible an Word export icon.

The work turn on the db is an Int.. how to write “SA”?

We are speaking of an .NET 4 solution, using VB.NET, the DataGrid is defined as

<asp:DataGrid ID="DG" runat="server" AutoGenerateColumns="False" ShowHeader="true"
    EnableViewState="true" CssClass="DGBorder" Width="99%" Height="1%" HorizontalAlign="left">
    <AlternatingItemStyle CssClass="DGRow2" />
    <SelectedItemStyle CssClass="DGSelect" />
    <ItemStyle CssClass="DGRow1" />
    <HeaderStyle />
    <Columns>
        <asp:BoundColumn DataField="Day" HeaderText="Date" DataFormatString="{0:dd/MM/yyyy}">
            <ItemStyle CssClass="DGBorder" Width="200px" HorizontalAlign="Left" />
            <HeaderStyle HorizontalAlign="Center" Width="200px" />
        </asp:BoundColumn>
        <asp:BoundColumn DataField="Turn" HeaderText="Turn">
            <ItemStyle CssClass="DGBorder" Width="100px" HorizontalAlign="Left" />
            <HeaderStyle HorizontalAlign="Center" Width="100px" />
        </asp:BoundColumn>
        <asp:BoundColumn DataField="Hours" HeaderText="Hours">
            <ItemStyle CssClass="DGBorder" Width="3em" HorizontalAlign="Center" />
            <HeaderStyle HorizontalAlign="Center" Width="3em" />
        </asp:BoundColumn>
        <asp:BoundColumn DataField="Squad" HeaderText="Squad">
            <ItemStyle CssClass="DGBorder" Width="100px" HorizontalAlign="Center" />
            <HeaderStyle HorizontalAlign="Center" Width="100px" />
        </asp:BoundColumn>
        <asp:ButtonColumn DataTextField="Squad" HeaderText="Fold" DataTextFormatString="<img src=Imgs\word.png border=0>" CommandName="PrintFold">
            <ItemStyle CssClass="DGBorder" Width="100px" HorizontalAlign="Center" />
            <HeaderStyle HorizontalAlign="Center" Width="100px" />
        </asp:ButtonColumn>
    </Columns>
</asp:DataGrid>

The solution is the SetRenderMethodDelegate method:

Protected Sub DG_ItemCreated(sender As Object, e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles DG.ItemCreated
    If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Or e.Item.ItemType = ListItemType.SelectedItem Then
        Dim drv As DataRowView = TryCast(e.Item.DataItem, DataRowView)
        If drv Is Nothing Then
            Exit Sub
        End If
        If strDataRif = String.Empty Then
            strDataRif = drv.Row("Day").ToString()
            strTurnoRif = drv.Row("Turn").ToString()
        End If
        strTempData = drv.Row("Day").ToString()
        strTempTurno = drv.Row("Turn").ToString()
        If strTempData <> strDataRif Or strTempTurno <> strTurnoRif Or boolForceFirst Then
            strDataRif = strTempData
            strTurnoRif = strTempTurno
            boolForceFirst = False
            ' 3 = position of column, starting from 0 , in DataGrid
            e.Item.Controls(3).SetRenderMethodDelegate(New RenderMethod(AddressOf RenderCustom))
        Else
            e.Item.Controls(4).Controls(0).Visible = False
        End If
    End If
End Sub

Protected Sub RenderCustom(ByVal writer As System.Web.UI.HtmlTextWriter, ByVal Container As System.Web.UI.Control)
    If DirectCast(Container, System.Web.UI.WebControls.TableCell).Text = "1" Then
        writer.Write("1")
    Else
        writer.Write("SA")
    End If
End Sub

If we asked to create the SA squad, we have 0 as number and then must be changed with “SA” , otherwise the squad numbering begins with 1 and must not be changed.
The result:

grid

Categories: .NET, VB.NET, Vs2013

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).

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)