Experiments with Entity Framework Core and ASP.NET Core 1.0 MVC

The article shows some of the ways in which Entity Framework Core can be used together with ASP.NET Core 1.0 MVC. Both packages run on dotnet which makes it possible to run on Linux, Mac or Windows operating systems. The Entity Framework providers are implemented in separate data access projects using onion architecture.

Code: https://github.com/damienbod/AspNet5MultipleProject

2016.12.01: Updated to ASP.NET Core 1.1
2016.06.29: Updated to ASP.NET Core RTM
2016.05.17: Updated to ASP.NET Core RC2 dotnet

Multiple projects using Onion architecture

Onion architecture is used in this example. The MVC 6 project is the outer ring and provides the infrastructure. The MVC 6 depends on the DomainModel project. The MVC 6 project uses the IoC to connect a data access provider implementation to the IDataAccessProvider interface. No direct references to the data access implementations exist, only to the DomainModel. No dependencies exist to the SQLite or MS SQL Server database. This is really useful. I could change the Provider to any other data access layer, for example MongoDB without much effort. The business logic will remain unchanged.

EF7_ASP_NET5_MVC_6_02

Extra layers could also be added to the application as long as each layer only has dependencies to layers towards the center of the onion. For example, I could use view model DTOs instead of using the EF POCO entities directly in the MVC 6 API service, or maybe application services if required. No dependencies exist to the providers, except in the outer layer.

The providers are added in the projects.json file

 "dependencies": {
        "DataAccessSqliteProvider": "1.0.0-*",
        "DataAccessMsSqlServerProvider": "1.0.0-*"
    },

And used in the Startup class.

public void ConfigureServices(IServiceCollection services)
{
	// Use a SQLite database
	var sqlConnectionString = Configuration.GetConnectionString("DataAccessSqliteProvider");

	services.AddDbContext<DomainModelSqliteContext>(options =>
		options.UseSqlite(
			sqlConnectionStringSqLite,
			b => b.MigrationsAssembly("AspNet5MultipleProject")
		)
	);

	services.AddScoped<IDataAccessProvider, DataAccessSqliteProvider.DataAccessSqliteProvider>();

	// Use a MS SQL Server database
	// var sqlConnectionString = Configuration.GetConnectionString("DataAccessMsSqlServerProvider");

	// services.AddDbContext<DomainModelMsSqlServerContext>(options =>
	// 	options.UseSqlServer(
	// 		sqlConnectionStringSqlServer,
	//              b => b.MigrationsAssembly("AspNet5MultipleProject")
	// 	)
	// );

	// services.AddScoped<IDataAccessProvider, DataAccessMsSqlServerProvider.DataAccessMsSqlServerProvider>();

	services.AddMvc().AddJsonOptions(options =>
	{
	  options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
	});
}

EF7 Migrations and configuration

To use Entity Framework 7 migrations, start the command line and use the dotnet ef command from Entity Framework. Usually the connection string is stored in a config file. Only one configuration file is used for all libraries. This file is defined in the parent project, so the MigrationsAssembly needs to be defined in the AddDbContext options.

var sqlConnectionString = Configuration["DataAccessSqliteProvider:ConnectionString"];

services.AddDbContext<DomainModelSqliteContext>(options =>
  options.UseSqlite(
    sqlConnectionString,
    b => b.MigrationsAssembly("AspNet5MultipleProject")
  )
);

Now the project can be used for the migrations. The ef command can be used to create the scripts and run the produced scripts in the database.

>
> dotnet restore
>
> dotnet ef migrations add testMigration
>
> dotnet ef database update
>
>

EF7 Shadow Properties

Shadow Properties is a nice feature from Entity Framework 7 which allows you to add fields in the database table which are not part of your model.

A shadow property can be added to the Entity in the OnModelCreating method of a DBContext inplementation using the Property method.

protected override void OnModelCreating(ModelBuilder builder)
{ 
	builder.Entity<DataEventRecord>().HasKey(m => m.DataEventRecordId);
	builder.Entity<SourceInfo>().HasKey(m => m.SourceInfoId);

	// shadow properties
	builder.Entity<DataEventRecord>().Property<DateTime>("UpdatedTimestamp");
	builder.Entity<SourceInfo>().Property<DateTime>("UpdatedTimestamp");

	base.OnModelCreating(builder); 
}

Here’s a model class

public class SourceInfo
{
	[Key]
	public long SourceInfoId { get; set; }
	
	public string Name { get; set; }

	public string Description { get; set; }

	public DateTime Timestamp { get; set; }

	public List<DataEventRecord> DataEventRecords { get; set; }
}

And the produced table in SQLite. You can see that the UpdatedTimestamp field has been added.

efcoreSQLiteMigratios_01

This can be used and set in the DBContext implementation. Here is an example setting the UpdatedTimestamp in the SaveChanges method.

public override int SaveChanges()
{
	ChangeTracker.DetectChanges();

	updateUpdatedProperty<SourceInfo>();
	updateUpdatedProperty<DataEventRecord>();

	return base.SaveChanges();
}

private void updateUpdatedProperty<T>() where T : class
{
	var modifiedSourceInfo =
		ChangeTracker.Entries<T>()
			.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);

	foreach (var entry in modifiedSourceInfo)
	{
		entry.Property("UpdatedTimestamp").CurrentValue = DateTime.UtcNow;
	}
}

