Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency injector doesn't resolve my repository (Debian and SqlLite) #1816

Closed
tango-charly opened this issue Nov 14, 2016 · 8 comments
Closed

Comments

@tango-charly
Copy link

Hi !

I got a probleme with Net.Core 1.1 dependency injector.

In my project (under Debian) i got a simple architecture:

I have a Generic Base Controller

BaseController.cs

namespace QuestionBank.Controllers
{
    [Route("api/[controller]")]
    public class BaseController<TEntity> : Controller
        where TEntity :  class, IEntity, new()
    {
        protected IRepository<TEntity> Repository {get;set;}

        public BaseController(IRepository<TEntity> repo)
        {
            this.Repository = repo;
        }

        // GET api/{Controller}
        [HttpGet]
        public virtual IEnumerable<string> Get()
        {
            return new string[] { "Entity 1", "Entity 2" };
        }
    }
} 

And One Controller (the method Get() is the one called when error occurred)

ThemeController.cs

namespace QuestionBank.Controllers
{
    [Route("api/[controller]")]
    public class ThemesController : BaseController<Theme>
    {
        public ThemesController(IRepository<Theme> repo) : base(repo)
        {
        }

        [HttpGet]
        public override IEnumerable<string> Get()
        {
            var t = this.Repository;
            var r = this.Repository.GetAll().ToList();
            return new string[] { "Theme 3", "Theme 4" };
        }
    }
}

I use SqlLite and i defined my context like this :

ApiContext.cs

namespace QuestionBank.Models
{
    public class ApiContext : DbContext
    {
        #region Entities Definition
        public DbSet<Theme> Themes { get; set; }
        #endregion

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Filename=./QuestionBank.db");
        }
    }
}

And finally my Startup.cs

namespace QuestionBank
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
            using(var client = new ApiContext())
            {
                client.Database.EnsureCreated();
            }
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();
            services.AddEntityFrameworkSqlite().AddDbContext<ApiContext>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseMvc();
        }
    }
}

My Repository is not resolved.
I got this error when i call the URL ---> /api/themes

fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HL0C57CDRPRT": An unhandled exception was thrown by the application.
System.InvalidOperationException: Unable to resolve service for type 'QuestionBank.Models.Repositories.Interfaces.IRepository`1[QuestionBank.Models.Theme]' while attempting to activate 'QuestionBank.Controllers.ThemesController'.

@khellang
Copy link
Member

How would you expect the repository to be injected when you haven't configured it anywhere?

@tango-charly
Copy link
Author

Sure ! Sry, i forgot this line in my startup.cs :
services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
I still got the problem, i'm testing Core.Net for the first time.
Did i missed something ?

@davidfowl
Copy link
Member

Can you update the example with your overall code (add the missing line). Also where is the definition for BaseRepository

@tango-charly
Copy link
Author

tango-charly commented Nov 14, 2016

Yes,

Here my BaseRepository

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using QuestionBank.Models.Entities.Abstracts;
using QuestionBank.Models.Repositories.Interfaces;

namespace QuestionBank.Models.Repositories
{
    public class BaseRepository<TEntity> :  
        IRepository<TEntity>, IDisposable
        where TEntity : class, IEntity, new()
    {
        protected volatile DbContext context;

        #region Constructors
        public BaseRepository(DbContext context) : base()
        {
            this.context = context;
        }
        #endregion

        protected virtual DbSet<TEntity> Data => context.Set<TEntity>(); 

        public IEnumerable<TEntity> GetAll()
        {
            return this.Data.ToList();
        }

        public IQueryable<TEntity> Query()
        {
            return this.Data;
        }

        public IQueryable<TEntity> GetBy(Expression<Func<TEntity, bool>> filter)
        {
            throw new NotImplementedException();
        }

        public TEntity GetById(int Id)
        {
            return this.Data.SingleOrDefault(x => x.Id == Id);
        }

        public void Insert(TEntity Model)
        {
            this.Data.Add(Model);
        }

        public void Update(TEntity Model)
        {
            this.Data.Attach(Model);
            this.context.Entry(Model).State = EntityState.Modified;
        }

        public void Delete(TEntity Model)
        {
            this.Data.Remove(Model);
        }

        public void Save()
        {
            context.SaveChanges();
        }

        #region IDisposable implementation
        private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        }

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

(My BaseController methods are under construction)

And my Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using QuestionBank.Models;
using QuestionBank.Models.Repositories;
using QuestionBank.Models.Repositories.Interfaces;

namespace QuestionBank
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
            using(var client = new ApiContext())
            {
                client.Database.EnsureCreated();
            }
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();
            services.AddEntityFrameworkSqlite().AddDbContext<ApiContext>();
            services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            app.UseMvc();
        }
    }
}

Maybe it would be easier if i create a public repostiory to share you my code ?

@khellang
Copy link
Member

I think you need to inject ApiContext, not DbContext.

@khellang
Copy link
Member

Also, that can't possibly be all of the exception? If that's the case, then the built-in container has some serious diagnostic problems.

@khellang
Copy link
Member

You said that you forgot the line in Startup, but surely if that line was present, the exception should've been different?

@tango-charly
Copy link
Author

Ah ! Thank's ! You found my mistake ! I injected DbContext, not ApiContext...
It works nice !
Thank's a lot ! 💃

PS : I tried to watch what my container contained, but I am not all that familiar yet with debugging under Debian

natemcmaster pushed a commit that referenced this issue Nov 14, 2018
* Refactoring and of FrameConnection and Frame
- Building on top of the last refactoring of FrameConnection, this change aims to clean up
the communication between the Frame and FrameConnection by removing some concepts and
being consistent about the communication between Frame and FrameConnection with or without
connection adapters. Changes include:
- Removing ConnectionLifetimeControl, ISocketOutput, StreamSocketOutput
- Moving more initialization of the frame to FrameConnection after the pipes
are setup
- OutputProducer communicates cancellation via the IPipeWriter instead of the output's IPipeReader.
- Frame always communicates via the pipes and that communications flows through the layers to the transport.
This means that each 1/2 of the adapted pipeline handles closing the right side of the transport at the
right time, propagating exceptions as necessary.
- This is how the flow looks now:
            ->                        ->
[transport]     [connection adapters]     [frame]
            <-                        <-
- Transports need to handle a ConnectionAbortedException on the output as a signal to stop
writing and end the connection. This will no longer try to drain the output but will just stop
writing and end the response immediately.
- Remove frame.Abort when cancellation on Write fails.
- Unify the connection shutdown logic
- Dispose 1/2 initialized connection adapters

#1815
ryanbrandenburg pushed a commit that referenced this issue Nov 27, 2018
- Renamed StreamPipeConnection to PipeReaderFactory
- Flow the transport cancellation token to the CopyToAsync routine 
- Other small cleanup and nits to make the style consistent with the other pipe reader loops
- Return a cancelled ValueTask from PipeWriterStream.WriteAsync
- Move event stream request to start itself
- We no longer need to pass the tcs through.
- It also cleans up handling failure in start since the application pipe hasn't been read or written to
@ghost ghost locked as resolved and limited conversation to collaborators Dec 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants