Entity Framework Core 2.2 Owned Entity with UseLazyLoadingProxies - entity-framework-core

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

Related

Entity Framework creating database but not tables

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.

Unity.MVC register autogenerated dbcontext from ef

I have an autogenerated dbcontext from EF. I've created a partial class of this autogenerated EF class so I can make it implement an interface so that I can pass that interface to my service that will use it. That'll get passed in via Unity.MVC DI framework. When I try to register this interface to the actual class I'm getting an error:
"There is no implicit reference conversion from ContactsEntities to IContactsEntities."
I'm not sure why I'm getting that or how to solve it.
EF Autogen file:
public partial class ContactsEntities : DbContext
{
public ContactsEntities()
: base("name=ContactsEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Contact> Contacts { get; set; }
}
My file:
public interface IContactsEntities
{
DbSet<Contact> Contacts { get; set; }
}
public partial class ContactsEntities : IContactsEntities
{
public virtual DbSet<Contact> Contacts { get; set; }
}
Unity registering that gives the error:
container.RegisterType<IContactsEntities, ContactsEntities>();

How can I use Entity Framework from ASP.NET MVC 4?

I am getting started with ASP.NET and I have a problem. I created an MVC4 application in the Visual Studio. Then to the Models I added an ADO.NET Entity Data Model with the Database first method. Everything okay until I try to add a new Controller when I get an error message: 'Projectname.Models.Tablename' is not part of specified 'Projectname.Models.Contextname' class and the 'Projectname.Models.Contextname' class could not be modified to add a DbSet<Projectname.Models.Tablename> property to it.
What is my mistake?
Here is my generated model:
public partial class Example
{
public int ID { get; set; }
public string Name { get; set; }
}
End here is my generated context:
public partial class TestEntities : DbContext
{
public TestEntities()
: base("name=TestEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Example> Example { get; set; }
}

Why is EF code first throwing model backing context exception? Using 4.0.3

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.

Entity framework context and structure map disposing

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).