Category Archives: Uncategorized

Another Use Case for DbContext.Add in EFCore (and a DDD win)

If you are like me and design your classes following Domain-Driven Design principals , you may find yourself with code like this for controlling how objects get added to collections in the root entity.

public class Samurai {

  public Samurai (string name) : this()
  {
     Name = name;
  }

  private Samurai ()
  {
    _quotes=new List<Quote>();
  }

  public int Id { get; private set; }
  public string Name { get; private set; }
  private readonly List _quotes = new List ()
  private IEnumerable Quotes => _quotes.ToList ();
  public void AddQuote (string quoteText) {
      var newQuote=new Quote(quoteText,Id);
      _quotes.Add (newQuote);
  }

I have a fully encapsulated collection of Quotes. The only way to add a new quote is through the AddQuote method. You can’t just call Samura.Quotes.Add(myquote).

Additionally, because I want to control how developers interact with my API, there is no DbSet for Quotes. You have to do all of your queries and updates via context.Samurais.

A big downside to this is that if I have a new quote and I know the ID of the samurai, I have to first query for the samurai and then use the AddQuote. That really bugs me. I just want to create a new quote, push in the Samurai’s ID value and save it. And that requires either raw SQL or a DbSet<Quote>. I don’t like either option. Raw SQL is a hack in this case and DbSet<Quote> will open my API up to potential misuse.

I was thinking about this problem while laying in bed this morning (admit it, that’s the first thing you do when you wake up, too, right?) and had an idea.

In EF Core, we can now add objects directly to the context without going through the DbSet. The context can figure out what DbSet the entity belongs to and apply the right info to the change tracker. I thought this was handy for being able to call

myContext.AddRange(personobjectA, accountobjectB, productObjectC);

Although I haven’t run into a good use case for leveraging that yet.

What occurred to me is that if DbContext.Add is  using  reflection, maybe EF Core can find a private DbSet.

So I added a private DbSet to my DbContext class:

private DbSet<Quote> Quotes { get; set; }
 And tried out this code (notice I’m using context.Add, not context.Quotes.Add):
static void AddQuoteToSamurai () 
{
  using (var context =newSamuraiContext ()) 
  {
    var quote=newQuote("Voila",1);
    context.Add(quote);
    context.SaveChanges();
  }
}
And it worked! But this isn’t complete yet. I’m breaking my rule of ensuring that only my aggregate root can manage quotes. So this is “dangerous” code from my DDD perspective. However, I was happy to know that EF Core would support this capability.
Currently, Samurai.AddQuote does not have any additional logic to be performed on the quote. What if I were to add in a “RemoveBadWords” rule before a quote can get added?
public void AddQuote (string quoteText) 
{
 Utilities.RemoveBadWords(quoteText);
 var newQuote=new Quote(quoteText,Id);
  _quotes.Add (newQuote);
}
 Now I have an important reason to use Samurai to do the deed. I can add a second, static AddQuote method that also takes an int. Because it’s static, it’s a pass through method.
public static Quote AddQuote(string quoteText,int samuraiId)
{
  Utilities.RemoveBadWords(quoteText); 
  var newQuote=newQuote(quoteText,samuraiId);
  return newQuote;
}

This works and now I don’t have to have an instance of Samurai to use it:

staticvoid AddQuoteToSamurai () 
{
  using (var context =newSamuraiContext ()) {
    context.Add(Samurai.AddQuote("static voila",1));
    context.SaveChanges();
}

One thing I was worried about was if I had an instance of Samurai and tried to use this to add a quote to a different samurai. That would break the aggregate root…it’s job is to manage its own quotes only. It shouldn’t know about other Samurais.

But .NET protects me from that. I can’t call the static method from an instance of Samurai.

I still think that there’s a little bit of code smell from a DDD perspective about having this static, pass-through method in an aggregate root so will have to investigate that (or wait for any unhappy DDDers in my comments). But for now I am happy that I can avoid having to query for an instance of Samurai just to do this one task.

Next Up: Devintersection, Las Vegas Oct 30-Nov 2

I have been away from home more than at home this fall! I have two trips behind me:

Trip 1: London for ProgNet, Salt Lake City for Pluralsight Live and Denver for Explore DDD.

Trip 2: Orlando for AngularMix then a side trip to Miami to visit friends and relatives

I’m home again for a bit then off again to Las Vegas for Devintersection. If you are still thinking about going (you should, really) you can still get a small discount using the code “LERMAN” when you register.

I will be giving 3 talks, participating in a panel and of course attending talks.

One for SURE that I’ll attend is on EF Core by two members of the team (and my friends!) Diego Vega and Andrew Peters on Tuesday.

I’m also doing an EF Core 2 talk which will be complementary to their session (not redundant) . That talk is on Wednesday morning.

 

Later on Wednesday I’m doing a session (should be FUN) for developers to take advantage of SQL Server in containers for quick dev environments. There I will show setting up and using a docker container with SQL Server for Linux (on my mac) and then a windows container for SQL Server Developer. For a dev or testing environment these are such fast and easy ways to spin up a SQL Server.

In my last session, which is on Thursday, I’m going to mostly code (yay! What is more fun that that?) to build up a data api and provide some design guidance at the same time as letting you get MORE eyeballs full of EF Core 2.0. And for a bonus, I finally got my hands on Azure Functions so I get to show off what I built on there as well.

After that, I’ll be on the closing panel. One NEVER knows what to expect there. Should be fun.

And also how cool is this graphic that the conference created, just for me to share just with you! 

 

 

 

 

Updated My EFCore / WebAPI / PostreSQL / XUnit Repo to 1.1

Today was dedicated to updating my long running repository sample that I started when EF Core was EF 7  to the newest version of EF Core: 1.1. Here is the updated repo: https://github.com/julielerman/EFCore-ASPNetCore-WebAPI-RTM.

Phase one of this update continues to use project.json.

In addition to updating the version #s of  the Nuget package references, I also made some changes to the code to reflect a few new features.

Pay attention to the tooling packages. In the tools section, the package name has changed – note DotNet at the end –  and the version is currently 1.0.0-preview3 even though IIS version is preview2.

 "tools": {
   "Microsoft.AspNetCore.Server.IISIntegration.Tools": 
       "1.0.0-preview2-final",
   "Microsoft.EntityFrameworkCore.Tools.DotNet":
       "1.0.0-preview3-final"
 },

Also in the dependencies, the EFCore Design package is 1.1.0 like the rest of EFCore. That’s part of the EF APIs, not tooling.

Code changes ….

You’ll discover the DbSet.Find method and change tracker Load method in use in the repository class. These were both added in to EF Core 1.1.

I modified the WeatherEvent class to fully encapsulate its Reactions collection using the support for mapping to IEnumerable. That resulted in some changes to constructors and the addition of an AddReaction method and a local variable.

Unrelated to EF Core, I also modified the SeedData.cs class. It reads a hard coded seeddata.json file to read in seed data. That data used old dates. I wanted the data to show current dates to help me tell that I really and truly pushed new data into the database. Since the Date property of WeatherEvent is private, they way I went about this was to read the raw JSON and update the date value that way then save the raw JSON back to the original file. Then I deserialize the JSON with a current range of dates into a set of WeatherEvents. This also means that I added Delete/Create database back in so the database gets thrown away and recreated/reseeded every time you start up the application.

The tests are also update to use the latest packages. In addition to changing the versions, I had to add a reference to an older package (InternalServices) as its dependency has not yet been updated in xunit.

Here’s the full project.json for the test project since I had to do a bunch of googling to figure it out.

{
 "version": "3.0.0-*",
 "description": "Tests for simple app using aspnetcore, efcore and   
                  postgresql. developed and run on OSX.",
 "authors": [ "Julie Lerman" ],
 "testRunner": "xunit",
 "dependencies": {
   "Microsoft.EntityFrameworkCore.InMemory": "1.1.0",
   "src": "3.0.0",
   "xunit": "2.2.0-beta4-build3444",
   "dotnet-test-xunit": "2.2.0-preview2-build1029",
   "Microsoft.DotNet.InternalAbstractions":"1.0.0"},
 "frameworks": {
 "netcoreapp1.0": {
   "dependencies": {
     "Microsoft.NETCore.App": {
     "type": "platform",
     "version": "1.1.0"
     }
   },
   "imports": [
     "dnxcore50",
     "portable-net45+win8"
     ]
   }
  }
}

I hope you find this repository useful to see EF Core 1.1 in action.

Oh and as per a tweet by Brad Wilson, I added SDK to my global.json file!

Now I have to go learn about why this is important. Clearly it is!

 

Using JSON Data and EF to Seed a Database

I’m so used to use standard code (C# and EF APIs) to let EF help me seed a database. You know, instantiate an object, populate its fields, maybe add objects to a related list. Then add the whole kit n’ kaboodle to the DbContext and call SaveChanges.

I was showing some EF Core code to Geoffrey Grossenbach when we were talking about doing a Play by Play for Pluralsight on EF Core in advance of my buckling down to do a more serious course. Geoffrey looked at all the code for building up my objects to seed the database and said “wow that’s a lot of code. Can’t you use JSON or something?” (Note: I tired of trying to get this code formatted prettily in wordpress, but you get the point…right?)

private static List BuildWeatherEvents()
{
  var events = new List
  {
   WeatherEvent.Create(DateTime.Now,WeatherType.Sun,
    new List<string[]>{new []{"Julie","Oh so sunny!"}}),
   WeatherEvent.Create(DateTime.Now.AddDays(-2),WeatherType.Rain),
   WeatherEvent.Create(DateTime.Now.AddDays(-3),WeatherType.Sun,
    new List<string[]>{
      new []{"Julie","Oh lovely summer sun!
                      Too bad I'm on my computer"},
      new []{"Everyone in vermont", "Hooray let's go play!"},
      new []{"Sampson","I'd like to go for a swim, please!"},
    }),
   WeatherEvent.Create(DateTime.Now.AddDays(-4),WeatherType.Cloudy),
   WeatherEvent.Create(DateTime.Now.AddDays(-5),WeatherType.Rain),
   WeatherEvent.Create(DateTime.Now.AddDays(-6),WeatherType.Sun)
  };
 var lastEvent = 
   WeatherEvent.Create(DateTime.Now.AddDays(-1),  WeatherType.Snow,
          new List<string[]> {
             new[] { "Julie", "Snow? In July? 
                      Okay this is ridiculous even for VT!" } });
 lastEvent.Reactions.FirstOrDefault().Comments.Add
     (new Comment { Text = "Get over it, Julie!" });
 events.Add(lastEvent);
 return events;
}

Oh how much prettier it would be. Here’s how I’ve done it with EF Core but you can certainly use the same concept for doing the same with EF6.

My domain is WeatherEvent which is the domain in my EFCore demo at https://github.com/julielerman/EFCore-ASPNetCore-WebAPI-RTM, (which I have not yet updated to demonstrate using the JSON data).

Here’s the json which I store in a file called weatherdataseed.json.

[
 {
  "date": "2016-07-27T00:00:00",
  "time": "22:09:13.8216230",
  "type": 5,
  "reactions": [
   {
    "name": "Julie",
    "quote": "Oh so sunny!",
    "comments": []
   }
  ],
 "mostCommonWord": null
 },
 {
 "date": "2016-07-25T00:00:00",
 "time": "22:09:13.8237230",
 "type": 1,
 "reactions": [],
 "mostCommonWord": null
 },
 {
  "date": "2016-07-24T00:00:00",
  "time": "22:09:13.8238740",
  "type": 5,
  "reactions": [
   {
    "name": "Julie",
    "quote": "Oh lovely summer sun! Too bad I'm on my computer",
    "comments": []
   },
   {
    "name": "Everyone in vermont",
    "quote": "Hooray let's go play!",
    "comments": []
   },
   {
    "name": "Sampson",
    "quote": "I'd like to go for a swim, please!",
    "comments": []
   }
  ],
  "mostCommonWord": null
 },
 {
  "date": "2016-07-23T00:00:00",
  "time": "22:09:13.8239130",
  "type": 6,
  "reactions": [],
  "mostCommonWord": null
 },
 {
  "date": "2016-07-22T00:00:00",
  "time": "22:09:13.8239210",
  "type": 1,
  "reactions": [],
  "mostCommonWord": null
 },
 {
  "date": "2016-07-21T00:00:00",
  "time": "22:09:13.8239290",
  "type": 5,
  "reactions": [],
  "mostCommonWord": null
 },
 {
  "date": "2016-07-26T00:00:00",
  "time": "22:09:13.8239360",
  "type": 2,
  "reactions": [
   {
    "name": "Julie",
    "quote": "Snow? In July? Okay this is ridiculous even for VT!",
    "comments": [
     {
     "text": "Get over it, Julie!"
     }
    ]
   }
 ],
 "mostCommonWord": null
 }
]

So that’s not just data, but hierarchical data with 3 levels of relationship. Expressing it in json is a lot easier and prettier and readable than building all of that up in C#, creating the objects, etc.

Now of course it’s time for the magical JSON.NET which makes it possible to pull this data in to EF short and sweet.

This is the full code that I’m using to seed the database from the JSON using EF.

I’m calling it from startup.cs in an ASP.NET Core Web API inside the Configure method. Here I’m just reading the file with System.IO.File.ReadAllText and then passing that text into my Seedit method. Also note the ConfigureServices where I’m setting up the DbContext along with the connection string it requires.

Note: There’s been some churn in the code in this post as  Shawn Wildermuth and I went through some learning on twitter with Dave Fowler, one of the core leads on the aspnet team. I  changed my original code to streamline it as per a great suggestion from Shawn, but Dave pointed out some scoping issues with that. So now the sample is back to my original version, where the seedit method is responsible for creating a new ServiceScope and instantiating the context. 

 public void ConfigureServices(IServiceCollection services)
 { 
   services.AddDbContext<WeatherContext>(
     options=> options.UseNpgsql(
       Configuration["Data:PostgreConnection:ConnectionString"]));
   services.AddMvc();
 }

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  loggerFactory.AddDebug();

  app.UseMvc();
  var dataText=System.IO.File.ReadAllText(@"weatherdataseed.json");
  Seeder.Seedit(dataText,app.ApplicationServices);
}

Here’s the Seedit method, which uses JSON.NET to deserialize the json into a list of  WeatherEvent objects.  The contract serializer is to overcome private setters in my WeatherEvent class. I got this from Daniel Wertheim’s github repo. Then I use the ASP.NET Core ServiceProvider to get service I set up in startup which will instantiate a WeatherContext along with the connection string specified in startup.

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using EFCoreWebAPI;
using Newtonsoft.Json;
using JsonNet.PrivateSettersContractResolvers;

public static class Seeder {
  public static void Seedit(string jsonData,
                            IServiceProvider serviceProvider) {
  JsonSerializerSettings settings = new JsonSerializerSettings {
    ContractResolver = new PrivateSetterContractResolver()
  };
  List<WeatherEvent> events =
   JsonConvert.DeserializeObject<List<WeatherEvent>>(
     jsonData, settings);
  using (
   var serviceScope = serviceProvider
     .GetRequiredService<IServiceScopeFactory>().CreateScope())
   {
     var context = serviceScope
                   .ServiceProvider.GetService<WeatherContext>();
     if (!context.WeatherEvents.Any()) {
       context.AddRange(events);
       context.SaveChanges();
     }
   }
 }
}

After instantiating the context, I have it check to see if I have any weather events in the database yet so I don’t re-seed. (This is logic I want for seeding during my demo so you may have different rules for the seeding.) Now I call context.AddRange passing in the list of WeatherEvent objects. I could use the DbSet directly or the context to call AddRange. And finally, SaveChanges.

So the key here, whether you are using EFCore or EF6 (so the use of the service is specific to the fact that my app is an ASPNET Core api), is really just reading the json file, deserializing the data and adding it to the context. This is oh, so much simpler than creating all of that data imperatively.

It may not always be the answer depending on the shape of your data but it was perfect for this particular model and the seed data that I needed.

 

Bio and Photos

Bio and Pictures as of April 2012

For Conferences, User Groups, etc.

[Longer Bio: 158 words, 1021 chars including spaces] 

Julie Lerman is the leading independent authority on Microsoft’s ADO.NET  Entity Framework and has been using and teaching the technology since its inception in 2006. Julie is the author of the highly acclaimed "Programming Entity Framework” book series from O’Reilly Media with recent editions on Code First (Dec 2011) and DbContext (Feb 2012) . She is well known in the .NET community as a Microsoft MVP, ASPInsider and INETA Speaker. She is a prolific blogger and a frequent presenter at technical conferences large and small around the world, such as TechEd and DevConnections. She also writes articles for many well-known technical publications, including the monthly Data Points column in MSDN Magazine. Julie keeps busy creating training videos for MSDN and Pluralsight.com.

Julia lives in Vermont where she has run the Vermont.NET User Group since 2002 and was a founding board member of the Vermont Software Developers Alliance. You can read her blog at www.thedatafarm.com/blog and her tweets at twitter.com/julielerman.

[Shorter Bio: 70 words, 454 chars including spaces]

Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find Julie presenting on Entity Framework and other Microsoft .NET topics at user groups and conferences around the world. Julie blogs at thedatafarm.com/blog, is the author of the highly acclaimed "Programming Entity Framework” books, the MSDN Magazine Data Points column and popular videos on Pluralsight.com. Follow Julie on twitter at julielerman.

julie_100x130.jpg juliex400.jpg JulieLermanGeekette.jpg
julie_100x130 juliex400 JulieLermanGeekette
100w x 130h, 7kb 400w x 442h, 32kb 110w x 127h, 5kb

 

 I have larger, hi-res versions as well. Email me if you want them.