Autofac DI problem ze wstrzykiwaniem

0

Hej.

Tworzę aplikację w której chciałbym wstrzyknąć swoją klasę do Maina() i tam wykorzystać swoje metody.

Posiadam klase EnduroService która jest następująca:

 public class EnduroService : IEnduroService
    {
        public readonly IGPXParserService _gpxParser;
        public readonly IDistanceService _distanceService;
        public readonly IElevationService _elevationService;
        public readonly ISpeedService _speedService;
        public readonly ITimeService _timeService;
        public readonly IEnumerable<Trackpoint> _node;

        // Inversion of Control here
        public EnduroService(IGPXParserService parser, IDistanceService distance, 
            IElevationService elevation, ITimeService time, string path)
        {
            _gpxParser = parser;
            _distanceService = distance;
            _elevationService = elevation;
            _timeService = time;
            _speedService = new SpeedService(distance, time);
            _node = _gpxParser.ReadGPXFile(path).ToList();
        }

        public IDictionary<string, object> DisplayAllCalculations()
        {}
}

Klasa EnduroService wykorzystuje wiele różnych innych services. Każda z nich ma swój oddzielny Interfejs.

Następnie klasę EnduroService chciałbym wstrzyknąć do metody Main() i tam wykonać pewne operacje.

Stworzyłem sobie klasę ContainerConfig

 public static class ContainerConfig
    {
        public static IContainer Configure()
        {
            var builder = new ContainerBuilder();

            Assembly executingAssembly = Assembly.GetExecutingAssembly();

            builder.RegisterAssemblyTypes(executingAssembly).AsSelf().AsImplementedInterfaces();


            builder.RegisterType<EnduroService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<DistanceService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<TimeService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<SpeedService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<ElevationService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<GPXParserService>().AsImplementedInterfaces().SingleInstance();


            var container = builder.Build();
            return container;
        }
    }

Gdzie rejestruje swoje typy i buduje kontenery.

Nastepnie w metodzie Main() swojej aplikacji konsolowej robię nastepujące rzeczy:

public class Program
    {
        private static ILogger _logger;
        public readonly IEnduroService _enduroService;
        public readonly IGPXParserService _gpxParser;
        public readonly IDistanceService _distanceService;
        public readonly IElevationService _elevationService;
        public readonly ISpeedService _speedService;
        public readonly ITimeService _timeService;
        public readonly IEnumerable<Trackpoint> _node;

        private const string pathToGPXFile = @"DATA\DH.gpx";

        public Program(IGPXParserService parser, IDistanceService distance,
           IElevationService elevation, ITimeService time, string path)
        {
            _gpxParser = parser;
            _distanceService = distance;
            _elevationService = elevation;
            _timeService = time;
            _speedService = new SpeedService(distance, time);
            _node = _gpxParser.ReadGPXFile(path).ToList();
        }

        static void Main(string[] args)
        {
            _logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.RollingFile(@"Log\ApplicationLog.txt", retainedFileCountLimit: 7)
                .CreateLogger();

            var container = ContainerConfig.Configure();

            //_enduroCalculations = new EnduroService(new GPXParserService(), 
            //    new DistanceService(), new ElevationService(), 
            //    new TimeService(), pathToGPXFile);

            //var calculations = _enduroCalculations.DisplayAllCalculations();


            using (var scope = container.BeginLifetimeScope())
            {
                var injection = scope.Resolve<IEnduroService>();

                var calculations = injection.DisplayAllCalculations();

                calculations.ToList().ForEach(item => Console.WriteLine("{0}: {1}", item.Key, item.Value));
                calculations.ToList().ForEach(item => _logger.Information("{0}: {1}", item.Key, item.Value));
            }

            //calculations.ToList().ForEach(item => Console.WriteLine("{0}: {1}", item.Key, item.Value));
            //calculations.ToList().ForEach(item => _logger.Information("{0}: {1}", item.Key, item.Value));

            Console.ReadLine();
        }
    } 

