Free .NET development software alternatives

The standard software stack for a .NET developer is

  • OS – desktop Windows
  • IDE – Visual Studio
  • Database – SQL Server

All these components are quite pricey. But there are free alternatives. And with my recent project I decided to use alternative software for development and production in .NET, all totally free.

  • OS – Linux Mint 17 (based on Debian/Ubuntu)
  • IDE – MonoDevelop
  • Database – MySQL Community Edition

monodevelop
Sample solution can be downloaded from here https://github.com/mchudinov/EF6MySQL

MonoDevelop Installation

The last version of Ubuntu (at the time of writing is 14) comes with MonoDevelop IDE version 4. To get the last features it should be upgraded to version 5.x. MonoDevelop 5.x solution and project files are compatible with Visual Studio 2012. Version 5.x is available from so called ppa – Personal Package Archive. Here is the ppa with stabile MonoDevelop https://launchpad.net/~ermshiperete/+archive/ubuntu/monodevelop

To add ppa with the last MonoDevelop stabile version on Linux Mint we can use a program called “Software Sources”.

Mint_software_sources

Or do the following steps in command line and  this should work on any Debian flavour.

1. Add ppa:ermshiperete/monodevelop to your system

sudo add-apt-repository ppa:ermshiperete/monodevelop

$ sudo add-apt-repository ppa:ermshiperete/monodevelop
You are about to add the following PPA to your system:
 A custom build of the latest stable release of MonoDevelop (built from the published source zip file) that can be installed parallel to the version included in the 

MonoDevelop as well as new version of Mono framework will be installed in /opt/monodevelop.

There is a monodevelop-launcher.sh script in /opt/monodevelop/bin that can be used to start MonoDevelop.

Install package monodevelop-current to always get the latest release.
 More info: https://launchpad.net/~ermshiperete/+archive/ubuntu/monodevelop
Press [ENTER] to continue or ctrl-c to cancel adding it

Executing: gpg --ignore-time-conflict --no-options --no-default-keyring --homedir /tmp/tmp.GAz3lWsYZx --no-auto-check-trustdb --trust-model always --keyring /etc/apt/trusted.gpg 
gpg: requesting key 839ECBBE from hkp server keyserver.ubuntu.com
gpg: key 839ECBBE: "Launchpad PPA for Eberhard Beilharz" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1

2. Pull down the latest list of software from all package archives, including the PPA you just added:

$ sudo apt-get update

3. Install Mono and MonoDevelop

$ sudo apt-get install mono-devel
$ sudo apt-get install monodevelop-5

4. Install certificates
If you get error when installing or updating NuGet packages in MonoDevelop then ssl certificates must be installed. Run the following commands in command line, answer yes when you will be asked to install certificates:

$ sudo mozroots --import --sync
$ sudo certmgr -ssl -m https://go.microsoft.com
$ sudo certmgr -ssl -m https://nugetgallery.blob.core.windows.net
$ sudo certmgr -ssl -m https://nuget.org
$ sudo certmgr -ssl https://nugetgallery.blob.core.windows.net
Mono Certificate Manager - version 3.2.8.0
Manage X.509 certificates and CRL from stores.
Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed.

X.509 Certificate v3
   Issued from: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
   Issued to:   CN=Microsoft Internet Authority
   Valid from:  4/25/2012 5:41:36 PM
   Valid until: 4/25/2020 5:40:55 PM
   *** WARNING: Certificate signature is INVALID ***
This certificate is already in the CA store.

X.509 Certificate v3
   Issued from: CN=Microsoft Internet Authority
   Issued to:   DC=com, DC=microsoft, DC=corp, DC=redmond, CN=MSIT Machine Auth CA 2
   Valid from:  5/15/2012 8:40:55 PM
   Valid until: 5/15/2016 8:50:55 PM
   *** WARNING: Certificate signature is INVALID ***
Import this certificate into the CA store ?yes

X.509 Certificate v3
   Issued from: DC=com, DC=microsoft, DC=corp, DC=redmond, CN=MSIT Machine Auth CA 2
   Issued to:   CN=*.blob.core.windows.net
   Valid from:  7/17/2014 11:43:37 PM
   Valid until: 5/15/2016 8:50:55 PM
Import this certificate into the AddressBook store ?yes

2 certificates added to the stores.

To start MonDevelop run command skript /opt/monodevelop/bin/monodevelop-launcher.sh

Example project with Entity Framework 6 and MySQL

To make EF and MySQL work together we need 3 packages: Entity Framework, MySql.ConnectorNET.Data and MySql.ConnectorNET.Entity. Here is packages.config example

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="EntityFramework" version="6.1.1" targetFramework="net45" />
  <package id="MySql.ConnectorNET.Data" version="6.8.3.2" targetFramework="net45" />
  <package id="MySql.ConnectorNET.Entity" version="6.8.3.2" targetFramework="net45" />
</packages>

