0

Please I have a problem with Entity framework core in ASP.Net core. First of Iam an amateur in both EF Core and ASP.Net Core.

Idea:

Iam trying to create a generic repository to do some repeated routins without repeat the code with when I work with multiple contexts.

The Repo class:

public sealed class Repo<TContext> : IRepo<TContext>, IDisposable where TContext : DbContext, new()
{

    


    #region properties

    /// <summary>
    /// Private DBContext property
    /// </summary>
    private DbContext _Context { get; } = null;


    /// <summary>
    /// Determine if Lazy Loading either activate or not
    /// </summary>
    private bool _LazyLoaded { get; set; }

    #endregion




    #region Construcors


    public Repo(bool LazyLoaded)
    {
        _Context                                  = new TContext();
        _LazyLoaded                               = LazyLoaded;
        _Context.ChangeTracker.LazyLoadingEnabled = LazyLoaded;
    }


    #endregion



    
    #region Routines

    


    #region Select

        /// <summary>
        /// Get All records from a table
        /// </summary>
        /// <typeparam name="TEntity">The entity to select from</typeparam>
        /// <returns></returns>
        public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
        {
            return _Context.Set<TEntity>().ToList();
        }



        /// <summary>
        /// Asynchronously, Get All records from a table
        /// </summary>
        /// <typeparam name="TEntity">The entity to select from</typeparam>
        /// <returns></returns>
        public Task<IEnumerable<TEntity>> GetAllAsync<TEntity>() where TEntity : class
        {
            return Task.Factory.StartNew(() => GetAll<TEntity>());
        }




        /// <summary>
        /// Get One record from a table, based on the primary key value
        /// </summary>
        /// <typeparam name="TEntity">The entity to select from</typeparam>
        /// <param name="pkValue">The primary key value</param>
        /// <returns></returns>
        public TEntity GetOne<TEntity>(object pkValue) where TEntity : class
        {
            return _Context.Set<TEntity>().Find(pkValue);
        }



        /// <summary>
        /// Asynchronously, Get One record from a table, based on the primary key value
        /// </summary>
        /// <typeparam name="TEntity">The entity to select from</typeparam>
        /// <param name="pkValue">The primary key value</param>
        /// <returns></returns>
        public Task<TEntity> GetOneAsync<TEntity>(object pkValue) where TEntity : class
        {
            return Task.Factory.StartNew(() => GetOne<TEntity>(pkValue));
        }






        #region Preview feature


         /// <summary>
        /// Get Many records from a table based on a property value
        /// </summary>
        /// <typeparam name="TEntity">The entity to select from</typeparam>
        /// <param name="prop">The property used in the condition</param>
        /// <param name="val">The value that will used in the search</param>
        /// <returns></returns>
        public IEnumerable<TEntity> GetMany<TEntity>(string prop, object val) where TEntity : class
        {
            return _Context.Set<TEntity>().AsEnumerable()
                           .Where(x => typeof(TEntity).GetProperty(prop).GetValue(x, null).ToString()
                                                      .Contains(val.ToString())).ToList();
        }

    #endregion




    #endregion



    #region Contains

    /// <summary>
    /// Check if a entity contains an object
    /// </summary>
    /// <typeparam name="TEntity">Entity to be look in</typeparam>
    /// <param name="obj">The object to be looking for</param>
    /// <returns></returns>
    public bool Contains<TEntity>(TEntity obj) where TEntity : class
    {
        return _Context.Set<TEntity>().AsEnumerable().Contains(obj);
    }


    /// <summary>
    /// Asynchronously Check if a entity contains an object
    /// </summary>
    /// <typeparam name="TEntity">Entity to be look in</typeparam>
    /// <param name="obj">The object to be looking for</param>
    /// <returns></returns>
    public Task<bool> ContainsAsync<TEntity>(TEntity obj) where TEntity : class
    {
        return Task.Factory.StartNew(() => Contains<TEntity>(obj));
    }




