locked
Replace Factory with Autofac Module for Unit of Work RRS feed

  • Question

  • User-108026213 posted

    Summary of the problem I am having:

    I am fairly new to coding and I am in the process of learning dependency injection using Autofac.  With past projects (not using Autofac), I have created a Factory to determine which objects get returned based on the value of the RepositoryType key set in the Web.config file.  So for example, if  the RepositoryType value is set to ADO, then I would want it (the factory) to return the Unit Of Work for ADO and register all the repositories, interfaces, unit of work, context in my DataAccessModule.  If the RepositoryType value is changed to DAPPER, then only my repositories, interfaces, unit of work, context would be registered and returned for DAPPER.  I'm just stuck on how to wire everything up via Autofac without implementing a Factory class for my unit of work and context classes. 

    I also don't know how to use a ConfigurationProvider class (if I wanted to access the RepostioryType property) from my ConfigurationProviderModule in my DataAccessModule.  Are we allowed to resolve in another Module or is that frowned upon?

    Any help or guidance you can provide on this topic would be greatly appreciated

    My code:

    <appSettings>
        <add key="RepositoryType" value="ADO" />
    </appSettings>

    IConfigurationProvider

    public interface IConfigurationProvider
        {
            string RepositoryType { get; }
    
            IDbConnection CreateConnection();
        }

    ConfigurationProvider

    This class will hold the configuration information.  It has a CreateConnection method to open a connection - this gets called from my DvdContextADO

    public class ConfigurationProvider : IConfigurationProvider
        {
            private readonly DbProviderFactory _provider;
            private readonly string _connectionName;
            private readonly string _connectionString;
            private readonly string _providerName;
            public string RepositoryType { get; private set; }
    
            public ConfigurationProvider(string connectionName, string connectionString, string providerName, string repositoryType)
            {
                _connectionName = connectionName ?? throw new ArgumentNullException("connectionName");
                _connectionString = connectionString ?? throw new ArgumentNullException("connectionString");
                _providerName = providerName ?? throw new ArgumentNullException("providerName");
                RepositoryType = repositoryType ?? throw new ArgumentNullException("repositoryType");
                _provider = DbProviderFactories.GetFactory(_providerName);
            }
    
            public IDbConnection CreateConnection()
            {
                var connection = _provider.CreateConnection();
    
                if (connection == null) throw new ConfigurationErrorsException($"Failed to create a connection using the connection string named '{_connectionName}' in web.config.");
    
                connection.ConnectionString = _connectionString;
    
                connection.Open();
    
                return connection;
            }
        }

    ConfigurationModule

    This module is responsible for registering the ConfigurationProvider class.  It reads the connection string information within Web.config

    public class ConfigurationModule : Module
        {
            public string ConnectionName { get; set; }
            public string AppSettingKey { get; set; }
    
            protected override void Load(ContainerBuilder builder)
            {
                var connection = ConfigurationManager.ConnectionStrings[ConnectionName];
    
                if (connection == null) throw new ConfigurationErrorsException($"Failed to find connection string named '{ConnectionName}' in web.config.");
    
                builder.Register(c => new ConfigurationProvider(
    
                    connectionName: ConfigurationManager.ConnectionStrings[ConnectionName].Name,
                    connectionString: ConfigurationManager.ConnectionStrings[ConnectionName].ConnectionString,
                    providerName: ConfigurationManager.ConnectionStrings[ConnectionName].ProviderName,
                    repositoryType: ConfigurationManager.AppSettings[AppSettingKey]
    
                    ))
                    .As<IConfigurationProvider>()
                    .SingleInstance();
    
                base.Load(builder);
            }
            
        }

    DvdContextADO

    Uses the ConfigurationProvider class

    public class DvdContextADO
        {
            private readonly IDbConnection _connection;
    
            public DvdContextADO(IConfigurationProvider configurationProvider)
            {
                _connection = configurationProvider.CreateConnection();
                Transaction = _connection.BeginTransaction();
            }
    
            public IDbTransaction Transaction { get; private set; }
    
            public IDbCommand CreateCommand()
            {
                var cmd = _connection.CreateCommand();
                
                cmd.Transaction = Transaction;
    
                return cmd;
            }
        }

    UnitOfWorkADO

    Unit of Work specific to ADO

    public class UnitOfWorkADO : IUnitOfWork
        {
            private readonly DvdContextADO _context;
            private IDbTransaction _transaction;
    
            private IDvdRepository _dvd;
            private IDvdDirectorRepository _dvdDirector;
            private IRatingRepository _rating;
            private IDirectorRepository _director;
    
            public UnitOfWorkADO(DvdContextADO context)
            {
                _context = context;
                _transaction = context.Transaction;
            }
    
            public IDirectorRepository Director
            {
                get
                {
                    return _director ?? (_director = new DirectorRepositoryADO(_context));
                }
            }
    
            public IDvdDirectorRepository DvdDirector
            {
                get
                {
                    return _dvdDirector ?? (_dvdDirector = new DvdDirectorRepositoryADO(_context));
                }
            }
    
            public IDvdRepository Dvd
            {
                get
                {
                    return _dvd ?? (_dvd = new DvdRepositoryADO(_context));
                }
            }
    
            public IRatingRepository Rating
            {
                get
                {
                    return _rating ?? (_rating = new RatingRepositoryADO(_context));
                }
            }
    
            public void Complete()
            {
                try
                {
                    if (_transaction == null)
                    {
                        return;
                    }
                    else
                    {
                        _transaction.Commit();
                        _transaction = null;
                    }
    
                }
                catch (Exception ex)
                {
                    _transaction.Rollback();
                    throw ex;
                }
            }
    
            public void Dispose()
            {
                if (_transaction != null)
                {
                    _transaction.Dispose();
                    _transaction = null;
                }
            }
        }

    DataAccessModule

    This is where I am stuck.  As you can see by the mess below, I am a bit lost, to say the least.  Any help you could provide would be greatly appreciated.

    public class DataAccessModule : Module
        {
            private readonly IConfigurationProvider _configurationProvider;
    
            public string AppSettingsKey { get; set; }
    
            public DataAccessModule(IConfigurationProvider configurationProvider)
            {
                _configurationProvider = configurationProvider;
            }
    
            protected override void Load(ContainerBuilder builder)
            {
                var context = new DvdContextADO(_configurationProvider);
    
                builder.Register(c => new UnitOfWorkADO(context)).
                                 As<IUnitOfWork>().InstancePerLifetimeScope();
    
    
                
                //var repositoryType = ConfigurationManager.AppSettings["RepositoryType"];
    
                //switch (repositoryType)
                //{
                //    case "ADO":
                //        builder.RegisterType<DvdContextADO>().AsSelf();
                //        builder.RegisterType<UnitOfWorkADO>().As<IUnitOfWork>().InstancePerLifetimeScope();
                //        builder.RegisterType<DirectorRepositoryADO>().As<IDirectorRepository>().InstancePerLifetimeScope();
                //        builder.RegisterType<DvdDirectorRepositoryADO>().As<IDvdDirectorRepository>().InstancePerLifetimeScope();
                //        builder.RegisterType<DvdRepositoryADO>().As<IDvdRepository>().InstancePerLifetimeScope();
                //        builder.RegisterType<RatingRepositoryADO>().As<IRatingRepository>().InstancePerLifetimeScope();
                //        break;
                //    default:
                //        throw new Exception("Could not find valid RepositoryType configuration value");
                //}
    
    
            }
        }

    Sunday, January 20, 2019 4:01 AM

