Nested Collections from 2 sources

Apr 25, 2011 at 3:30 PM

I want to inject from 2 sources that belong to a "disconnected graph". A simple example could be:

public class Customer
    {
        public int CustomerId { get; set; }
        public string FirstName { get; set; }
        public string SecondName { get; set; }
        public List<CustomerCountry> CustomerCountries { get; set; }
    }

 public class CustomerCountry
    {
        public int CountryId { get; set; }
        public int PayLoad { get; set; }
    }

public class Country
    {
        public int CountryId { get; set; }
        public string Name { get; set; }
    }

I mean disconnected because CustomerCountry doesn't hold a reference to a Country, but just the CountryId. The target DTO is defined like this:

public class CustomerDTO
    {
        public int CustomerId { get; set; }
        public string FirstName { get; set; }
        public string SecondName { get; set; }
        public List<CountryDTO> Countries { get; set; }
    }
}

 public class CountryDTO
    {
        public int CountryId { get; set; }
        public string Name { get; set; }
    }

Obviously I need 2 sources in order to feed this DTO, for instance:

Customer customer = GetCustomer();
List<Country> countries = GetCountries();

Is it posible to configure a ValueInjection that allows me to get the 2 sources and to create a CustomerDTO with the nested collection Countries populated? Something like:

CustomerDTO customerDTO = new CustomerDTO();

customerDTO.InjectFrom(customer,countries);

Thank you very much.

Apr 26, 2011 at 8:00 AM

a.InjectFrom(b,c) is the same as doing a.InjectFrom(b).InjectFrom(c) the difference is that in the second case you can do a.InjectFrom(b).InjectFrom<MyInj>(c).InjectFrom(new MyInjwithParams(para), d) and so on.

of course you can do a.InjectFrom<MyInj>(b,c)

 

in your case for the simple fields you do:

cusotmerDto.InjectFrom(customer);

and for your list there are many different ways, one very simple would be something like:

cDto.Countries = new List<CountryDto>();

foreach(c in country.Countries)

{

//go to repo/ service if needed

// cDto.Countries.Add()

}

or you can do an injection that will do the same thing as above

Apr 26, 2011 at 10:27 AM

OK, thank you.

My intention is to isolate the code that make the mapping / adaption / injection from the code that retrieves data, etc.

More like:

customerDTO
   .InjectFrom(customer)
   .InjectFrom<NestedIList>(countries)

NestedIList Injector should resolve "countries" type and populate the customerDTO.Countries property.

Could this be made?

Best regards.

Apr 26, 2011 at 1:21 PM

yes,

you could look at prodinner.codeplex.com

in there I do an Injection from IEnumerable<int> to IEnumerable<ClassThatInheritsEntity> so inside the injection I get the GenericTypeArgument and IoC.Resolve( IRepo<TypeThatIGot>)

http://code.google.com/p/prodinner/source/browse/trunk/WebUI/Builder/IntsToEntities.cs

Apr 26, 2011 at 2:32 PM

Thanks, but it happens what I said, you need to access repositories within the code that make the mapping.

Although Automapper doesn't provide a solution to merge a disconnected graph, you can do the following, which it is more accurate to what I want to achieve:

var countryMapping = Mapper.CreateMap<Country, CountryDTO>();
var customerMapping = Mapper.CreateMap<Customer, CustomerDTO>()
                .ForMember(d => d.Countries, opt => opt.Ignore());

var result = Mapper.Map<Customer, CustomerDTO>(customer);
result.Countries = Mapper.Map<List<Country>, List<CountryDTO>>(countries);
So in my opinion the solution would be a mixed of both approaches.

 

Apr 26, 2011 at 2:46 PM
Edited Apr 26, 2011 at 2:48 PM

you can send additional data by using the constructor of the injection like this:

a.InjectFrom(new MyInj(countries), b);

public class MyInj{

public IList countries;

public MyInj(IList<Country> c){

countries = c;

}

public SetValue{

//use countries here

}

 

probably you'll end up with something like this:

customerDTO

.InjectFrom(customer)

.InjectFrom(new TheInj(countries), customer);

May 2, 2011 at 3:08 PM

Hi, O!

I'm trying a Naive approach to get:

customerDTO.InjectFrom(customer)

.InjectFrom(new MyInj(countries),customer);

with no success. This is my new Injector:

public class MyInj : FlatLoopValueInjection<IList<CustomerCountry>, IList<CountryDTO>>
    {
        public IList<Country> countries;

        public MyInj(IList<Country> c)
        {
            countries = c;
        }

        protected override IList<CountryDTO> SetValue(IList<CustomerCountry> sourcePropertyValue)
        {
            IList<CountryDTO> countriesDTO = new List<CountryDTO>();
            foreach (Country c in countries)
            {
                countriesDTO.Add(new CountryDTO()
                {
                    CountryId = c.CountryId,
                    Name = c.Name
                });
            }
            return countriesDTO;
            
        }

May 2, 2011 at 4:36 PM

here you go:

using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Omu.ValueInjecter;

namespace Tests
{
    public class BelitreTest
    {
        public class Customer
        {
            public int CustomerId { get; set; }
            public string FirstName { get; set; }
            public string SecondName { get; set; }
            public IEnumerable<CustomerCountry> CustomerCountries { get; set; }
        }

        public class CustomerCountry
        {
            public int CountryId { get; set; }
            public int PayLoad { get; set; }
        }

        public class Country
        {
            public int CountryId { get; set; }
            public string Name { get; set; }
        }

        public class CustomerDto
        {
            public int CustomerId { get; set; }
            public string FirstName { get; set; }
            public string SecondName { get; set; }
            public IEnumerable<CountryDTO> Countries { get; set; }
        }

        [Test]
        public void Doit()
        {
            var c = new Customer
                        {
                            CustomerId = 3,
                            FirstName = "fn",
                            SecondName = "sn",
                            CustomerCountries =
                                new[] { new CustomerCountry { CountryId = 1 }, new CustomerCountry { CountryId = 2 } }
                        };

            var countries = new[]
                                {
                                    new Country {CountryId = 1, Name = "Moldova"},
                                    new Country {CountryId = 2, Name = "Japan"}
                                };

            var d = new CustomerDto();
            d.InjectFrom(c);
            Assert.AreEqual(c.CustomerId, d.CustomerId);
            Assert.AreEqual(c.FirstName, d.FirstName);
            Assert.AreEqual(c.SecondName, d.SecondName);

            d.InjectFrom(new My(countries), c);

            Assert.AreEqual(c.CustomerCountries.Count(), d.Countries.Count());
            Assert.AreEqual(countries.First().Name, d.Countries.First().Name);
        }

        public class My : ConventionInjection
        {
            private readonly IEnumerable<Country> countries;

            public My(IEnumerable<Country> countries)
            {
                this.countries = countries;
            }

            protected override bool Match(ConventionInfo c)
            {
                return c.SourceProp.Name == "CustomerCountries"
                       && c.TargetProp.Name == "Countries"
                       && c.SourceProp.Value != null;
            }

            protected override object SetValue(ConventionInfo c)
            {
                var src = c.SourceProp.Value as IEnumerable<CustomerCountry>;

                return src.Select(
                    o =>
                    new CountryDTO
                        {
                            CountryId = o.CountryId, 
                            Name = countries.Single(v => v.CountryId == o.CountryId).Name
                        });
            }
        }
    }
}

May 8, 2011 at 5:32 PM

Thank you very much. It opens my range of posible solutions :-)