How to add a parameterized DbContext to a Unit of work - entity-framework-core

I am trying to refactor my asp.net mvc + entity framework project to use repository pattern, and a unit of work. In the newest versions of asp.net mvc DbContext is creating automatically using a parameterized constructor, it looks like this:
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Course> Courses { get; set; }
}
How can I create an instance of this DbContext in unitOfWork class, what options should I write it the brackets?
P.S: I can't remove the parameters, cause they are used in program.cs in the next way
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext") ?? throw new InvalidOperationException("Connection string 'SchoolContext' not found.")));
And I also can't add an emty constuctor, cause it will cause an error due to the above statement. (you can't have both constructors)

Thanks to the comments I figured it out, here what I was looking for:
In the class create an instance of context
private SchoolContext _context;
And then initialize it using a constructor like this
public UnitOfWork()
{
var contextOptions = new DbContextOptionsBuilder<SchoolContext>()
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=Mentoring.Data;Trusted_Connection=True;MultipleActiveResultSets=true")
.Options;
_context = new SchoolContext(contextOptions);
}

Related

issue with new create dbcontext class object in asp.net core 2.1

I m new in .net core 2.1
I m working with .net core 2.1 with code first approach
issue is when I create a new object dbcontext class then give error see below line
dbcontextstudent db=new dbcontextstudent(); //here give an red line
appsettings.json
},
"ConnectionStrings": {
"sqlserverconn": "Server=DEVISSHAHID; Database=studdbs; User id=xxxx;Password=xxxxx;"
},
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//connection string
services.AddDbContext<DbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("sqlserverconn")));
student.cs
namespace WebApplication1.Models
{
public class student
{
[Key]
public int studid { get; set; }
public string studname { get; set; }
public string studsalary { get; set; }
public int studage { get; set; }
}
}
dbcontextstudent.cs
namespace WebApplication1.Models
{
public class dbcontextstudent : DbContext
{
public dbcontextstudent(DbContextOptions<dbcontextstudent> options) : base(options)
{
}
public DbSet<student> stud { get; set; }
}
}
HomeController.cs
I m not understood the above intellisense
I write the code as per intellisense but still give an error I know error is clear but not solved
which place doing I m wrong?
You will have to pass your DbContext type to the AddDbContext method in ConfigureServices method like this:
services.AddDbContext<dbcontextstudent>(options => options.UseSqlServer(Configuration.GetConnectionString("sqlserverconn")));
After that, you have registered the dbcontextstudent class in dependency injection.
You shouldn't create the instance of dbcontextstudent on your own like you did:
dbcontextstudent db=new dbcontextstudent();
Instead you can inject it though the constructor of your controller like this:
public HomeController : Controller
{
private readonly dbcontextstudent _db;
public HomeController(dbcontextstudent db)
{
_db = db;
}
... and then you can use the _db variable in your post action
}

Log Exceptions with EntityFramework in WebAPI Exception Filter or IExceptionLogger

I'm thinking to use ExceptionLogger or ExceptionFilterAttribute in my Web API 2.x application. My OR/M is Entity Framework 6 and AutoFac as my IoC.
Consider exception attribute below:
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
public IDbContext MyContext { get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
// some code
MyContext.SaveChanges();
}
}
I think, if my app throws any exception OnException method will be invoke and I need a new instance of DbContext to save the error log, but my AutoFac configuration for DbContext is InstancePerRequest so I think AutoFac will resolve MyContext as a shared DbContext.
Here is my AutoFac configuration for DbContext:
builder.RegisterType<DbContext>()
.As<IDbContext>()
.InstancePerRequest();
How could I make AutoFac to reslove DbContext as a new instance for ApiExceptionFilterAttribute?
Ok, since no one answered my question, I talk a little about how everyone could solve this problem. To solve this problem you can only use ServiceLocator pattern (it's an anti-pattern.). By injecting a life-time-scope object of AutoFac you are able to create different scopes.
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
// public IDbContext MyContext { get; set; }
public readonly ILifetimeScope LifeTimeScpoe{ get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
// some code
using(var scope = LifeTimeScope.BeginLifeTimeScope("ExceptionLogging"))
{
using(var dbContext = scope.Resolve<>(IDbContext))
{
// do some log logic
dbContext.SaveChanges();
}
}
}
}

How to create Table per type inheritance in Entity Framework Core 2.0 code first?

