Sunday, January 20, 2013

Day 2 - Porting nopCommerce to Linux - built in data storage

For some odd reason, I was now able to debug from within MonoDevelop again and that made me happy. Now that the Installation page loaded correctly, I decided to test the SqlServerCe built in data storage for nopCommerce. I provided the email and password, unchecked the "Create sample data" checkbox, selected the "Use built-in data storage (SQL Server Compact)" option and hit Install. Failed.
Setup failed: The requested feature is not implemented.

I set my breakpoint on the InstallController.cs and began walking through the code line by line to see where it had failed. I landed on SqlCeConnectionFactory.cs file line 41.


I tested the value of the providerInvariantName parameter to see if it was indeed null or whitespace, but it wasn't. For now I decided to comment this line and see if I could proceed. Same error. Examining the stack trace I found that Database.cs in the EntityFramework project had another Contract clause.

Contract.Requires(value != null);

And again, value wasn't null. It seemed like Contract clause wasn't behaving as designed. I decided to comment this one out as well. I found another one in EntityFramework, and then another one. After commenting out quiet a few of these over the next few trial runs, I decided to comment them all using Find and Replace. After fixing some compiler errors EntityFramework compiled without errors. Time to commit changes.

In next run, the application complained that it couldn't find SqlServerCe library. I noticed that Nop.Data project had an entry for System.Data.SqlServerCe.Entity.dll which had dependency issues under Linux. It appeared as though I couldn't get too far with SqlServerCe as the backend driver. I decided to give MS SQL Server a try instead.

In first try I got the familiar Feature Not Implemented error.

Setup failed: The requested feature is not implemented.

However, an empty database did get created. I did a search and commented Contract.Requires from the EntityFramework project. I also set a breakpoint on the FailFast  method in the Environment.cs core file which gets invoked when Contract clause failed.

Setup failed: The provider did not return a ProviderManifest instance.

Examining the stack trace hinted to this location: EntityFramework/Core/Common/DbProviderServices.cs:208.

There was also an inner exception being raised.

Invalid URI: The format of the URI could not be determined: System.Data.Resources.SqlClient.SqlProviderServices.ProviderManifest.xml

Further exploring down the stack trace took me to EntityFramework.SqlServer/SqlProviderManifest.cs:78 which passed a baseURI parameter. For some reason Mono didn't like the value that was being passed. I used an overloaded method that did not require baseURI to see if it'd let me proceed.

Setup failed: The requested feature is not implemented.

This was being raised from the EntityFramework.SqlServer project. I did another search and comment on Contract.Requires this time on the EntityFramework.SqlServer project.

System.StackOverflowException: The requested operation caused a stack overflow.

After another round of placing breakpoints and tracing through the code, I found that the stack overflow was being caused by EntityFramework/Core/Common/Utils/Memorizer.cs:71 when attempting to evaluate result.GetValue(). I surrounded it with a try catch and returned the default value of the generic TResult in case of an exception to see if it'd let me proceed.

Setup failed: An unexpected exception was thrown during validation of 'Name' when invoking System.ComponentModel.DataAnnotations.MaxLengthAttribute.IsValid. See the inner exception for details.

This was coming from EntityFramework/Internal/Validation/ValidationAttributeValidator.cs:75. Inner exception hinted of an unimplemented function.

IsValid(object value) has not been implemented by this class. The preferred entry point is GetValidationResult() and classes should override IsValid(object value, ValidationContext context).

In order to proceed, I forced a ValidationResult of Success.

Setup failed: The handle is invalid.

Reviewing the stack trace took me to EntityFramework/Core/Metadata/Edm/LightweightCodeGenerator.cs:193. Examining the code showed that CreatePropertyGetter was creating a dynamic method to map to the getter method of a property on an Entity. After some more trial and error, it became apparent that while the Type handle was mapping correctly, the RuntimeMethodHandle held an invalid value. Fortunately, the getter method on a property has the naming convention of get_PropertyName. This could come in handy to boot strap this code to allow proceeding further. Time to commit changes and review the work.

I reviewed the database that was created by the application, and I see tables being created. This means that EntityFramework is facilitating table creation which is good news. This is a good stopping point for the day.

Conclusion thus far: SqlServerCe driver won't work under Linux, however, after some heavy tweaking and commenting of EntityFramework code, SqlServer driver does work, at least as far as creating tables.


  1. This comment has been removed by the author.

  2. I get the exception:
    Invalid URI: The format of the URI could not be determined: System.Data.Resources.SqlClient.SqlProviderServices.ProviderManifest.xml

    When using Entity Framework with mono on Linux. What is your resolution for this?

    You say that 'I used an overloaded method that did not require baseURI to see if it'd let me proceed.' - Is that in your code or in Entity Framework?

    1. It was part of Entity Framework code under the EntityFramework.SqlServer project in the SqlProviderManifest.cs file.
      The method in question is GetXmlResource which returns the result of XmlReader.Create. One of the overloads for XmlReader.Create takes parameter for baseURI and another one doesn't. I replaced the code there to use the latter.