Search code examples
c#unity-containerenterprise-libraryfactory-pattern

Unity IOC and Factory pattern, and repository pattern


I am trying to use the Unity Block of the enterprise library in a program i am writing.

But i think i am using dependency injection wrong. I was wondering if some one could point me in the right direction.

    static void Main(string[] args)
        {
                using (IUnityContainer container = new UnityContainer())
                {
                    InitialiseContainer(container);
                    DataCopierFactory dcFactory = new DataCopierFactory();

                    ERunOptions dataCopierType = ExtractParams(args);

                    IDataCopier dataCopier = dcFactory.CreateDataCopier((int)dataCopierType, container);
                    dataCopier.DetectChanges();
                    dataCopier.ParseData();
                    dataCopier.CopyData();
                }
            }
         }
 //use the ioc container to register the EF context type to the repository interfaces..
        private static void InitialiseContainer(IUnityContainer container)
        {

            //add Extensions:
            container.AddNewExtension<Interception>();

            //Licence Schedule
            container.RegisterType<IEFContext, LTE_DownFromWeb_EFContext>("DataCopier.ScheduleDataCopier.Source");
            container.RegisterType<IEFContext, LTE_Licensing_EFContext>("DataCopier.ScheduleDataCopier.Destination");

            container.RegisterType<IRepositorySession>("Schedule_Source",new InjectionConstructor(container.Resolve<IEFContext>("DataCopier.ScheduleDataCopier.Source")));
            container.RegisterType<IRepositorySession>("Schedule_Destination",new InjectionConstructor(container.Resolve<IEFContext>("DataCopier.ScheduleDataCopier.Destination")));



        }

So basically the DataCopier Factory creates an instance of a DataCopier like so:

DataCopierFactory:

 //return a data copier that will transfer data from any DB to any other DB
        public IDataCopier CreateDataCopier(int i, IUnityContainer container)
        {
            switch(i)
            {
                case 1:
                    return new ScheduleDataCopier(container);

                default:
                    throw new InvalidOperationException("Parameter " + i + " does not exist");
            }
        }

a data copier looks like this:

     class ScheduleDataCopier : IDataCopier
    {
        private List<Site> _sites;
        private List<SitesAndApparatuses> _scheduleList;
        private IUnityContainer _container;
        public ScheduleDataCopier(IUnityContainer container)
        {
            _container = container;
            _scheduleList = new List<SitesAndApparatuses>();
        }

        //check if new sites registration has arrived in tblSites on down from web db.
        public bool DetectChanges()
        {
            using (var db = _container.Resolve<IRepositorySession>("Schedule_Source"))
            { 
                SiteAudit lastSite = new SitesAuditRepository().GetLatest();
                var sitesRepo = new SitesRepository();
                var sites = sitesRepo.Where(x => x.SID > lastSite.SALatestSID);

                if (sites.Count() < 1)
                {
                    return false;
                }
                _sites = sites.ToList();
                db.Dispose();
            }
            return true;
        }
        //parse the data into a list of object SitesAndApparatuses
        public bool ParseData()
        {
            try
            {
                foreach (Site s in _sites)
                {
                    var schedule = (SitesAndApparatuses)XmlObjectBuilder.Deserialize(typeof(SitesAndApparatuses), s.XMLFile);
                    schedule.acCode = s.Registration.RAcCode;
                    _scheduleList.Add(schedule);
                }
            }
            catch (Exception ex)
            {
                throw new NotImplementedException("HANDLE THIS SHIT!", ex);
            }
            return true;
        }

        public bool CopyData()
        {
            try
            {
               using (var db = _container.Resolve<IRepositorySession>("Schedule_Destination"))
                {
                        var licensingScheduleRepo = new LicensingScheduleRepository();
                       //some logic
                    db.Commit();
                }
            }
            catch (Exception ex)
            {
            }
                return true;
        }
}

Second question, i resolve my unit of work object called RepositorySession in the Datacopier classes using the unity container i passed... is this the wrong approach and why, im struggling to find any info on it online?