Following code generates only single table "CertificateEvent".
How do I achieve TPT inheritance in EF Core 2.0?
public abstract class CertificateEvent {
public int CertificateEventId { get; set; }
}
public class Assignment : CertificateEvent {...}
public class Assessment : CertificateEvent {...}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
public DbSet<Assessment> AssessorAssessments { get; set; }
public DbSet<Assignment> AssessorAssignments { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CertificateEvent>().ToTable(nameof(CertificateEvent));
modelBuilder.Entity<Assessment>().ToTable(nameof(Assessment));
modelBuilder.Entity<Assignment>().ToTable(nameof(Assignment));
}
}
class MyDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<MyDbContext>();
builder.UseSqlServer("Server=(local);Database=Test;Trusted_Connection=True;MultipleActiveResultSets=true");
return new MyDbContext(builder.Options);
}
}
I've also tried dotnet ef migrations add Inheritance, but it did not created TPT inheritance in the database
TPT is not in EF Core (yet). See
The feeling from our team is that TPT is generally an anti-pattern and
results in significant performance issues later on. While enabling it
may make some folks "happier" to start with it ultimately just leads
to issues. We are willing to consider it though, so we're leaving this
open and will consider it based on the feedback we get.
https://github.com/aspnet/EntityFrameworkCore/issues/2266

EntityFramework how to not map a class but do map it's inherited properties

We use EntityFramework 6.1 with CodeFirst in our web mvc application (StdWebApp). Now we want to make a new custom version of this application (CustomWebApp) .
The CustomWebApp will use most of the code of the standard one, in it's domain model it will extend the Person class.
In CustomDomain we make implement a new DbContext that must connect with the database of the custom app (CustomSqlDb).
In (C#) code there is no problem that there is a Person in Domain and in CustomDomain. However we have not been able to devise a mapping for Person in the Custom DbContext that will:
Create a single "Person" table.
Contains fields form "CustomDomain.Person" AND those from "Domain.Person".
We tried some variants like this:
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
}
);
using this document as our inspiration msdn mapping types
But EF complains about the simple name beeing equal.
Obviously we could rename the "Person" in "CustomDomain" to "PersonCustom" but that could lead to a lot of silly names if we have to do this again in the future like "PersonCustomExtraSpecial" etc.
Thoughts anyone?
UPDATE
we tried the solution suggested by mr100, here is the complete code:
namespace Domain
{
public class Person
{
public int Id { get; set; }
public string Stuff { get; set; }
}
}
namespace CustomDomain
{
public class Person : Domain.Person
{
public string ExtraStuff { get; set; }
}
}
namespace CustomDomain
{
public class DbModel : DbContext
{
DbSet<CustomDomain.Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomDomain.Person>().Map(m => m.ToTable("Person"));
}
}
}
This still result in the error
The type 'CustomDomain.Person' and the type 'Domain.Person' both have the same simple name of 'Person' and so cannot be used in the same model. All types in a given model must have unique simple names. Use 'NotMappedAttribute' or call Ignore in the Code First fluent API to explicitly exclude a property or type from the model.
So we added the following code:
namespace CustomDomain
{
public class DbModel : DbContext
{
DbSet<CustomDomain.Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Domain.Person>();
modelBuilder.Entity<CustomDomain.Person>().Map(m => m.ToTable("Person"));
}
}
}
Still same result.
To achieve this your DbContext class in CustomWebApps should have property People defined like this:
public DbSet<CustomDomain.Person> People {get; set;}
and no property:
public DbSet<Domain.Person> People {get; set;}
even if it comes from StdWebApp DbContext class from which CustomWebApp DbContext class may derive (if that is the case for you). Additionally you may set properly table name:
modelBuilder.Entity<Person>().ToTable("Person");

On Insert / Update logic in EF code first

I would like to add some logic to the insert and update events of some EF objects.
I have a MVC application with category object which has a property which is a slugified version of the name property.
public class Category
{
public string Name { get; set; }
public string UrlName{ get; set; }
}
I would like to set the UrlName property only on the insert and update events because my slugify logic is quite elaborate.
I am aware that I can add some logic inside the SaveChanges() function on the context itself but I rather would like to put the code closer to the entity itself.
Is there a way to accomplish such thing using EF code first?
You can setup a base class with methods to be called before insert and update
public abstract class Entity
{
public virtual void OnBeforeInsert(){}
public virtual void OnBeforeUpdate(){}
}
public class Category : Entity
{
public string Name { get; set; }
public string UrlName{ get; set; }
public override void OnBeforeInsert()
{
//ur logic
}
}
Then in your DbContext
public override int SaveChanges()
{
var changedEntities = ChangeTracker.Entries();
foreach (var changedEntity in changedEntities)
{
if (changedEntity.Entity is Entity)
{
var entity = (Entity)changedEntity.Entity;
switch (changedEntity.State)
{
case EntityState.Added:
entity.OnBeforeInsert();
break;
case EntityState.Modified:
entity.OnBeforeUpdate();
break;
}
}
}
return base.SaveChanges();
}
No there is no such extension point because your entity is POCO - it is not aware of its persistence. Such logic must be triggered in data access layer which is aware of persistence. DbContext API offers only overriding of SaveChanges.
You can expose custom events or methods on your entities and call them during processing in SaveChanges.