All replies

  • User-108026213 posted

    Okay, I think I figured out what I needed to do.  I did some more research and found a really good post which pointed me in the right direction:  https://benedict-chan.github.io/blog/2014/08/13/resolving-implementations-at-runtime-in-autofac/

    UnitOfWorkADO

    This class changed to using Property Injection via Autofac

    public class UnitOfWorkADO : IUnitOfWork
        {
            private readonly DvdContextADO _context;
            private IDbTransaction _transaction;
    
            public UnitOfWorkADO(DvdContextADO context)
            {
                _context = context;
                _transaction = context.Transaction;
            }
    
            public IDirectorRepository Director { get; set; }
    
            public IDvdDirectorRepository DvdDirector { get; set; }
    
            public IDvdRepository Dvd { get; set; }
    
            public IRatingRepository Rating { get; set; }
    
    
            public void Complete()
            {
                try
                {
                    if (_transaction == null)
                    {
                        return;
                    }
                    else
                    {
                        _transaction.Commit();
                        _transaction = null;
                    }
    
                }
                catch (Exception ex)
                {
                    _transaction.Rollback();
                    throw ex;
                }
            }
    
            public void Dispose()
            {
                if (_transaction != null)
                {
                    _transaction.Dispose();
                    _transaction = null;
                }
            }
        }

    DataAccessModuleADO

    Updated to be specifically for ADO by using Keyed Registration in Autofac

    public class DataAccessModuleADO : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                var keyedRepositoryType = "ADO";
    
                builder.RegisterType<UnitOfWorkADO>()
                       .As<IUnitOfWork>()
                       .PropertiesAutowired()
                       .InstancePerLifetimeScope()
                       .Keyed<IUnitOfWork>(keyedRepositoryType);
    
                builder.RegisterType<DvdContextADO>()
                       .AsSelf()
                       .InstancePerLifetimeScope()
                       .Keyed<DvdContextADO>(keyedRepositoryType);
    
                builder.RegisterType<RatingRepositoryADO>()
                       .As<IRatingRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IRatingRepository>(keyedRepositoryType);
    
                builder.RegisterType<DirectorRepositoryADO>()
                       .As<IDirectorRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDirectorRepository>(keyedRepositoryType);
    
                builder.RegisterType<DvdRepositoryADO>()
                       .As<IDvdRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDvdRepository>(keyedRepositoryType);
    
                builder.RegisterType<DvdDirectorRepositoryADO>()
                       .As<IDvdDirectorRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDvdDirectorRepository>(keyedRepositoryType);
            }
        }

    UnitOfWorkFactory

    Added this class which returns the correct UnitOfWork based on the RepositoryType

    public class UnitOfWorkFactory : IUnitOfWorkFactory
        {
            private readonly Func<string, IUnitOfWork> _unitOfWork;
            private readonly string _repositoryType;
    
            public UnitOfWorkFactory(Func<string, IUnitOfWork> unitOfWork, IConfigurationProvider configurationProvider)
            {
                this._unitOfWork = unitOfWork;
                this._repositoryType = configurationProvider.RepositoryType;
            }
    
            public IUnitOfWork Create()
            {
                var uow = this._unitOfWork(_repositoryType);
                return uow;
            }
        }

    AutofacConfig

    public class AutofacConfig
        {
            public static void RegisterComponents()
            {        
                var builder = new ContainerBuilder();
    
                builder.RegisterType<HomeController>().InstancePerLifetimeScope();
    
                builder.RegisterModule(new ConfigurationModule {
                  ConnectionName = "DefaultConnection",
                  AppSettingKey = "RepositoryType"
                });
    
                builder.RegisterModule(new DataAccessModuleADO());
    
                builder.RegisterType<UnitOfWorkFactory>().As<IUnitOfWorkFactory>().InstancePerLifetimeScope();
                
    
                var container = builder.Build();
                
                DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    
            }
        }

    I think I am on the right track now

    If you could review what I have done above in regards to wiring up via Autofac and provide any feedback, I would greatly appreciate it.  Also, did I select the correct scope for UnitOfWork, context, and repositories?  I chose InstancePerLifetimeScope() based on what I read online

    Monday, January 21, 2019 8:47 PM
  • User1520731567 posted

    Hi IndyCode,

    If you could review what I have done above in regards to wiring up via Autofac and provide any feedback, I would greatly appreciate it.  Also, did I select the correct scope for UnitOfWork, context, and repositories?  I chose InstancePerLifetimeScope() based on what I read online

    Choosing the right lifetime scope will help you avoid captive dependencies and other pitfalls where a component lives too long or not long enough. 

    This scope (InstancePerLifetimeScope) applies to nested lifetimes. A component with per-lifetime scope will have at most a single instance per nested lifetime scope.

    This is useful for objects specific to a single unit of work that may need to nest additional logical units of work. Each nested lifetime scope will get a new instance of the registered dependency.

    More details,you could refer to this article:

    https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html

    Best Regards.

    Yuki Tao

    Tuesday, January 22, 2019 6:48 AM
  • User1120430333 posted

    I am fairly new to coding and I am in the process of learning dependency injection using Autofac. With past projects (not using Autofac), I have created a Factory to determine which objects get returned based on the value of the RepositoryType key set in the Web.config file. So for example, if the RepositoryType value is set to ADO, then I would want it (the factory) to return the Unit Of Work for ADO and register all the repositories, interfaces, unit of work, context in my DataAccessModule. If the RepositoryType value is changed to DAPPER, then only my repositories, interfaces, unit of work, context would be registered and returned for DAPPER. I'm just stuck on how to wire everything up via Autofac without implementing a Factory class for my unit of work and context classes.

    Why are you concerned as to what the underlying database technology is that is being used? For whatever reason a path must be chosen, then  it should be a hard path and not a dynamic path, a hard code seperation. But I don't get this unless you are playing.

    What does the client care what the underlying database technology is about? Why is it on one hand, Dapper is being used, and on the other hand, some other DB technology is being used? Are you playing here?

    In using a factory pattern, a creation pattern that doesn't expose object creation,  is based on a common Interface for all objects created. What is the common Interface? 

    What is this DataAccessModel? At best, the DataAccessModel is at the lowest level and its objects should be working with the database technology.

    Do you understand  architectural styles?

    https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ee658117(v=pandp.10)

     Do you understand the repositroy pattern?

    https://martinfowler.com/eaaCatalog/repository.html

    What shouldn't be used in a repository object. 

    https://blog.sapiensworks.com/post/2012/03/05/The-Generic-Repository-Is-An-Anti-Pattern.aspx

    I have nothing against the repository pattern, but I never used a generic repository when I have used or seen  the repository pattern in a solution.

    Tuesday, January 22, 2019 10:11 AM
  • User-108026213 posted

    Yuki Tao,

    Thank you for your response.  I'll review those and see if I can figure out if I have the correct scope.

    Tuesday, January 22, 2019 6:49 PM
  • User-108026213 posted

    DA924,

    Thank you for your response.

    • Why are you concerned as to what the underlying database technology is that is being used? For whatever reason a path must be chosen, then  it should be a hard path and not a dynamic path, a hard code seperation. But I don't get this unless you are playing.
      • This strictly for playing purposes.  In my coding class, we learned about the Factory pattern and how it can create objects based on input.  We would do assignments using the Repository Pattern.  We would have to create three types or repositories.  One using ADO.NET, one using Dapper, and one using Mock (in-memory).  This way we got practice in each.  Then we implemented the Factory pattern and would read the AppSetting RepositoryType and the factory would create and return the specific object(s) we would need based on that setting.  I'm trying to learn Autofac and implement the Unit of Work using ADO.NET to start.  I can tell from my searches on ADO.NET and Unit of Work that not a whole lot of people use it.  There is also a huge debate over EF and whether it already implements Repository and Unit of Work pattern.
    • What does the client care what the underlying database technology is about? Why is it on one hand, Dapper is being used, and on the other hand, some other DB technology is being used? Are you playing here?
      • Yeah just playing.  See answer above
    • What is this DataAccessModel? At best, the DataAccessModel is at the lowest level and its objects should be working with the database technology.
      • I meant DataAccessModuleADO not Model.  Autofac allows you to use Modules
      • A module is a small class that can be used to bundle up a set of related components behind a ‘facade’ to simplify configuration and deployment. The module exposes a deliberate, restricted set of configuration parameters that can vary independently of the components used to implement the module.
        
        The components within a module still make use dependencies at the component/service level to access components from other modules.
        
        Modules do not, themselves, go through dependency injection. They are used to configure the container, they are not actually registered and resolved like other components. If your module takes a constructor parameter, for example, you need to pass that in yourself. It won’t come from the container.
      • Do you understand  architectural styles?
        • We learned about N-Tier architecture.  I know there is quite a bit more and have been reading up on it.  I will be adding a Service layer into this which is new for me
      • Do you understand the repositroy pattern?
        • I do.  I didn't post my repository here but I feel really comfortable with that pattern.  I see a lot of people using generics which I have never used.  I have also seen some say that Repository Pattern should not be used.  I'm not expert enough to say one way or the other
        • public class RatingRepositoryADO : IRatingRepository
              {
                  private readonly DvdContextADO _context;
          
                  public RatingRepositoryADO(DvdContextADO context)
                  {
                      _context = context;
                  }
          
                  public IEnumerable<Rating> GetAll()
                  {
                      List<Rating> ratings = new List<Rating>();
          
                      using (var cmd = (SqlCommand)_context.CreateCommand())
                      {
                          cmd.CommandType = CommandType.StoredProcedure;
                          cmd.CommandText = "RatingSelectAll";
          
                          using (var dr = cmd.ExecuteReader())
                          {
                              while (dr.Read())
                              {
                                  Rating currentRow = new Rating
                                  {
                                      RatingId = (int)dr["RatingId"],
                                      RatingName = dr["RatingName"].ToString()
                                  };
          
                                  ratings.Add(currentRow);
                              }
                          }
                      }
          
                      return ratings;
                  }
          
              }

    Tuesday, January 22, 2019 7:10 PM
  • User1120430333 posted

    Your instructor should give you real world usage assignments and not some fantasy usage assignment that will never happen.  

    You should understand the main purpose of using an IoC like Autofac and other Inversion of Control frameworks and Dependency Injection

    https://ardalis.com/new-is-glue

    <copied>

    Decoupling Services from Providers
    New is Glue. It binds your code to a particular collaborator.
    If there is any chance you’ll need to be flexible about which implementation your code will need, it’s worth introducing an interface to keep your code loosely coupled.
    It doesn’t matter what the service is you need – you can always replace it with an interface even if your class is the only one that uses it.

    <end>

    https://msdn.microsoft.com/en-us/magazine/mt703433.aspx?f=255&MSPPError=-2147217396

    <copied>

    When looking at code to evaluate its coupling, remember the phrase “new is glue.”
    That is, anywhere you see the “new” keyword instantiating a class, realize you’re gluing your implementation to that specific implementation code.
    The Dependency Inversion Principle (bit.ly/DI-Principle) states: “Abstractions should not depend on details; details should depend on abstractions.” In this example, the details of how the controller pulls together the data to pass to the
    view depend on the details of how to get that data—namely, EF.

    <end>

    https://www.c-sharpcorner.com/blogs/understanding-interfaces-via-loose-coupling-and-tight-coupling

    https://dzone.com/articles/absolute-beginners-tutorial

    https://dzone.com/articles/absolute-beginners-tutorial

    I meant DataAccessModuleADO not Model. Autofac allows you to use Modules

    An IoC framework  shouldn't be controlling architectural style. The style should be usable without the IoC being there. The usage of the IoC could change from one IoC like over to Unity. and then you are in a bind, becuase you Autofac the hell out of everything. An architecture should be scalable and flexible. 

    I have also seen some say that Repository Pattern should not be used. I'm not expert enough to say one way or the other

    If you understand the repository pattern is a Domain/Business pattern and not a persistence pattern, which calls upon a mapping/persistence layer, then you're OK. If you doiung anything else with the repository pattern not using it in a domain manner, then you don't understand it. 

    Tuesday, January 22, 2019 9:05 PM
  • User-108026213 posted

    Just wanted to post an update in case other newbies stumble across this post.  Made some updates.  Not sure this is the best way but it feels a lot better then when I started.  Again this post really helped me figure out how to implement a factory using Autofac:  https://benedict-chan.github.io/blog/2014/08/13/resolving-implementations-at-runtime-in-autofac/

    Autofac.config

    public class AutofacConfig
        {
            public static void RegisterComponents()
            {        
                var builder = new ContainerBuilder();
    
                builder.RegisterType<HomeController>().InstancePerLifetimeScope();
    
                builder.RegisterModule(new ConfigurationModule {
                  ConnectionName = "DefaultConnection",
                  AppSettingKey = "RepositoryType"
                });
    
                builder.RegisterModule(new UnitOfWorkModule());
                builder.RegisterModule(new RepositoryFactoryModule());
                builder.RegisterModule(new RepositoryModuleADO());
                builder.RegisterModule(new RepositoryModuleDapper());
    
                var container = builder.Build();
                
                DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    
            }
        }

    Autofac Modules

    public class ConfigurationModule : Module
        {
            public string ConnectionName { get; set; }
            public string AppSettingKey { get; set; }
    
            protected override void Load(ContainerBuilder builder)
            {
                var connection = ConfigurationManager.ConnectionStrings[ConnectionName];
    
                if (connection == null) throw new ConfigurationErrorsException($"Failed to find connection string named '{ConnectionName}' in web.config.");
    
                builder.Register(c => new ConfigurationProvider(
    
                    connectionName: ConfigurationManager.ConnectionStrings[ConnectionName].Name,
                    connectionString: ConfigurationManager.ConnectionStrings[ConnectionName].ConnectionString,
                    providerName: ConfigurationManager.ConnectionStrings[ConnectionName].ProviderName,
                    repositoryType: ConfigurationManager.AppSettings[AppSettingKey]
    
                    ))
                    .As<IConfigurationProvider>()
                    .SingleInstance();
    
                base.Load(builder);
            }
            
        }
    public class RepositoryModuleADO : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                var keyedRepositoryType = "ADO";
    
                builder.RegisterType<RatingRepositoryADO>()
                       .As<IRatingRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IRatingRepository>(keyedRepositoryType);
    
                builder.RegisterType<DirectorRepositoryADO>()
                       .As<IDirectorRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDirectorRepository>(keyedRepositoryType);
    
                builder.RegisterType<DvdRepositoryADO>()
                       .As<IDvdRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDvdRepository>(keyedRepositoryType);
               
                builder.RegisterType<DvdDirectorRepositoryADO>()
                       .As<IDvdDirectorRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDvdDirectorRepository>(keyedRepositoryType);
    
                base.Load(builder);
            }
        }
    public class RepositoryModuleDapper : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                var keyedRepositoryType = "Dapper";
    
                builder.RegisterType<RatingRepositoryDapper>()
                       .As<IRatingRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IRatingRepository>(keyedRepositoryType); ;
    
                builder.RegisterType<DirectorRepositoryDapper>()
                       .As<IDirectorRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDirectorRepository>(keyedRepositoryType);
    
                builder.RegisterType<DvdRepositoryDapper>()
                       .As<IDvdRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDvdRepository>(keyedRepositoryType);
    
                builder.RegisterType<DvdDirectorRepositoryDapper>()
                       .As<IDvdDirectorRepository>()
                       .InstancePerLifetimeScope()
                       .Keyed<IDvdDirectorRepository>(keyedRepositoryType);
    
                base.Load(builder);
            }
    
        }
     public class UnitOfWorkModule : Module
        {
            protected override void Load(ContainerBuilder builder)
            {
                builder.RegisterType<UnitOfWork>()
                       .As<IUnitOfWork>()
                       .InstancePerLifetimeScope();
                
                builder.RegisterType<DvdContext>()
                       .AsSelf()
                       .InstancePerLifetimeScope();
    
                base.Load(builder);
            }
        }
    public class RepositoryFactoryModule : Module
        {
    
            protected override void Load(ContainerBuilder builder)
            {
    
                builder.RegisterType<RatingRepositoryFactory>().As<IRatingRepositoryFactory>().InstancePerLifetimeScope();
                builder.RegisterType<DirectorRepositoryFactory>().As<IDirectorRepositoryFactory>().InstancePerLifetimeScope();
                builder.RegisterType<DvdRepositoryFactory>().As<IDvdRepositoryFactory>().InstancePerLifetimeScope();
                builder.RegisterType<DvdDirectorRepositoryFactory>().As<IDvdDirectorRepositoryFactory>().InstancePerLifetimeScope();
    
                builder.Register<Func<string, IRatingRepository>>(c =>
                {
                    var componentContext = c.Resolve<IComponentContext>();
                    return (repositoryType) =>
                    {
                        var ratingRepository = componentContext.ResolveKeyed<IRatingRepository>(repositoryType);
                        return ratingRepository;
                    };
                });
    
                builder.Register<Func<string, IDirectorRepository>>(c =>
                {
                    var componentContext = c.Resolve<IComponentContext>();
                    return (repositoryType) =>
                    {
                        var directorRepository = componentContext.ResolveKeyed<IDirectorRepository>(repositoryType);
                        return directorRepository;
                    };
                });
    
                builder.Register<Func<string, IDvdRepository>>(c =>
                {
                    var componentContext = c.Resolve<IComponentContext>();
                    return (repositoryType) =>
                    {
                        var dvdRepository = componentContext.ResolveKeyed<IDvdRepository>(repositoryType);
                        return dvdRepository;
                    };
                });
    
                builder.Register<Func<string, IDvdDirectorRepository>>(c =>
                {
                    var componentContext = c.Resolve<IComponentContext>();
                    return (repositoryType) =>
                    {
                        var dvdDirectorRepository = componentContext.ResolveKeyed<IDvdDirectorRepository>(repositoryType);
                        return dvdDirectorRepository;
                    };
                });
    
    
                base.Load(builder);
            }
        }

    Rating Repository for Dapper and one using ADO

    public class RatingRepositoryADO : IRatingRepository
        {
            private readonly DvdContext _context;
    
            public RatingRepositoryADO(DvdContext context)
            {
                this._context = context;
            }
    
            public IEnumerable<Rating> GetAll()
            {
                List<Rating> ratings = new List<Rating>();
    
                using (var cmd = (SqlCommand)_context.CreateCommand())
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.CommandText = "RatingSelectAll";
    
                    using (var dr = cmd.ExecuteReader())
                    {
                        while (dr.Read())
                        {
                            Rating currentRow = new Rating
                            {
                                RatingId = (int)dr["RatingId"],
                                RatingName = dr["RatingName"].ToString()
                            };
    
                            ratings.Add(currentRow);
                        }
                    }
                }
    
                return ratings;
            }
    
        }
    public class RatingRepositoryDapper : IRatingRepository
        {
            private readonly DvdContext _context;
            private readonly IDbConnection _cn;
    
            public RatingRepositoryDapper(DvdContext context)
            {
                this._context = context;
                this._cn = context.Transaction.Connection;
            }
    
            public IEnumerable<Rating> GetAll()
            {
                return _cn.Query<Rating>("RatingSelectAll", commandType: CommandType.StoredProcedure, transaction: _context.Transaction);
            }
        }

    Repository Factories - Need one for each Repository (Just showing the Rating one)

    public class RatingRepositoryFactory : IRatingRepositoryFactory
        {
            private readonly Func<string, IRatingRepository> _ratingRepository;
            private readonly string _repositoryType;
    
            public RatingRepositoryFactory(Func<string, IRatingRepository> ratingRepository, IConfigurationProvider configurationProvider)
            {
                this._ratingRepository = ratingRepository;
                this._repositoryType = configurationProvider.RepositoryType;
            }
    
            public IRatingRepository Create()
            {
                var ratingRepository = this._ratingRepository(_repositoryType);
                return ratingRepository;
            }
        }

    Unit of Work

    public class UnitOfWork : IUnitOfWork
        {
            private readonly DvdContext _context;
            private IDbTransaction _transaction; 
    
            public UnitOfWork(DvdContext context, IRatingRepositoryFactory ratingRepository, IDirectorRepositoryFactory directorRepository, IDvdRepositoryFactory dvdRepository, IDvdDirectorRepositoryFactory dvdDirectorRepository)
            {
                _context = context;
                _transaction = context.Transaction;
    
                Rating = ratingRepository.Create();
                Director = directorRepository.Create();
                Dvd = dvdRepository.Create();
                DvdDirector = dvdDirectorRepository.Create();
            }
    
            public IDirectorRepository Director { get; private set; }
    
            public IDvdDirectorRepository DvdDirector { get; private set; }
    
            public IDvdRepository Dvd { get; private set; }
    
            public IRatingRepository Rating { get; private set; }
    
    
            public void Complete()
            {
                try
                {
                    if (_transaction == null)
                    {
                        return;
                    }
                    else
                    {
                        _transaction.Commit();
                        _transaction = null;
                    }
    
                }
                catch (Exception ex)
                {
                    _transaction.Rollback();
                    throw ex;
                }
            }
    
            public void Dispose()
            {
                if (_transaction != null)
                {
                    _transaction.Dispose();
                    _transaction = null;
                }
            }
        }

    DvdContext

    public class DvdContext
        {
            private readonly IDbConnection _connection;
    
            public DvdContext(IConfigurationProvider configurationProvider)
            {
                _connection = configurationProvider.CreateConnection();
                Transaction = _connection.BeginTransaction();
            }
    
            public IDbTransaction Transaction { get; private set; }
    
            public IDbCommand CreateCommand()
            {
                var cmd = _connection.CreateCommand();
                
                cmd.Transaction = Transaction;
    
                return cmd;
            }
        }

    Saturday, January 26, 2019 12:39 AM
  • User1120430333 posted

    The below code is not a repository object. It is just a Data Access Object doing low level database activity, a low level mapping solution, which the a repository object should be calling upon from the Domain.

    https://blog.sapiensworks.com/post/2012/11/01/Repository-vs-DAO.aspx

    http://www.bradoncode.com/blog/2013/08/repository-vs-domain-model-vs-data.html

    https://pehapkari.cz/blog/2018/02/28/domain-driven-design-repository/

    The author of Domain Driven Design and the repository pattern.

    https://martinfowler.com/eaaCatalog/repository.html

    To be honest and not trying to be smart here, but what you have architected here the whole enchilada would never make it out of a professional code review, and you would be told to go back and come up with something that is maintainable, scalable and with things in their proper places.

    Just becuase one can, it doesn't mean that one should.

    Although I have never used the UoW pattern, it might be beneficial to understand its purpose. And again, I am not trying to be smart here, but you have never been in the trenches. You're still in boot camp.  

     https://www.codeproject.com/Articles/581487/Unit-of-Work-Design-Pattern

    public class RatingRepositoryADO : IRatingRepository
        {
            private readonly DvdContext _context;
    
            public RatingRepositoryADO(DvdContext context)
            {
                this._context = context;
            }
    
            public IEnumerable<Rating> GetAll()
            {
                List<Rating> ratings = new List<Rating>();
    
                using (var cmd = (SqlCommand)_context.CreateCommand())
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.CommandText = "RatingSelectAll";
    
                    using (var dr = cmd.ExecuteReader())
                    {
                        while (dr.Read())
                        {
                            Rating currentRow = new Rating
                            {
                                RatingId = (int)dr["RatingId"],
                                RatingName = dr["RatingName"].ToString()
                            };
    
                            ratings.Add(currentRow);
                        }
                    }
                }
    
                return ratings;
            }
    
        }

    Saturday, January 26, 2019 5:35 AM