This is probably too much code for someone to read.. but im hoping for an answer!

Thanks in advance Neil


Solution

  • I'd do something like:

    container.RegisterType<IEFContext, LTE_DownFromWeb_EFContext>("Source");
    container.RegisterType<IEFContext, LTE_Licensing_EFContext>("Destination");
    container.RegisterType<IRepositorySession>("Source",new InjectionConstructor(new ResolvedParameter<IEFContext>("Source"));
    container.RegisterType<IRepositorySession>("Destination",new InjectionConstructor(new ResolvedParameter<IEFContext>("Destination")));
    container.RegisterType<IDataCopier,ScheduleDataCopier>("0",new InjectionConstructor(new[] {new ResolvedParameter<IRepositorySession("Source"),new ResolvedParameter<IRepositorySesison>("Destination")}));
    //Now resolve
    
    ERunOptions dataCopierType = ExtractParams(args);
    IDataCopier dataCopier = container.Resolve<IDataCopier(dataCopierType.ToString());
    dataCopier.DetectChanges();
    dataCopier.ParseData();
    dataCopier.CopyData();
    

    DataCopier Class

    class ScheduleDataCopier : IDataCopier
    {
        private List<Site> _sites;
        private List<SitesAndApparatuses> _scheduleList;
        private IRepositorySession _source;
        private (IRepositorySession _destination;
    
        public ScheduleDataCopier(IRepositorySession source, (IRepositorySession destination)
        {
            _source=source;
            _destination=destination;
            _scheduleList = new List<SitesAndApparatuses>();
        }
    
        //check if new sites registration has arrived in tblSites on down from web db.
        public bool DetectChanges()
        {
            SiteAudit lastSite = new SitesAuditRepository().GetLatest();
            var sitesRepo = new SitesRepository();
            var sites = sitesRepo.Where(x => x.SID > lastSite.SALatestSID);
    
            if (sites.Count() < 1)
            {
                return false;
            }
            _sites = sites.ToList();
            _source.DoSomething();
            _source.CommitAndReleaseResources();//clean up but leave object reusable
            return true;
        }
    
        //parse the data into a list of object SitesAndApparatuses
        public bool ParseData()
        {
            try
            {
                foreach (Site s in _sites)
                {
                    var schedule = (SitesAndApparatuses)XmlObjectBuilder.Deserialize(typeof(SitesAndApparatuses), s.XMLFile);
                    schedule.acCode = s.Registration.RAcCode;
                    _scheduleList.Add(schedule);
                }
            }
            catch (Exception ex)
            {
                throw new NotImplementedException("HANDLE THIS SHIT!", ex);
            }
            return true;
        }
    
        public bool CopyData()
        {
            try
            {
                var licensingScheduleRepo = new LicensingScheduleRepository();
                //some logic
                _desitnation.Commit();
            }
            catch (Exception ex)
            {
                //handle exception
            }
            return true;
        }
    }
    

    The two main differences between what you're doing and the above is that I'm using Injection Parameters (the ResolvedParameter class) to dynamically resolve instances of objects when they're needed.

    This allows me to get Unity to do my entire DI process for me, including resolve my DataCopier. If I add another Datacopier I'd just need to add the new DataCopier type to Unity with a name that matches the appropriate ERunOptions type and I'd be able to resolve the new DataCopier with no change to my code:

    container.RegisterType<IDataCopier,RandomDataCopier>("0",new InjectionConstructor(new[] {new ResolvedParameter<IRepositorySession("RandomSource"),new ResolvedParameter<IRepositorySesison>("RandomDestination")}));
    

    and:

    ERunOptions dataCopierType = ExtractParams(args);
    IDataCopier dataCopier = container.Resolve<IDataCopier(dataCopierType.ToString());
    dataCopier.DetectChanges();
    dataCopier.ParseData();
    dataCopier.CopyData();
    

    Stays the same but can process a ScheduledDataCopier or a RandomDataCopier