    /// <summary>
    ///  Check if a entity contains an object based on a custom EQUALITY Comparer
    /// </summary>
    /// <typeparam name="TEntity">Entity to be look in</typeparam>
    /// <typeparam name="TEntityComparer">The custom TEntity EQUALITY Comparer</typeparam>
    /// <param name="obj">The object to be looking for</param>
    /// <returns></returns>
    public bool Contains<TEntity, TEntityComparer>(TEntity obj)
        where TEntity : class
        where TEntityComparer : IEqualityComparer<TEntity>, new()
    {
        return _Context.Set<TEntity>().AsEnumerable().Contains(obj,new TEntityComparer() as IEqualityComparer<TEntity>);
    }



    /// <summary>
    ///  Asynchronously Check if a entity contains an object based on a custom EQUALITY Comparer
    /// </summary>
    /// <typeparam name="TEntity">Entity to be look in</typeparam>
    /// <typeparam name="TEntityComparer">The custom TEntity EQUALITY Comparer</typeparam>
    /// <param name="obj">The object to be looking for</param>
    /// <returns></returns>
    public Task<bool> ContainsAsync<TEntity, TEntityComparer>(TEntity obj)
        where TEntity : class
        where TEntityComparer : IEqualityComparer<TEntity>, new()
    {
        return Task.Factory.StartNew(() => Contains<TEntity, TEntityComparer>(obj));
    }

    #endregion



    #region Insert



    /// <summary>
    /// Insert one record into the database table
    /// </summary>
    /// <typeparam name="TEntity">Entity to add into</typeparam>
    /// <param name="record">The record to be added</param>
    public void Insert<TEntity>(TEntity record) where TEntity : class
        {
            _Context.Set<TEntity>().Add(record);
        }



        /// <summary>
        /// Asynchronously, Insert one record into the database table
        /// </summary>
        /// <typeparam name="TEntity">Entity to add into</typeparam>
        /// <param name="record">The record to be added</param>
        public Task InsertAsync<TEntity>(TEntity record) where TEntity : class
        {
            return Task.Factory.StartNew(() => Insert(record));
        }



        /// <summary>
        /// Insert a range of reords in a table
        /// </summary>
        /// <typeparam name="TEntity">Entity to insert into</typeparam>
        /// <param name="records">Records to be inserted</param>
        public void InsertRange<TEntity>(List<TEntity> records) where TEntity : class
        {
            _Context.Set<TEntity>().AddRange(records);
        }



        /// <summary>
        /// Asynchronously, Insert a range of reords in a table
        /// </summary>
        /// <typeparam name="TEntity">Entity to insert into</typeparam>
        /// <param name="records">Records to be inserted</param>
        public Task InsertRangeAsync<TEntity>(List<TEntity> records) where TEntity : class
        {
            return Task.Factory.StartNew(() => InsertRange(records));
        }



        #endregion




    #region Delete

        /// <summary>
        /// Delete One record from a database table
        /// </summary>
        /// <typeparam name="TEntity">Entity to remove from</typeparam>
        /// <param name="record">The record to be removed</param>
        public void Delete<TEntity>(TEntity record) where TEntity : class
        {
            this._Context.Set<TEntity>().Remove(record);
        }



        /// <summary>
        /// Asynchronously, Delete One record from a database table
        /// </summary>
        /// <typeparam name="TEntity">Entity to remove from</typeparam>
        /// <param name="record">The record to be removed</param>
        public Task DeleteAsync<TEntity>(TEntity record) where TEntity : class
        {
            return Task.Factory.StartNew(() => Delete(record));
        }


        #endregion




        /// <summary>
        /// Save the repository changes
        /// </summary>
        public void Save()
        {
            _Context.SaveChanges();
        }


    #endregion




    #region Disposing


        #region Properties

        private bool _disposed { get; set; } = false;

        #endregion


        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _Context.Dispose();
                }
            }

            _disposed = true;
        }



        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }


    #endregion


}

In ASP.Net core:

Models

  public class Client
    {
        [Key]
        public string ID { get;                         set; }
        public         string             Name   { get; set; }
        public         int                Age    { get; set; }
        public virtual ICollection<Order> Orders { get; set; }
    }


public class Order
{
    [Key]
    public int ID { get;        set; }
    public Client  CLT   { get; set; }
    public string  PRD   { get; set; }
    public decimal Total { get; set; }
}

Code First DbContext:

 public class TrainContext:DbContext
    {
        public TrainContext(DbContextOptions<TrainContext> contextOptions) : base(contextOptions)
        {

        }

        public TrainContext()
        {
            
        }

        protected void OnModelCreating(ModelBuilder builder)
        {
            foreach (var property in builder.Model.GetEntityTypes().SelectMany(t => t.GetProperties()).Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)))
            {
                property.SetColumnType("decimal(18,2)");
            }
        }

        #region DB Sets

        public virtual DbSet<Client> Client { get; set; }
        public virtual  DbSet<Order>  Order  { get; set; }

        #endregion
    }

Client Controller:

 public class ClientController : Controller
    {
        private readonly Repo<TrainContext> db = new Repo<TrainContext>(false);

        private readonly TrainContext _context;

        public ClientController(TrainContext context)
        {
            _context = context;
        }
        public IActionResult Index()
        {



            //var data = _context.GetAll<Models.Client>();
            var data = _context.Client.ToList();

            ViewBag.Clients = data;

            return View();
        }
    }

Startup.cs ConfigureServices method:

  public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddDbContext<TrainContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DB")));

        }

AppSettings.Json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DB": "Server=ServerName; Database=GOrders;Integrated Security=True"
  }
}

The problem:

The problem is this repository is work fine when I pass a Db First Context in a Console application.

But when I try to use this repository in my Asp.Net core application with Code first approach I got this error:

InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.

Please any help to fix this issue ? and massive thanks in advance.

XDev
  • 125
  • 1
  • 8
  • your `DbContext` is not configured with a specific db provider. You can configure it by overriding the method `OnConfiguring` or use `AddDbContext` in `ConfigureServices` method in `Startup` class. The exception message clearly explains how you can solve the issue as well. I'm pretty sure that when googling for that error, you should have been able to find the solution yourself. – King King Feb 07 '21 at 17:41
  • Please can you provide me a solution as answer after your permission and massive thanks for your time sir – XDev Feb 07 '21 at 17:55

1 Answers1

0

As the error message said:

A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider.

Two ways you could meet your requirement.

First way,override the DbContext.OnConfiguring:

public class TrainContext : DbContext
{
    public TrainContext(DbContextOptions<TrainContext> contextOptions) : base(contextOptions)
    {

    }

    public TrainContext()
    {

    }

    protected void OnModelCreating(ModelBuilder builder)
    {

        foreach (var property in builder.Model.GetEntityTypes().SelectMany(t => t.GetProperties()).Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)))
        {
            property.SetColumnType("decimal(18,2)");
        }
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb; Database=GOrders;Integrated Security=true");
    }

    #region DB Sets

    public virtual DbSet<Client> Client { get; set; }
    public virtual DbSet<Order> Order { get; set; }

    #endregion
}

Second way by using AddDbContext,but before using AddDbContext,you need add some missing things:

Modify Program.cs:

using Microsoft.AspNetCore.Hosting;  //be sure add these references
using Microsoft.Extensions.Hosting;

class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
          webBuilder.UseStartup<Startup>();
      });
}

Then modify Sdk="Microsoft.NET.Sdk" to Sdk="Microsoft.NET.Sdk.Web" in your .csproj file as follows:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
  </PropertyGroup>

</Project>

Detailed reason you could check the following reference:

https://stackoverflow.com/a/58097655/11398810

Startup.cs(Be sure you have Configure method in Startup.cs):

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<TrainContext>(options => 
        options.UseSqlServer(Configuration.GetConnectionString("DB")));

    }
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }
        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

After modify like above,then you could run add-migraiton init and update-database command.

Rena
  • 30,832
  • 6
  • 37
  • 72