App.config should look like this

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework" requirePermission="false" />
  </configSections>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" />
    </providers>
  </entityFramework>
  <system.data>
    <DbProviderFactories>
      <remove invariant="MySql.Data.MySqlClient" />
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" />
    </DbProviderFactories>
  </system.data>
  <connectionStrings>
    <add name="MyModel" connectionString="Data Source=localhost;Database=Mydatabase;Uid=root;Pwd=root;" providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
</configuration>

One important thing to remember in App.config is that connectionString name attribute must have the same name as Context class, but without “Context”. For example MyDatabaseContext is our context class, the name of the connection string will be MyDatabase. Otherwise EF is looking for a standard SQLEXPRESS database on localhost.

Another important thing is that Context class must have a constructor that calls the parent constructor with connection string name as a parameter.

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace EF6MySQL
{
    public class MyModelContext : DbContext
    {
        public MyModelContext() : base("name=MyModel") { }

        public DbSet<MyModel> MyModel { get; set; }
    }
}

Enable and start migrations commands in Package Manager Console.
MonoDevelop does not have Package Manager console that can run migrations commands. This can be done in Visual Studio only.

Enable-Migrations
Add-Migration Init
Update-Database

1. Enable-Migrations

PM> Enable-Migrations
Checking if the context targets an existing database...
Code First Migrations enabled for project EF6MySQL.

2. Add-Migration Init

PM> Add-Migration Init

No MigrationSqlGenerator found for provider 'MySql.Data.MySqlClient.EF6'. Use the SetSqlGenerator method in the target migrations configuration class to register additional SQL generators.

PM> Add-Migration Init
Scaffolding migration 'Init'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration Init' again.
PM> Update-Database

First time you run Add-Migration Init you get an error message

No MigrationSqlGenerator found for provider ‘MySql.Data.MySqlClient.EF6’. Use the SetSqlGenerator method in the target migrations configuration class to register additional SQL generators.

To solve this the following line must be added to the Configuration.cs in Migrations folder.
SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());

namespace EF6MySQL.Migrations
{
    using System.Data.Entity.Migrations;

    internal sealed class Configuration : DbMigrationsConfiguration<EF6MySQL.MyModelContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
        }

        protected override void Seed(EF6MySQL.MyModelContext context){ }
    }
}

3. Update-Database

Update-Database command can give us another error:

Specified key was too long; max key length is 767 bytes

767 bytes is the stated prefix limititation for InnoDB tables and if we do not have so long index then error comes from __MigrationHistory tabel, not from data model tables. To check it use -Verbose option with Update-Database command

PM> Update-Database -Verbose
Using StartUp project 'EF6MySQL'.
Using NuGet project 'EF6MySQL'.
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Target database is: 'Mydatabase' (DataSource: localhost, Provider: MySql.Data.MySqlClient.EF6, Origin: Configuration).
Applying explicit migrations: [201410062356508_Init].
Applying explicit migration: 201410062356508_Init.
create table `MyModel` (`Id` int not null  auto_increment ,primary key ( `Id`) ) engine=InnoDb auto_increment=0
create table `__MigrationHistory` (`MigrationId` varchar(150)  not null ,`ContextKey` varchar(300)  not null ,`Model` longblob not null ,`ProductVersion` varchar(32)  not null ,primary key ( `MigrationId`,`ContextKey`) ) engine=InnoDb auto_increment=0
MySql.Data.MySqlClient.MySqlException (0x80004005): Specified key was too long; max key length is 767 bytes

EF creates a key on (MigrationId,ContextKey) which exceeds the hardcoded maximum key length constraint in MySQL. MigrationId is 255 characters long, and ContextKey is 512 characters long, but each char can occupy more than 1 byte, so mysql is complaining.

The problem is described here http://stackoverflow.com/questions/20832546/entity-framework-with-mysql-and-migrations-failing-because-max-key-length-is-76  The solution is described here http://msdn.microsoft.com/en-us/data/dn456841.aspx. We basically have to tell EF it has to change the way it generates the __MigrationHistory table.

The following file must be added to the solution under Migrations folder to change the way EF generates MigrationHistory table:

using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Migrations.History;

namespace EF6MySQL.Migrations
{
    class MySqlHistoryContext : HistoryContext
    {
        public MySqlHistoryContext(DbConnection connection, string defaultSchema)
            : base(connection, defaultSchema)
        {}

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
            modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();
        }
    }
}

And Configuration.cs class must have a new call in constructor
SetHistoryContextFactory("MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));

namespace EF6MySQL.Migrations
{
    using System.Data.Entity.Migrations;

    internal sealed class Configuration : DbMigrationsConfiguration<EF6MySQL.MyModelContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
            SetHistoryContextFactory("MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));
        }

        protected override void Seed(EF6MySQL.MyModelContext context){}
    }
}

Template solution can be downloaded from here https://github.com/mchudinov/EF6MySQL