This property can also be used in Linq requests:

_context.DataEventRecords.OrderByDescending(
    dataEventRecord => EF.Property<DateTime>(dataEventRecord, "UpdatedTimestamp")).ToList();

Another example for shadow properties can be found towards the end of this video tutorial: https://channel9.msdn.com/Events/ASPNET-Events/ASPNET-Fall-Sessions/Entity-Framework-7

EF 7 Include and Json Serialization

By using the Include statement, child entities can be returned together with the parent entities in one request. This can be implemented using the Include extension:

return _context.SourceInfos
        .Include(s => s.DataEventRecords)
        .OrderByDescending(srcInfo => EF.Property<DateTime>(srcInfo, "UpdatedTimestamp")).ToList();

If returning child parent entities directly in a Web API controller as JSON, you might possible get a circular dependency JSON serialization exception depending on your model. To prevent this, the ReferenceLoopHandling.Ignore needs to be configured in the default JsonOutputFormatter. This can be set in the Startup class in the ConfigureServices method.

	services.AddMvc().AddJsonOptions(options =>
	{
	  options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
	});
);

Testing with Fiddler

Here are some examples how you can test the application.

DataEventRecord with an existing SourceInfo

POST http://localhost:5000/api/dataeventrecords HTTP/1.1
User-Agent: Fiddler
Host: localhost:5000
Content-Length: 135
Content-Type: application/json;

{
  "DataEventRecordId":3,
  "Name":"Funny data",
  "Description":"yes",
  "Timestamp":"2015-12-27T08:31:35Z",
  "SourceInfo":
  { 
    "SourceInfoId":1,
    "Name":"Beauty",
    "Description":"first Source",
    "Timestamp":"2015-12-23T08:31:35+01:00",
    "DataEventRecords":[]
  }, 
  "SourceInfoId": 1
}

DataEventRecord with a new SourceInfo

{
  "DataEventRecordId":7,
  "Name":"Funny data",
  "Description":"yes",
  "Timestamp":"2015-12-27T08:31:35Z",
  "SourceInfo":
  { 
    "SourceInfoId":0,
    "Name":"Beauty",
    "Description":"second Source",
    "Timestamp":"2015-12-23T08:31:35+01:00",
    "DataEventRecords":[]
  },
 "SourceInfoId":0
}

Get all SourceInfos

http://localhost:5000/api/dataeventrecords/SourceInfos?withChildren=true

[
  { 
    "SourceInfoId":1,
    "Name":"Beauty",
    "Description":"first Source",
    "Timestamp":"2015-12-23T08:31:35+01:00",
    "DataEventRecords":[
       { 
         "DataEventRecordId":1,
         "Name":"Funny data",
         "Description":"yes",
         "Timestamp":"2015-12-27T08:31:35+01:00",
         "SourceInfoId":1
       },
       {
         "DataEventRecordId":2,
         "Name":" second",
         "Description":"22dcee",
         "Timestamp":"2015-12-23T08:31:35+01:00",
         "SourceInfoId":1
       }
    ]
  }
]

Using Entity Framework 7 together with ASP.NET 5 MVC 6, it is really easy to create a API back-end for your application, if it fits your architecture, requirements. The two packages work really good together.

Links:

Announcing Entity Framework Core 1.1

Announcing Entity Framework Core RC2

https://channel9.msdn.com/Events/ASPNET-Events/ASPNET-Fall-Sessions/Entity-Framework-7

http://ef.readthedocs.org/en/latest/modeling/shadow-properties.html

Step by Step implementing Onion architecture in ASP.NET MVC Application

http://jeffreypalermo.com/blog/the-onion-architecture-part-1/

ASP.NET Core 1.0 with SQLite and Entity Framework 7

ASP.NET Core 1.0 MVC File Upload with MS SQL SERVER FileTable

http://www.bricelam.net/

https://github.com/dotnetcurry/building-asp.net-mvc-6-apps

http://weblogs.asp.net/jeff/ef7-rc-navigation-properties-and-lazy-loading

https://msdn.microsoft.com/magazine/mt614250

6 comments

  1. […] Experiments with Entity Framework 7 and ASP.NET 5 MVC 6 – Damien Bod […]

  2. […] Experiments with Entity Framework 7 and ASP.NET 5 MVC 6 […]

  3. […] Experiments with Entity Framework 7 and ASp.NET MVC 6 by Damien Bod. […]

  4. Hey Damien, thanks for the great article.

    Quick question…you mention that the MVC 6 depends on the DomainModel project.
    When looking at the Startup.cs and the Controller.cs I see the following:
    using DomainModel;

    What I’m not understanding or seeing is how/where is that dependency configured?
    When opening the project.json of the AspNet5MultipleProject, I *do not* see any reference to the DomainModel.

    If no reference exists, then why are we capable of using DomainModel;

    I guess I’m not seeing it…

  5. […] Experiments with Entity Framework 7 and ASP.NET MVC 6 : Damien Bod 는 ASP.NET MVC 6와 Entity Framework 7을 함께 사용한 경험을 공유했습니다. […]

  6. […] Experiments with Entity Framework 7 and ASP.NET 5 MVC 6 // weblogs.asp.net/aspnet-team […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: