I have an interface that is being attached in my DI
builder.RegisterType<TaskTempo>().As<ITaskTempo>();
This is being done because ITaskTempo is dynamic and could be one of multiple objects, but sharing same interface.
Anyway, I have DbContext where I map the DbSet with entity manually
// DI register
builder.RegisterType<MapperFor890>().As<IEntityMapper>();
// ...
// DbContext
public partial class MyDBContext : DbContext
{
private readonly IEntityMapper _entityMapper;
// Please not that I do not want to manually add all of diff implementations of ITaskTempo here
// public virtual DbSet<TaskTempo1> TaskTempo { get; set; }
public MyDBContext(DbContextOptions<MyDBContext> options, IEntityMapper mapper)
: base(options)
{
_entityMapper = mapper;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Map Entities
_entityMapper.Map(modelBuilder, this);
}
}
// ...
// EntityMapper
public class MapperFor890 : IEntityMapper
{
public void Map(ModelBuilder modelBuilder, DbContext context)
{
modelBuilder.Entity<TaskTempo1>(entity =>
{
// Map here the properties
});
}
}
Then, I am able to fetch data from EfRepository:
// Global Repository
public class EfRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
protected readonly ILogger<EfRepository<TEntity>> Logger;
public EfRepository(DbContext context,
ILogger<EfRepository<TEntity>> logger)
{
Context = context;
Logger = logger;
}
public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
Logger.LogDebug($"Finding '{typeof(TEntity).Name}' with predicate");
return Context.Set<TEntity>().Where(predicate);
}
}
// TaskTempo Repository
public class TaskTempoRepository<TTaskTempo> : EfRepository<TTaskTempo>, ITaskScanRepository<TTaskTempo> where TTaskTempo : class, ITaskTempo
{
public MyDBContext DbContext => Context as MyDBContext;
public TaskScanRepository(MyDBContext context, ILogger<TaskScanRepository<TTaskTempo>> logger) : base(context,logger)
{
}
}
// DI injection
builder.RegisterType<TaskTempo1>().As<ITaskTempo>();
builder.RegisterType<TaskTempoRepository<ITaskTempo>>().As<ITasTempoRepository<ITaskTempo>>();
Once I fetch
_taskTempoRepository.Find(t => t.Id = 3);
I am getting the following error:
Cannot create a DbSet for 'ITaskTempo' because this type is not included in the model for the context
Related
I'm trying to use EF Core InMemory Database and XUnit in my integration tests, but unfortunately, I'm getting this Exception:
System.InvalidOperationException : Relational-specific methods can only be used when the context is using a relational database provider.
This is the Class that uses the WebApplicationFactory:
public class TestFactory<TProgram, TDbContext> : WebApplicationFactory<TProgram>
where TProgram : class where TDbContext : DbContext
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.RemoveDbContext<TDbContext>();
services.AddDbContext<TDbContext>(options =>
{
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
});
});
}
}
This is the base Class used in the Test Classes:
public class TestFactoryBase : IClassFixture<TestFactory<Program, CDbContext>>
{
public HttpClient _client;
public CDbContext _dbContext;
public TestFactoryBase(TestFactory<Program, CDbContext> factory) {
_client = factory.CreateClient();
_dbContext = factory.Services.GetService<CDbContext>();
}
}
This is the Test Class:
public class RolesControllerTest : TestFactoryBase
{
private IRoleRepository _rolesRepository;
public RolesControllerTest(TestFactory<Program, CDbContext> factory) : base(factory)
{
_rolesRepository = CreateRoleRepository(_dbContext);
}
// Tests Here!
}
How can I solve this exception?
I'm trying to use Entity Framework Core with ASP.NET Boilerplate .NET Core, but I don't want to use Repository built-in functions.
There is a problem with my DB context; it keeps returning:
System.ArgumentNullException: 'Value cannot be null.'
for the DbContext instance as shown below:
public class MainProjectsAppService : ApplicationService
{
private readonly DecentralizationDbContext _ctx;
public MainProjectsAppService(IDbContextProvider<DecentralizationDbContext> dbContextProvider)
{
_ctx = dbContextProvider.GetDbContext();
}
public void CustomizedCreateMainProject(MainProject mainProject)
{
MainProject customizedMainProject = new MainProject
{
...
};
_ctx.MainProjects.Add(customizedMainProject);
_ctx.SaveChanges();
}
}
Below is the DbContext class code:
namespace Decentralization.EntityFrameworkCore
{
public class DecentralizationDbContext : AbpZeroDbContext<Tenant, Role, User, DecentralizationDbContext>
{
/* Define a DbSet for each entity of the application */
public DbSet<MainProject> MainProjects { get; set; }
public DecentralizationDbContext(DbContextOptions<DecentralizationDbContext> options)
: base(options)
{
}
}
}
Do not call dbContextProvider.GetDbContext() in the constructor.
Define a getter instead:
public class MainProjectsAppService : ApplicationService
{
private readonly IDbContextProvider<DecentralizationDbContext> _dbContextProvider;
private DecentralizationDbContext _ctx => _dbContextProvider.GetDbContext();
public MainProjectsAppService(IDbContextProvider<DecentralizationDbContext> dbContextProvider)
{
_dbContextProvider = dbContextProvider;
}
}
Reference: aspnetboilerplate/aspnetboilerplate#4809
I am using repository pattern on EF Core and Autofac in a windows service.
I have a service that needs to connect with the some dozen databases which have the same schema (same dbcontext) but only different data.
How can I achieve this in my service using Autofac? Belo
public class ReportRepository : IReportRepository
{
private readonly ReportDbContext dbContext;
public ReportRepository(ReportDbContext dbContext)
{
this.dbContext = dbContext
}
public SomeModel GetData()
{
return dbContext.SalesData;
}
}
public class ReportService : IReportService
{
private readonly IReportRepository reportRepositoryEUServer;
public ReportService(IReportRepository reportRepositoryEUServer)
{
this.reportRepositoryEUServer = reportRepositoryEUServer
}
public SomeModelDto GenerateReport()
{
var euData = reportRepositoryEUServer.GetData();
// I need to call other servers (e.g LATAM) here and get the data and aggregate them with euData
}
}
Create base context including all settings, dbsets etc:
public abstract class BaseContext : DbContext
{
public BaseContext(DbContextOptions options)
: base(options)
{ }
public DbSet<object> FirstSet { get; set; }
...
}
inherit from BaseContext for both DBs
public class LATAMContext : BaseContext
{
public LATAMContext(DbContextOptions<LATAMContext> options) : base(options)
{
}
}
public class EUContext : BaseContext
{
public EUContext(DbContextOptions<EUContext> options) : base(options)
{
}
}
and register both in Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<LATAMContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LATAMConnectionString")));
services.AddDbContext<EUContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EUConnectionString")));
// Autofac
var builder = new ContainerBuilder();
// needed only if you plan to inject ICollection<BaseContext>
builder.RegisterType<LATAMContext>().As<BaseContext>();
builder.RegisterType<EUContext>().As<BaseContext>();
builder.Populate(services);
return new AutofacServiceProvider(builder.Build());
}
add connection strings in appsettings.json
"ConnectionStrings": {
"LATAMConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true",
"EUConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
and now you can inject both contexts
public class ReportRepository : IReportRepository
{
private readonly LATAMContext latamDbContext;
private readonly EUContext euDbContext;
public ReportRepository(LATAMContext latamDbContext, EUContext euDbContext)
{
this.latamDbContext = latamDbContext;
this.euDbContext = euDbContext;
}
}
or if you plan to inject collection of contexts
public class ReportRepository : IReportRepository
{
private readonly ICollection<BaseContext> dbContexts;
public ReportRepository(ICollection<BaseContext> dbContexts)
{
this.dbContexts = dbContexts;
}
}
to access specific context
var _euContext = dbContexts.FirstOrDefault(x => x is EUContext) as EUContext;
var _latamContext = dbContexts.FirstOrDefault(x => x is LATAMContext) as LATAMContext;
I wanna to try generic but I get some problem.
and this is my step
step 1. I create a database Model also inheritance class
public class DBRepo { }
public partial class UserAccount : DBRepo
{
public int Id { get; set; }
public string Account { get; set; }
public string Pwd { get; set; }
}
step 2. I wish all CRUD action can using this interface. so I do this
public class DBServices
{
public interface IDBAction<TEntity> where TEntity : DBRepo
{
void InsertData(TEntity entity);
}
public class dbCRUD<TEntity> : IDBAction<TEntity> where TEntity : DBRepo
{
private readonly CoreContext _db;
private DbSet<TEntity> dbSet;
public dbCRUD(CoreContext _db)
{
this._db = _db;
this.dbSet = _db.Set<TEntity>();
}
public void InsertData(TEntity _entity)
{
this.dbSet.Add(_entity);
this._db.SaveChanges();
}
}
}
and then I usine ServiceProvider like
ServiceProvider provider = new ServiceCollection()
.AddSingleton<IDBAction<DBRepo>>()
.BuildServiceProvider();
provider.GetService<IDBAction<DBRepo>>().InsertData(_ua);
and I'll get this error
Cannot instantiate implementation type ....
so I change to try other way like.
in Constructor
private readonly IDBAction<DBRepo> dBAction;
public HomeController( IDBAction<DBRepo> _dBAction)
{
this.dBAction = _dBAction;
}
....
this.dBAction.InsertData(_ua);
sure.I get error again
InvalidOperationException: Unable to resolve service for type...
have some can teach me how to fix the problem?
-> Update
I try to change like but it's failed
ServiceProvider provider = new ServiceCollection()
.AddScoped<IDBAction<DBRepo>, dbCRUD<DBRepo>>()
.AddScoped<CoreContext>()
.BuildServiceProvider();
error same this
Unable to resolve service for type...
it's my DBContext
public virtual DbSet<UserAccount> UserAccount { get; set; }
public CoreContext(DbContextOptions<CoreContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(#"Connection String");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserAccount>(entity =>
{
entity.Property(e => e.Account).IsRequired();
entity.Property(e => e.Pwd)
.IsRequired()
.HasMaxLength(20);
});
}
You are registering it in the wrong way. You must provide the implementation of your (generic) and also the DbContext must be registered.
ServiceProvider provider = new ServiceCollection()
.AddSingleton<IDBAction<DBRepo>, dbCRUD<DBRepo>>()
.BuildServiceProvider();
provider.GetService<IDBAction<DBRepo>>().InsertData(_ua);
Also registering it as a singleton will cause problems with change-tracking of entity-framework. So you should register it like this:
ServiceProvider provider = new ServiceCollection()
.AddScoped<IDBAction<DBRepo>, dbCRUD<DBRepo>>()
.AddScoped<CoreContext>()
.BuildServiceProvider();
provider.GetService<IDBAction<DBRepo>>().InsertData(_ua);
You could also implement a real generic like this
public interface IEntity
{
Guid Id { get; set; }
}
public class DbAction<TEntity> : IDbAction<TEntity> where TEntity: class, IEntity, new()
{
public void InsertData(TEntity entity)
{
...
}
}
Now register it as a generic
ServiceProvider provider = new ServiceCollection()
.AddScoped(typeof(IDbAction<>), typeof(DbAction<>))
.AddScoped<CoreContext>()
.BuildServiceProvider();
provider.GetService<IDBAction<DBRepo>>().InsertData(_ua);
Small example, but would work.
Edit:
DbContextOptions must of course be passed to the DbContext for the IoC to work.
.AddDbContext<CoreContext>(options => options.UseSqlServer("my-conntection-string")); // change provider if necessary, this will only work with MS SQL Server
I am Creating a web api using Repository pattern and 3 tier architecture. i also made a IOC Container (Autofac) for hadling dependencies. But i am keep getting error, in tried everything. The Error is: An error occurred when trying to create a controller of type 'StudentsController'. Make sure that the controller has a parameterless public constructor.
Here is my Code:
My Context Class:
public class StudentContext: DbContext
{
public StudentContext(): base("name=StudentDB")
{
//Database.SetInitializer<StudentContext>(null);
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new StudentMap());
}
public DbSet<Student> Students { get; set; }
}
My Repository Class:
public class Repository<T> : IRepository<T> where T : class
{
public readonly StudentContext context;
public IDbSet<T> entities;
public Repository(StudentContext _context)
{
this.context = _context;
entities = _context.Set<T>();
}
public IQueryable<T> Table()
{
return this.Entities;
}
public void Insert (T entity)
{
this.Entities.Add(entity);
this.Save();
}
private IDbSet<T> Entities
{
get
{
if (entities == null)
entities = context.Set<T>();
return this.entities;
}
}
public void Save()
{
context.SaveChanges();
}
}
My Services Class is as:
public class StudentService: IStudentService
{
public IRepository<Student> _StudentRepository;
public StudentService(IRepository<Student> sturepo)
{
this._StudentRepository = sturepo;
}
public IQueryable<Student> GetAllStudents()
{
return _StudentRepository.Table();
}
public void InsertStudent(Student std)
{
_StudentRepository.Insert(std);
}
}
My API Controller:
public class StudentsController : ApiController
{
public IStudentService studentservice;
public StudentsController(IStudentService stuservce)
{
this.studentservice = stuservce;
}
public IList<Student> GetStudents()
{
List<Student> student = studentservice.GetAllStudents().ToList();
return student.ToList();
}
}}
Autofac Container Class:
public class AutofacConfig
{
public static void ConfigureContainer()
{
var builder = new ContainerBuilder();
// Get your HttpConfiguration.
var config = GlobalConfiguration.Configuration;
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => !t.IsAbstract && typeof(ApiController).IsAssignableFrom(t))
.InstancePerMatchingLifetimeScope();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
// OPTIONAL: Register the Autofac model binder provider.
builder.RegisterWebApiModelBinderProvider();
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
Calling Autofac in Global.Ascx:
protected void Application_Start()
{
AutofacConfig.ConfigureContainer();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}