Fabricio Rodriguez
Fabricio Rodriguez

Reputation: 4249

.Net Core Web API with EF

I am brand new to both .Net Core and to Entity Framework. I am trying to learn both technologies, by following some tutorial, but I am struggling. My Web API project has two controllers - the wizard generated ValuesController.cs that gets created when creating a new Core Web API project, and a second controller I added called FilesController.cs.

My appsettings.json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },

  "ConnectionStrings": {
    "    \"PropWorxConnection\"": "server=a.b.com;user id=propworx;persistsecurityinfo=True;database=abcde;password=12345"
  }
}

My Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MySQL.Data.EntityFrameworkCore.Extensions;

namespace PropWorxAPI
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddDbContext<Models.PropWorxDbContext>(options =>
                options.UseMySQL(Configuration.GetConnectionString("PropWorxConnection")));
        }

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

            app.UseMvc();
        }
    }
}

A simple model:

namespace PropWorxAPI.Models
{
    [Table("files")]
    public partial class File
    {
        public int ID { get; set; }
        public string FileNum { get; set; }
    }
}

My Context:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MySQL.Data.EntityFrameworkCore.Extensions;

namespace PropWorxAPI.Models
{
    public class PropWorxDbContext : DbContext
    {
        private readonly string _connectionString;

        public PropWorxDbContext(string connectionString)
        {
            _connectionString = connectionString;
        }

        public PropWorxDbContext(DbContextOptions<PropWorxDbContext> options) : base(options)
        {
        }

        public DbSet<Models.File> Files { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Models.File>().ToTable("Files");
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseMySQL(_connectionString);
            base.OnConfiguring(optionsBuilder);
        }
    }
}

and my controller:

using Microsoft.AspNetCore.Mvc;
using PropWorxAPI.Models;

namespace PropWorxAPI.Controllers
{
    [Route("api/[controller]")]
    public class FilesController : Controller
    {
        private readonly PropWorxDbContext _context;

        public FilesController(PropWorxDbContext context)
        {
            _context = context;
        }

        [HttpGet]
        public string Get()
        {
            return "Controller Working!!!";
        }
    }
}

Now, when I run it (F5), it opens up Edge and goes to http://localhost:51932/api/values. This displays this output:

["value1","value2"]

So it's defaulting to ValuesController (How do I changed that???). Anyway, the important thing is the auto generated ValuesController works. I now manually change the URL to point to the FilesController I created, i.e.

http://localhost:51932/api/files

...and I get this:

HTTP 500 error That’s odd... the website can’t display this page

Now, if I remove the context argument from my FilesController - i.e. I go from:

public FilesController(PropWorxDbContext context)
        {
            _context = context;
        }

to

public FilesController()
        {
        }

then it works and it correctly displays:

this is the files controller

Any ideas why? Thanks, and sorry for the overly verbose post...

Upvotes: 1

Views: 1335

Answers (3)

Fabricio Rodriguez
Fabricio Rodriguez

Reputation: 4249

Figured it out - my ConnectionStrings in appsettings.json wasn't formatted correctly (I had some weird forward slashes - not sure why... thanks for your help though Ahmar and user1900799 - truly appreciated!

Upvotes: 0

Woot
Woot

Reputation: 1809

Your dependency injection isn't working. When the framework tries to create a new FilesController it can't because it doesn't know what PropWorxContext is.

namespace PropWorxAPI.Models
{
    public class PropWorxDbContext : DbContext
    {
        private readonly string _connectionString;

        //public PropWorxDbContext(string connectionString)
        //{
        //    _connectionString = connectionString;
        //}

        public PropWorxDbContext(DbContextOptions<PropWorxDbContext> options) : base(options)
        {
        }

        public DbSet<Models.File> Files { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Models.File>().ToTable("Files");
        }

        //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        //{
        //    optionsBuilder.UseMySQL(_connectionString);
        //    base.OnConfiguring(optionsBuilder);
        //}
    }
}

You dont need the connection string or the OnConfiguring override because you are doing these things in your Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // This must come before MVC
    services.AddDbContext<Models.PropWorxDbContext>(options => options.UseMySQL(Configuration.GetConnectionString("PropWorxConnection")));

    services.AddMvc();

}

The services.AddDbContext line registers your database with dependency injection. Which enables your controller to work using the correct constructor

public FilesController(PropWorxDbContext context)
{
    _context = context;
}

I think the only problem you have is the unnecessary code in your DbContext which would prevent the dependency injection container from newing it up.

Upvotes: 2

Ahmar
Ahmar

Reputation: 3877

Remove these two lines from OnConfiguring method.

optionsBuilder.UseMySQL(_connectionString);
base.OnConfiguring(optionsBuilder);

You already tell the provider in ConfigureServices method of startup.cs

services.AddDbContext<Models.PropWorxDbContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("PropWorxConnection")));

Updated DbContext

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MySQL.Data.EntityFrameworkCore.Extensions;

namespace PropWorxAPI.Models
{
    public class PropWorxDbContext : DbContext
    {

        public PropWorxDbContext(DbContextOptions<PropWorxDbContext> options) : base(options)
        {
        }

        public DbSet<Models.File> Files { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Models.File>().ToTable("Files");
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {

        }
    }
}

Upvotes: 2

Related Questions