Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API Versioning with ASP.NET Web API and Entity Framework

I am developing a REST API using ASP.NET Web API, Code-First Entity Framework 5 and SQL Server 2012 and I need to be able to version the API. I've read a few blog posts and articles about indicating the API version either in the URI or in a custom HTTP header and using a custom IHttpControllerSelector to select different ApiControllers based on the indicated version. This all makes sense.

What I'm struggling to figure out is how to manage the affects of versioning beyond the Web API layer, specifically in Entity Framework. How do I go about evolving my DbContext without breaking older versions of the API? Can I version the DbContext as well? And if so, how?

like image 506
Raymond Saltrelli Avatar asked Feb 13 '13 16:02

Raymond Saltrelli


People also ask

Is versioning possible in asp net web API?

Summary. As the application grows and business need increase, Versioning of the API is one of the difficult and important part of the API as it makes the API backward compatible. We can do Versioning in ASP.NET Web API with URI, QueryString, Custom Headers and Accept Header parameters, etc.

How do I use versioning in .NET Core API?

Query String Parameter Versioning We've already set a name for the query string parameter (api-version) that we are going to use to send versioning information. We use the [ApiVersion("1.0")] attribute to set the version of the controller. We can see that all the strings starting with “B” are returned as a response.


2 Answers

What I ended up doing was combining the Repository Pattern with Pablo's answer. The gist of it is that my EF models are versioned, I use EF Code-First Migrations to migrate the database to the new versions of the models, my DbContext always works with the latest version of the models, I developed a number of concrete repositories that each implement the IRepository<TItem> interface below.

public interface IRepository<TItem> : IQueryable<TItem>, ICollection<TItem>, IDisposable
    where TItem : class
{
    void Update(TItem item);
    void SaveChanges();
}

One implementation of IRepository<TItem> is DbRepository<TItem> which wraps the entity framework code used to talk to the database.

public class DbRepository<TItem> : IRepository<TItem> 
    where TItem : class
{
    private MyDbContext _db;

    public DbRepository()
    {
        _db = new MyDbContext();
    }

    // Implementation of IRepository<TItem> methods
}

Another implementation of IRepository<TItem> is TypeConversionRepository<TExternal,TInternal> which is an abstract class that facilitates converting from one model type to another.

public abstract class TypeConversionRepository<TExternal, TInternal> : IRepository<TExternal>
    where TExternal : class
    where TInternal : class
{
    protected IRepository<TInternal> InternalRepository { get; set; }

    protected abstract TInternal ConvertInbound(TExternal externalItem);

    protected abstract TExternal ConvertOutbound(TInternal internalItem);

    // Implementation of IRepository<TItem> methods
}

Methods that return models or accept models as parameters use ConvertInbound() and ConvertOutbound() to convert models of type TExternal to TInternal and vice versa. Therefore, given the following 2 versions of MyModel, we can write 2 versions of MyModelRepository; version 2 can talk directly to the database while version 1 will need to convert from version 2 back to version 1.

namespace Models.v1
{
    public class MyModel
    {
        public int Id { get; set; }
        public string MyProperty { get; set; }
    }

    public class MyModelRepository : TypeConversionRepository<Models.v1.MyModel,Models.v2.MyModel>
    {
        MyModelRepository()
        {
            this.InternalRepository = new Models.v2.MyModelRepository();
        }

        protected override TInternal ConvertInbound(TExternal externalItem)
        {
            return new Models.v2.MyModel
            {
                Id = externalItem.Id,
                MyNewProperty = externalItem.MyProperty
            };
        }

        protected override TExternal ConvertOutbound(TInternal internalItem)
        {
            return new Models.v1.MyModel
            {
                Id = internalItem.Id,
                MyProperty = internalItem.MyNewProperty
            };
        }
    }
}

namespace Models.v2
{
    public class MyModel
    {
        public int Id { get; set; }
        public string MyNewProperty { get; set; }
    }

    public class MyModelRepository : DbRepository<MyModel>
    {

    }
}

Now the v1 ApiController can use the v1 MyModelRepository, the v2 ApiController can use the v2 MyModelRepository, but in the end all requests utilize a database that has been migrated to v2.

like image 122
Raymond Saltrelli Avatar answered Sep 28 '22 02:09

Raymond Saltrelli


I think it is a good practice to evolve the Web API and the underline DB model separately (or EF model). That means a DTO model for the Web API, which is mapped to the EF Model in the Web API. That layer of indirection will give you the chance to make changes that perhaps only affects the Web API or the EF model. In addition, a new version in Web API might not impact directly in existing EF model. For example, a new version of the Web API that uses a completely different set of tables.

Regards, Pablo.

like image 27
Pablo Cibraro Avatar answered Sep 28 '22 03:09

Pablo Cibraro