When I run the migration, the database is creating but none of the tables are. I have no clue what I'm doing wrong as I did the same thing the other day with no issue. The initial migration ran and created the database but, none of the tables. I've tried deleting the db and migrations and doing the whole process over again with no luck. Below is some code and a picture of my folder structure. Hopefully someone can point out what I'm doing wrong.
Here is one of my models:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Workout_Tracker.Models
{
public class Exercise
{
public int ID { get; set; }
public int Weight { get; set; }
public string Name { get; set; }
public int WorkoutID { get; set; }
public Workout Workout { get; set; }
public IList<ExerciseSet> Sets { get; set; }
}
}
Here is my dbcontext:
namespace Workout_Tracker.Data
{
public class ApplicationDbContext : DbContext
{
public DbSet<User> Users;
public DbSet<Workout> Workouts;
public DbSet<Exercise> Exercises;
public DbSet<ExerciseSet> Exercise_Sets;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
}
Here is a migration:
namespace Workout_Tracker.Migrations
{
public partial class first : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
Here is startup.cs:
namespace Workout_Tracker
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
And here is my folder structure:
You are just exposing the DbSet<T> without a getter and setter.
Either change your db-sets to this:
public DbSet<User> Users { get; set; }
public DbSet<Workout> Workouts { get; set; }
public DbSet<Exercise> Exercises { get; set; }
Or even better, use the fluent-API and don't expose DbSet<T> at all.
protected override void OnModelCreating(ModelBuilder builder)
{
// you can define table names, keys and indices here
// there is also `IEntityTypeConfiguration<T>` which is much better since it keeps the DbContext clean
// https://codeburst.io/ientitytypeconfiguration-t-in-entityframework-core-3fe7abc5ee7a
builder.Entity<User>();
builder.Entity<Workout>();
builder.Entity<Exercise>();
}
Then when injecting your ApplicationDbContext you can use the generic-method context.Set<T>. For instance context.Set<User> to retrieve the users db-set.
FYI: There is also currently no db-set for ExerciseSet which is a subset of Exercise.
The docs for entity-framework are very good, I'd recommend to get familiar with them.
Related
I want to create a bridging table between dbo.AspNetUsers and dbo.Recipe called dbo.Binder. A user can save many recipes and a recipe can be saved by many users (many-to-many).
I'm able to create bridging tables within my DbContext fine, called RecipeContext, e.g dbo.Recipe, dbo.TagRecipe and dbo.Tag, but I am confused how I'm suppose to do this between two different DbContexts, that being RecipeContext (DbContext) and ApplicationDbContext(IdentityDbContext<IdentityUser>).
One solution I read was for RecipeContext to inherit from IdentityDbContext<IdentityUser>, like so:
public class RecipeContext :IdentityDbContext<IdentityUser>
Though when I do this and run
Update-Database -Context RecipeContext
I get an error
There is already an object named `AspNetRoles` in the database
I suppose this is from when I had the contexts separate, and yes, the AspNetRoles/identity tables are already created in my database from before.
I'm wondering if for one, I'm on the right track and two, what do I do about the fact the Identity tables already exist?
Thank you
For reference
RecipeContext:
public class RecipeContext :IdentityDbContext<IdentityUser> /*: DbContext*/
{
public RecipeContext(DbContextOptions options) : base(options) { }
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<TagRecipe> TagRecipes { get; set; }
public DbSet<StarRating> StarRatings { get; set; }
public DbSet<Binder> Binders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Identity
base.OnModelCreating(modelBuilder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
...(code left out)
// Override pural table names to singular
modelBuilder.Entity<Binder>().ToTable("Binder");
// Declare use UserId and RecipeId as primary keys for Binder
modelBuilder.Entity<Binder>()
.HasKey(b => new { b.UserId, b.RecipeId });
modelBuilder.Entity<Binder>()
.HasOne<ApplicationUser>(b => b.User)
.WithMany(u => u.Binders)
.HasForeignKey(b => b.UserId);
modelBuilder.Entity<Binder>()
.HasOne<Recipe>(b => b.Recipe)
.WithMany(u => u.Binders)
.HasForeignKey(b => b.RecipeId);
}
}
ApplicationDbContext (this is to be merged into RecipeContext):
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Binder class:
public class Binder
{
public string UserId { get; set; }
public int RecipeId { get; set; }
public virtual ApplicationUser User { get; set; }
public Recipe Recipe { get; set; }
}
ApplicationUser class (this was self created to create a collection of Binders)
public class ApplicationUser : IdentityUser
{
public ICollection<Binder> Binders { get; set; }
}
Recipe class:
public class Recipe
{
public int Id { get; set; }
...(code left out)
public ICollection<Binder> Binders { get; set; }
}
Startup class - ConfigureServices method (I feel some adjustments will need to be made if the two dbContexts are merging)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Identity
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.AddRazorPages(); // Added - Identity scaffhold Views
// Recipe context
services.AddDbContext<RecipeContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
}
appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=WCIMDBCoreEF1ModelTest;Trusted_Connection=True;MultipleActiveResultSets=true",
"ApplicationDbContextConnection": "Server=(localdb)\\mssqllocaldb;Database=MVCApp;Trusted_Connection=True;MultipleActiveResultSets=true"
}
I am currently working on a codebase, to which I want to add a number of new entities with corresponding owned entities. Because, in some other part of the codebase I won't touch, UseLazyLoadingProxies is called; I receive the following exception:
System.InvalidOperationException : Navigation property 'Foo' on entity type 'FooOwner' is not virtual. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.
If I mark the property as virtual, the owned entity goes into a new table; which I do not want either.
According to github issues I encountered, these seem to be the expected behavior.
My question is this: Is there a way to work around this problem, such that, I can somehow mark the owned entity to be stored in the same table as the owner entity, and if possible to be always Included, eagerly loaded.
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using NUnit.Framework;
namespace StackOverflowObjectContext.Tests
{
public class Foo
{
public long Id { get; set; }
public int Data { get; set; }
}
public class FooOwner
{
public int Id { get; set; }
public Foo Foo { get; set; }
}
public class FooOwnerMap : IEntityTypeConfiguration<FooOwner>
{
public void Configure(EntityTypeBuilder<FooOwner> builder)
{
builder.HasKey(x => x.Id);
builder.HasOne(x => x.Foo);
}
}
public class StackOverflowObjectContext : DbContext
{
public StackOverflowObjectContext(DbContextOptions options) : base(options) { }
DbSet<FooOwner> FooOwners { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new FooOwnerMap());
base.OnModelCreating(modelBuilder);
}
}
[TestFixture]
public class StackOverflowTest
{
StackOverflowObjectContext _objectContext;
[SetUp]
public void SetUp()
{
var builder = new DbContextOptionsBuilder<StackOverflowObjectContext>()
.UseSqlServer(#"Data Source=.\SQLEXPRESS;Initial Catalog=StackOverflow;Integrated Security=True")
.UseLazyLoadingProxies();
_objectContext = new StackOverflowObjectContext(builder.Options);
}
[Test]
public void CanGenerateCreateScript()
{
var script = _objectContext.Database.GenerateCreateScript();
Debug.WriteLine(script);
}
}
}
You should use OwnsOne instead of HasOne
I am creating db and initializing data with code below, if DB doesnt exist, it creates db and populate it, when I run application second time I get error
Cannot drop database "aspnet-app" because it is currently in use.
Application_start, with initializations
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Database.SetInitializer(new ComponentDbInitialize());
ComputerContext context = new ComputerContext();
context.Database.Initialize(true); //here it fails on second run
context.SaveChanges();
}
ComputerContext
public ComputerContext():base("name=Default")
{
}
public DbSet<Computer> Computers { get; set; }
public DbSet<Motherboard> Motherboards { get; set; }
public DbSet<CPU> CPUs { get; set; }
public DbSet<Case> Cases { get; set; }
public DbSet<HDD> HDDs { get; set; }
public DbSet<RAM> RAMs { get; set; }
public DbSet<GPU> GPUs { get; set; }
public DbSet<PSU> PSUs { get; set; }
}
Initialize class
public class ComponentDbInitialize : DropCreateDatabaseAlways<ComputerContext>
{
protected override void Seed(ComputerContext context)
{
GetCpu().ForEach(p => context.CPUs.Add(p));
GetGpu().ForEach(p => context.GPUs.Add(p));
GetCase().ForEach(p => context.Cases.Add(p));
GetHdd().ForEach(p => context.HDDs.Add(p));
GetMb().ForEach(p => context.Motherboards.Add(p));
GetPsu().ForEach(p => context.PSUs.Add(p));
GetRam().ForEach(p => context.RAMs.Add(p));
context.SaveChanges();
}
private static List<CPU> GetCpu() .....
private static List<GPU> GetGpu().....
private static List<HDD> GetHdd()....
private static List<Case> GetCase()....
private static List<Motherboard> GetMb()....
private static List<PSU> GetPsu()....
private static List<RAM> GetRam()....
I get same error even with
context.Database.Initialize(false);
Thank you for any help/advice
Your initialiser is using the DropCreateDatabaseAlways class which, as it suggests, drops that database every time the application is initialised.
Instead perhaps you could use CreateDatabaseIfNotExists or DropCreateDatabaseIfModelChanges:
public class ComponentDbInitialize : CreateDatabaseIfNotExists<ComputerContext>
{
}
Heres the exception:
The model backing the 'ScannerContext' context has changed since the
database was created. Consider using Code First Migrations to update
the database (http://go.microsoft.com/fwlink/?LinkId=238269).
I get this everytime I run my application. I cant figure out what it means. I think it means something isn't mapped correctly, but I cant figure out what. I am using the code first model, and I have an existing database that I want totally custom mappings for. Right now, I have everything in my classes named the same as my database to eliminate possible cuases.
The Exception is thrown when I try to .Add() the entity to the context.
The Entity as it is in the Database
The Entity in my DataLayer
public class EAsset
{
public int i_GID { get; set; }
public EAssetType Type { get; set; }
public EOrgEnvironment Environment { get; set; }
public EUser Contact { get; set; }
public string s_Name { get; set; }
public string s_Role { get; set; }
public DateTime d_Added { get; set; }
public DateTime d_LastUpdated { get; set; }
public bool b_Retired { get; set; }
public EAsset()
{
Type = new EAssetType();
Environment = new EOrgEnvironment();
Contact = new EUser();
d_Added = DateTime.Now;
d_LastUpdated = DateTime.Now;
}
}
The Context Object (with attempted table mapping and key assignment)
public class ScannerContext : DbContext
{
public ScannerContext()
: base("LabDatabase") { }
public DbSet<EAsset> EAssets { get; set; }
public DbSet<EAssetType> EAssetTypes { get; set; }
public DbSet<EOrgEnvironment> EOrgEnvironments { get; set; }
public DbSet<EUser> EUsers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EAsset>().HasKey(k=>k.i_GID).ToTable("t_Assets");
modelBuilder.Entity<EAssetType>().HasKey(k => k.i_ID).ToTable("t_Asset_Types");
modelBuilder.Entity<EOrgEnvironment>().HasKey(k => k.i_ID).ToTable("t_Org_Environments");
modelBuilder.Entity<EUser>().HasKey(k => k.i_ID).ToTable("t_Users");
base.OnModelCreating(modelBuilder);
}
}
The Program
class Program
{
static void Main(string[] args)
{
EAsset Entity = new EAsset { s_Name = "jewri-pc" };
var sContext = new ScannerContext();
sContext.EAssets.Add(Entity);
sContext.SaveChanges();
}
}
For EF runtime version 4.0.3 / version 4.0
public class ScannerContext : DbContext
{
public ScannerContext()
: base("LabDatabase") { }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<ScannerContext>(null); // <--- This is what i needed
...
base.OnModelCreating(modelBuilder);
}
}
With that code installed I am now chasing errors related to having all my relationships accounted for in the model. The FK Constraints are forcing me to add the missing relational items.
Found info here. They explain the importance a bit.
The model backing the <Database> context has changed since the database was created
Enable-Migrations -ContextTypeName EmployeeProject.Models.DepartmentContext
Means you have to write your project name.Models.Context name
It will work.
I have strange problem with disposing entity framework connection in asp.net mvc application.
I have simple structure for example :
Entity :
public class EmployeeReport
{
public int EmployeeReportId { get; set; }
public DateTime Created { get; set; }
public Decimal Hours { get; set; }
public string Comment { get; set; }
public int EmployeeId { get; set; }
public int ContractId { get; set; }
public int ServiceId { get; set; }
public virtual ReportContract Contract { get; set; }
public virtual ReportService Service { get; set; }
public virtual Employee Employee { get; set; }
}
Entity mapper :
public class EmployeeReportMapper : EntityTypeConfiguration<EmployeeReport>
{
public EmployeeReportMapper()
{
ToTable("intranet_employee_reports");
HasKey(x => x.EmployeeReportId);
Property(x => x.Created).HasColumnName("Created").IsRequired();
Property(x => x.Comment).HasColumnName("Comment").IsOptional();
Property(x => x.Hours).HasColumnName("Hours").IsRequired();
HasRequired(x => x.Employee).WithMany().HasForeignKey(x => x.EmployeeId);
HasRequired(x => x.Service).WithMany().HasForeignKey(x => x.ServiceId);
HasRequired(x => x.Contract).WithMany().HasForeignKey(x => x.ServiceId);
}
}
DbContext - interface
public interface IDbContext : IDisposable
{
IDbSet<EmployeeReport> EmployeeReports { get; }
}
DbContext - implementation
public class IntranetDbContext : DbContext,IDbContext
{
public IDbSet<EmployeeReport> EmployeeReports { get; set; }
...
public IntranetDbContext() : base("IntranetDb")
{
Database.SetInitializer<IntranetDbContext>(null);
}
public void Commit()
{
SaveChanges();
}
public void ChangeEntityState(object entity, EntityState entityState)
{
Entry(entity).State = entityState;
}
public void ExecuteSql(string query, SqlParameterCollection parameterCollection)
{
Database.ExecuteSqlCommand(query, parameterCollection);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
/* Register custom mapping class */
modelBuilder.Configurations.Add(new EmployeeReportMapper());
....
base.OnModelCreating(modelBuilder);
}
}
Finally my structure map configuration :
public class CoreRegistry : Registry
{
public CoreRegistry()
{
For<IDbContext>().HttpContextScoped().Use<IntranetDbContext>();
...
}
}
and Global.asax :
protected void Application_EndRequest(object sender, EventArgs e)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
Ok, now the problem, in my application i using standard constructor dependency injection
or call ObjectFactory.GetInstance().
In one of my controller i call service class, which has access to dbcontext and fetch some entites.
Unfortunately i get classic exception :
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
This is strange, because service is called during request and all data are forced to client in controller...
Any idea, where I do mistake?
EDIT :
Service code :
public class EmployeeService : IEmployeeService
{
/// <summary>
/// IDbContext reference
/// </summary>
private readonly IDbContext _dbContext;
public EmployeeService(IDbContext dbContext)
{
_dbContext = dbContext;
}
public List<Employee> GetSubordinateEmployees(Employee employee)
{
List<Employee> employees = new List<Employee>();
foreach (var unit in employee.OrganizationUnits.ToList()) /* throw exception*/
{
foreach (var childrenUnit in unit.ChildrenUnits)
{
employees.AddRange(childrenUnit.Employees);
}
}
return employees.Distinct().ToList();
}
Controller :
private readonly IEmployeeService _employeeService;
public EmployeeReportController(IEmployeeService employeeService)
{
_employeeService = employeeService;
}
[HttpGet]
public ActionResult SearchReports()
{
List<Employee> employees = _employeeService.GetSubordinateEmployee(IntranetSession.Current.LoggedAccount.Employee).ToList(); // Exception!
...
return View();
}
}
Your code doesn't use current DbContext at all. The problem is:
IntranetSession.Current.LoggedAccount.Employee
Followed by:
employee.OrganizationUnits.ToList()
Your employee stored in session was loaded with context which is already disposed but it still keeps reference to that context. When you loaded that employee you didn't eager load his organizations so once you access her OrganizationUnits it will try to trigger lazy loading on disposed context.
There are two ways to avoid this problem:
Eager load all information you need to use from your employee stored in session. It means retrieving employee like context.Employees.Include(e => e.OrganizationUnits).Single(...)
Store only employee's Id in session and load on-demand data you need
If you want to cache whole employee in session make sure that you will disable proxy creation for objects stored in session by calling :
context.Configuration.ProxyCreationEnabled = false;
It will ensure that cached data will not keep reference to disposed context (which btw. prevent garbage collector to collect context and all its referenced objects).