Niestety zamiast poprawnych obliczeń otrzymuje wyjątek taki jak:

 {"None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Trails_Services.Display.EnduroService' can be invoked with the available services and parameters:\r\nCannot resolve parameter 'System.String path' of constructor 'Void .ctor(Trails_Interfaces.IParser.IGPXParserService, Trails_Interfaces.ICalculations.IDistanceService, Trails_Interfaces.ICalculations.IElevationService, Trails_Interfaces.ICalculations.ITimeService, System.String)'."}

Czy ktoś mógłby mi wytłumaczyć co robię źlę i jak to naprawić? Nie do końca czuję jak ten cały Autofac DI działa.

2

Masz w konstruktorze string path Autofac nie wie co ma z tym zrobić.
http://docs.autofac.org/en/latest/register/parameters.html

0

Właśnie nie wiem czy koniecznie muszę robić tam ten konstruktor bo tak na prawdę na poziomie klasy Progrm chce używać tylko EnduroService.

Więc wystarczy mi wstrzyknąć EnduroService przez konstruktor?

5

No ale to właśnie jest problem z utworzeniem EnduroService bo tam jest parametr string path, więc nie wstrzykniesz EnduroService bo Autofac nie potrafi tego utworzyć.

1

najszybszym rozwiazaniem zapewne bedzie zrobienie jakiegos interfejsu do stringa ALE NIE POLECAM TAK TEGO ROBIC. Nie jest to dobra droga (to powstalo zeby Ci to wybic z glowy, ale zapomnialem o tym napisac :D)

sadze ze nie jestes pierwsza osoba ktora zatkela sie z tym problemem
https://stackoverflow.com/questions/9066200/passing-parameters-to-constructors-using-autofac

dostales rozniew od @DibbyDum linka co trzeba zrobic

2

Dlaczego Program ma w ogóle jakieś niestatyczne pola i konstruktor?

fasadin napisał(a):

najszybszym rozwiazaniem zapewne bedzie zrobienie jakiegos interfejsu do stringa

WHAT THE FAAAAAAAAAAAAAAAAAAAAAAK???????????????????????!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

0

Dzięki, patrze właśnie na to wszystko teraz. Link do Stack Overflow nie działa :P

0

Okej udało mi się to rozwiązać z Waszą pomocą :)

Zmodyfikowałem klasę ContainersConfig i stworzyłem Typowane parametry

 public static class ContainerConfig
    {
        public static IContainer Configure()
        {
            var builder = new ContainerBuilder();

            Assembly executingAssembly = Assembly.GetExecutingAssembly();

            builder.RegisterAssemblyTypes(executingAssembly).AsSelf().AsImplementedInterfaces();

            builder.RegisterType<EnduroService>().AsImplementedInterfaces().SingleInstance().WithParameter(new TypedParameter(typeof(string), @"DATA\DH.gpx"));
            builder.RegisterType<DistanceService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<TimeService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<SpeedService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<ElevationService>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<GPXParserService>().AsImplementedInterfaces().SingleInstance().WithParameter(new TypedParameter(typeof(string), @"DATA\DH.gpx"));


            var container = builder.Build();
            return container;
        }
    }

Nie wiem czy to jest dobre rozwiązanie, czy może da sieto zrobić jakoś lepiej/ładniej ale działa :) 
0
somekind napisał(a):

Dlaczego Program ma w ogóle jakieś niestatyczne pola i konstruktor?

Nie wiesz, że można w klasie robić niestatyczne pola oraz definiować konstruktor że tak Ciebie to dziwi? Konwencja wymaga tylko metody statycznej Main (która jest oznaczane w CIL jako .entrypoint) a gdzie ją umieścisz to nie ma znaczenia. Co więcej możesz mieć więcej punktów wejścia w programie i sterować parametrami kompilatora w celu wyboru konkretnego.

0

Wiem, tylko nie wiem po co sobie życie utrudniać i pisać spaghetti. Dlatego spytałem. ;]

Widzę, że mamy nowego newbie, który chętnie się dzieli wiedzą z książek. ;)

0

Widzę, że mamy starego użytkownika która nie posiada nawet wiedzy z książek oraz potrafi ocenić cudza wiedzę na podstawie jednego postu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1