c #: generar dinámicamente linq select con propiedades anidadas

Actualmente tenemos un paquete que genera linq select dinámicamente a partir de campos de cadena. Funciona bien con propiedades planas, pero no está diseñado para trabajar con campos anidados como someObj.NestedObj.SomeField.

Nuestro código actual funciona de la siguiente manera en el método de servicio:

_context.Shipments
    .Where(s => s.Id == request.Id) // it does not matter just an example
    .Select(request.Fields)
    .ToPage(request); // ToPage extension comes from a nuget package

l parámetro "campos" del objeto de solicitud es solo una cadena que se separa con comas, incluidas las propiedades del objeto de envío.

Realicé algunas refactorizaciones en Envío, agrupé algunos campos en una nueva clase llamada Dirección y la agregué a Envío de la siguiente manera:

// before refactoring
class Shipment {
    // other fields...
    public string SenderAddress;
    public string SenderCityName;
    public string SenderCityId;

    public string RecipientAddress;
    public string CityName;
    public string CityId;
}

// after refactoring
class Shipment {
   // other fields...
   public Address Sender;
   public Address Recipient;
}

class Address {
    public string AddressText;
    public string CityName;
    public string CityId;
}

En aras de la asignación de la base de datos actual, agregué las asignaciones correspondientes como:

public class ShipmentMap : DataEntityTypeConfiguration<Shipment>
    {
        public ShipmentMap()
        {
            ToTable("Shipments");
            // other property mappings
            Property(s => s.Recipient.AddressText).HasMaxLength(1100).HasColumnName("RecipientAddress");
            Property(s => s.Recipient.CityName).HasMaxLength(100).HasColumnName("CityName");
            Property(s => s.Recipient.CityId).IsOptional().HasColumnName("CityId");

            Property(s => s.Sender.AddressText).HasMaxLength(1100).HasColumnName("SenderAddress");
            Property(s => s.Sender.CityName).HasMaxLength(100).HasColumnName("SenderCityName");
            Property(s => s.Sender.CityId).IsOptional().HasColumnName("SenderCityId");
        }
    }

DataEntityTypeConfiguration proviene de paquetes nuget como:

  public abstract class DataEntityTypeConfiguration<T> : EntityTypeConfiguration<T> where T : class
  {
    protected virtual void PostInitialize();
  }

Entonces, mi problema es que la selección (campos) no funciona cuando fields = "Recipient.CityId".

¿Cómo puedo generar dinámicamente linq para seleccionar con campos anidados?

Intenté a continuación usandoLINQ: Selección dinámica Pero no funciona

// assume that request.Fields= "Recipient.CityId"

// in the service method
List<Shipment> x = _context.Shipments
    .Where(s => s.Id == request.Id)
    .Select(CreateNewStatement(request.Fields))
    .ToList();


 // I tried to generate select for linq here    
 Func<Shipment, Shipment> CreateNewStatement(string fields)
        {
            // input parameter "o"
            var xParameter = Expression.Parameter( typeof( Shipment ), "o" );

            // new statement "new Data()"
            var xNew = Expression.New( typeof( Shipment ) );

            // create initializers
            var bindings = fields.Split( ',' ).Select( o => o.Trim() )
                .Select(o =>
                {
                    string[] nestedProps = o.Split('.');
                    Expression mbr = xParameter;

                    foreach (var prop in nestedProps)
                        mbr = Expression.PropertyOrField(mbr, prop);

                    // property "Field1"
                    PropertyInfo mi = typeof( Shipment ).GetProperty( ((MemberExpression)mbr).Member.Name );
                    //
                    // original value "o.Field1"
                    var xOriginal = Expression.Property( xParameter, mi );

                    MemberBinding bnd = Expression.Bind( mi, xOriginal );
                    return bnd;
                });

            // initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }"
            var xInit = Expression.MemberInit( xNew, bindings );

            // expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }"
            var lambda = Expression.Lambda<Func<Shipment,Shipment>>( xInit, xParameter );

            // compile to Func<Data, Data>
            return lambda.Compile();
        }

Lanza una excepción porque mbr se convierte en CityId después del bucle y "mi" es nulo porque no hay ningún campo CityId en el envío. ¿Que me estoy perdiendo aqui? ¿Cómo puedo crear una selección dinámica para una cadena dada con propiedades anidadas?

UPDATE:

Encontré la solución y la agregué como respuesta, también creé un github gist para la solución:

https: //gist.github.com/mstrYoda/663789375b0df23e2662a53bebaf2c7

Respuestas a la pregunta(3)

Su respuesta a la pregunta