Dblock247 Dblock247 - 3 months ago 29
C# Question

Entity Framework 7 With Existing Database in .Net 5 MVC 6

Hi i am having some trouble pulling data from an existing database in Using Entity Framework 7 MVC 6. (I have posted the project code Here). I have set up appsettings.json with the proper connection string:

"Data": {
"DefaultConnection": {
"ConnectionString": "Data Source=localhost;Initial Catalog=Demo;Integrated Security=True"
}


I have my custom context:

public class DatabaseContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
}


Two Poco classes:

[Table("Customers")]
public class Customer
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string EmailAddress { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public User User { get; set; }
public bool Active { get; set; }
}

[Table("Users")]
public class User
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string EmailAddress { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public bool Active { get; set; }
}


And i am setting up the service in startup.cs

public Startup(IHostingEnvironment env)
{
// Set up configuration sources.

var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();

// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}

builder.AddEnvironmentVariables();
Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; set; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);

services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

services.AddMvc();

// Add application services.

}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();

app.UseApplicationInsightsRequestTelemetry();

if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");

// For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859
try
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
.Database.Migrate();
}
}
catch { }
}

app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

app.UseApplicationInsightsExceptionTelemetry();

app.UseStaticFiles();

app.UseIdentity();

// To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}

// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}


My users controller:

[Route("[controller]")]
public class UsersController : Controller
{
public DatabaseContext _context { get; set; }

public UsersController(DatabaseContext context)
{
_context = context;
}
[Route("[action]")]
public IActionResult Index()
{
using (_context)
{
List<User> users = _context.Users.ToList();
}


return View();
}
}


i keep getting the following error on the List line when I navigate to the Users/index page:

$exception {"Object reference not set to an instance of an object."} System.NullReferenceException

For some reason it is not pulling the information from the database. I create it in Microsoft SQLServer 2014. And there is data in the users table. Am i missing a step or am I trying to access the data the wrong way?

Answer

The main problem can be fixed by usage of

public UsersController([FromServices] DatabaseContext context)
{
    _context = context;
}

instead of

public UsersController(DatabaseContext context)
{
    _context = context;
}

It's possible to use

[FromServices]
public DatabaseContext _context { get; set; }

but one have to remove the constructor public UsersController(DatabaseContext context). The last way is not recommended, because RC2 dropped the second way. See the announcement.

The above changes fixes the first problem which you have, but the database and the test data which you use produces one more problem, because Updated field of your Users and Customers tables contains NULL values. Thus you have to use

public DateTime? Updated { get; set; }

instead of

public DateTime Updated { get; set; }

The way, which I would recommend you is the usage of the commend

dnx ef dbcontext scaffold
    "Data Source=localhost;Initial Catalog=Demo;Integrated Security=True"
    EntityFramework.MicrosoftSqlServer --outputDir ..\Bestro\Model --verbose

which you can execute in the same directory where the main project.json exist (in src\JenSolo). I wrapped parts of the command on the new line for better reading. One should place all in one line of cause. The above command will create Users and Customers classes instead of [Table("Users")]User and [Table("Customers")]Customer, but you can use the code as basis and to make all required modification later manually.

UPDATED: It seems to me that the following command corresponds better to generation of the scaffold classes:

dnx ef dbcontext scaffold
    "Data Source=localhost;Initial Catalog=Demo;Integrated Security=True"
    EntityFramework.MicrosoftSqlServer --outputDir ..\Bestro\Model --verbose
    --targetProject Bestro --dataAnnotations

because you use Class Library Bestro in the main project JenSolo. You should execute the above command from the command line with the folder ...\src\JenSolo as the current directory. It will create the Model folder in the Class Library project (Bestro). The Model folder will contains many *.cs files: one file for every database table and one additional file DemoContext.cs, which contains the the class DemoContext derived from DbContext (Demo is the name of the database, which you use). You should remove OnConfiguring function from DemoContext.cs to be able to configure the connection string via

services.AddEntityFramework()
   .AddSqlServer()
   .AddDbContext<DemoContext>(options =>
      options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

in ConfigureServices of Startup.cs of the main project JenSolo.

UPDATED: Starting with .NET Core RC2 one should use dotnet ef dbcontext scaffold instead of dnx ef dbcontext scaffold.