none
List<object.GetType()> = new List<object.GetType()> RRS feed

  • Pergunta

  • //tenho as seguintes classes

    class Item
    {
    			
    }
    
    class Order
    {
    	public List<Item> Items { get; set; }
    }

    class Phone
    {
    
    }
    
    class Seller
    {
            public List<Phone> PhoneNumbers { get; set; }
    }

    class MyAdapter { public List<Model> Read<Model>() where Model: new() { List<Model> result = new List<Model>; Model obj = new Model(); result.Add(obj); PropertyInfo[] properties = typeof(Model).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (PropertyInfo property in properties) { Type propertyType = property.PropertyType; if (propertyType.IsGenericType) { Type propGenericType = propertyType.GetGenericTypeDefinition(); if (propGenericType == typeof(List<>)) { Type listType = propertyType.GetGenericArguments()[0]; //o problema esta nesse ponto... //preciso preencher a lista das propriedades filhas e depois atribuilas nos objetos pais //chamada recursiva //chama Read mais uma vez, agora passando como generic o tipo da propriedade filha

    //esse ponto nao é reconhecido pelo compilador List<listType> lst = this.Read<listType>(); property.SetValue(obj, lst, null); //...o problema esta nesse ponto } } } return result; } }


    //exemplo utilizando as classes acima

    MyAdapter adp = new MyAdapter();
    
    //fill list of orders with your items
    List<Order> orders = adp.Read<Order>();
    
    //fill list of sellers with your phonenumbers
    List<Seller> sellers = adp.Read<Seller>();


    • Editado Joab Silveira sexta-feira, 8 de fevereiro de 2013 03:52 correção em public List<Model> Read<Model>() where T: new() para public List<Model> Read<Model>() where Model: new()
    quinta-feira, 7 de fevereiro de 2013 18:17

Respostas

  • Você precisa de algo +/- assim:

    var method = typeof(MyAdapter).GetMethod("Read");
    method = method.MakeGenericMethod(listType);
    
    method.Invoke(null, "where blablabla");
    

    Por que isso?
    Como você só conhecerá o tipo listType em runtime não é possível usa-lo em compile-time.
    Desta forma, toda a chamada precisa ocorrer por Reflection.

    • Marcado como Resposta Joab Silveira sexta-feira, 8 de fevereiro de 2013 18:23
    sexta-feira, 8 de fevereiro de 2013 14:04
  • Eu cometi um equívoco.
    Em:

    method.Invoke(null, "where blablabla");

    Estou assumindo que o método é estático, porque o primeiro parâmetro "null" é a instância do objeto na qual o método será executado.

    Algo assim deve funcionar para instâncias:

    var adp = new MyAdapter();
    method.Invoke(adp, "where blablabla");

    Abs

    • Marcado como Resposta Joab Silveira sexta-feira, 8 de fevereiro de 2013 18:23
    sexta-feira, 8 de fevereiro de 2013 15:40

Todas as Respostas

  • Afinal qual sua dúvida?

    Qual problema está sendo gerado.


     
    Atenciosamente:
     
    Edney Batista da Silva - Desenvolvedor .Net, C#, Asp.Net, Java, SQL
     
    CONTATOS:
     
    Hotmail:
    edney_contato@hotmail.com
    Skype: edney.dyn

    quinta-feira, 7 de fevereiro de 2013 21:01
  • Você está trabalhando com listas em Linq , talvez não seja necessario

    fazer nada recursivo , e sim usar metodos do LINQ como .Select() e .Where(),

    Abraço


    Se for útil marcar como resposta Para que outros Aproveitem

    https://www.virtualbase.com.br/


    • Editado Jones Roberto terça-feira, 1 de dezembro de 2020 02:30
    sexta-feira, 8 de fevereiro de 2013 00:09
  • Opa Edney,

    como c pode perceber, para chamar o método generico Read eu preciso passar um tipo em <Model>
    public List<Model> Read<Model>()

    quando esse tipo é previamente conhecido funciona sem problemas como no exemplo abaixo:
    eu chamo o metodo passando o tipo Order, ou seja, eu ja sei o tipo que devo passar
    List<Order> orders = adp.Read<Order>();

    no trecho onde indico o problema o tipo é dinamico, ou seja, eu nao sei qual tipo vai ser passado, isso porque vai depender da classe que representa o modelo que eu mando para o metodo em <Model>

    //pego o tipo da lista (a lista é uma propriedade do <Model> que passei no metodo)
    Type listType = propertyType.GetGenericArguments()[0];         
           
    //tento criar uma nova lista com o tipo capturado. esse trecho não é reconhecido na hora de compilar
    //Error 10 The type or namespace name 'listType' could not be found (are you missing a using directive or an assembly reference?)
    List<listType> lst = this.Read<listType>();  

    //atribuo a lista à propriedade referente no <Model>
    property.SetValue(obj, lst, null);     


    sexta-feira, 8 de fevereiro de 2013 03:36
  • Opa Jones,

    na verdade nao estou trabalhando com Linq
    o where utilizado em "where Model: new()" é uma restrição de generics (constraint generic) que determina que o tipo Model deverá ter um construtor no formato new Model()
    isso me permite instanciar o Model passado no metodo dessa maneira: 
    Model model = new Model();

    voltando ao problema, vou explicar o que estou precisando.
    estou trabalhando em algo desse tipo:


    onde o Adapter é a ponte entre os dados do banco de dados e o modelo de classes, algo como um ORM
    atraves de atributos eu faço o mapeamento das classes e suas propriedades 
    dentre outros atributos que mapeiam tabelas, campos, stored procedures, chaves primarias e por ai vai, abaixo vao os mais importantes

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
        public class MappedToDBTable : Attribute
        {
            public string TableName { get; set; }
        }
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
        public class MappedToDBColumn : Attribute
        {
            public string ColumnName { get; set; }
        }

    a partir dai, reescrevendo as classes que citei no topico ficariam assim

    [MappedToDBTable(TableName="Items")]
    class Item
    {
    	[MappedToDBColumn, MappedToDBPrimaryKey]
    	public int ItemId { get; set; }
    
    	[MappedToDBColumn]
    	public int OrderNum { get; set; }
    }
    [MappedToDBTable(TableName="Orders")]
    class Order
    {
    	[MappedToDBColumn, MappedToDBPrimaryKey]
    	public int OrderNum { get; set; }
    
    	[MappedToDBChildTable(TableName="Items", MasterKeyFieldName="OrderNum", ForeingKeyFieldName="OrderNum")]
    	public List<Item> Items { get; set; }
    }

    percebe-se que Orders tem um relacionamento de um para muitos com Items

    ...voltando ao adapter..., 
    os metodos Create, Read, Update e Delete dele sao genericos, vou passar classes que estejam com esses mapeamentos
    o adapter vai verificar se a classe e suas propriedades sao mapeados para um atributo valido e a partir dai conectar com um banco de dados e fazer a operacao de acordo com o metodo chamado
    os metodos Create, Update e Delete ja estao funcionando corretamente, inclusive com tabelas relacionadas
    se eu passo um objeto do tipo Order no metodo Create, o adapter vai inserir o registro referente ao pedido na tabela Orders do banco e tambem vai inserir todos os itens do pedido na tabela Items, lembrando que os itens estao mapeados para uma propriedade de Order
    ate aki ta tudo ok, consigo criar, atualizar e apagar registros de forma recursiva em tabelas relacionadas

    o problema esta apenas no Read, nao consigo retornar os dados das tabelas filhas no trecho onde indiquei o problema na postagem do topico
    hoje o Read apenas retorna os dados de Order, isso porque nao consigo instanciar a lista com listType como informei em

    ...
    Type listType = propertyType.GetGenericArguments()[0];
    List<listType> lst = this.Read<listType>();
    ...

    como nos metodos Create, Update e Delete eu nao preciso criar novas instancias do modelo passado, nao tive problemas com eles, no caso do Read, eu preciso criar uma instancia de uma List<> do tipo referente a tabela pai (List<Order>) (essa primeira parte ta funcionando) e para cada registro da tabela pai, criar uma instancia de uma List<> do tipo de seus filhos (List<Item>) e depois atribui-la a propriedade referente na tabela pai

    temporariamente resolvi o problema dessa maneira:

    //pedidos
    List<Order> orders = adp.Read<Order>();
    
    //para cada pedido capturo os itens e faço a atribuicao
    foreach (Order order in orders)
    {
    	List<Item> items = adp.Read<Item>("where orderNum = " + order.OrderNum);
    	order.Items = items;
    }

    quero elimitar esse trecho do loop, e coloca-lo como responsabilidade do adapter
    toda vez que preciso consultar os pedidos tenho que consultar os itens manualmente ou entao criar um adapterPedidos descendente de adapter especifico para pedidos e jogar esse trecho nele
    mas teria que fazer isso para todas as tabelas relacionadas

    abc.
    • Editado Joab Silveira sexta-feira, 8 de fevereiro de 2013 05:25
    sexta-feira, 8 de fevereiro de 2013 05:05
  • Você precisa de algo +/- assim:

    var method = typeof(MyAdapter).GetMethod("Read");
    method = method.MakeGenericMethod(listType);
    
    method.Invoke(null, "where blablabla");
    

    Por que isso?
    Como você só conhecerá o tipo listType em runtime não é possível usa-lo em compile-time.
    Desta forma, toda a chamada precisa ocorrer por Reflection.

    • Marcado como Resposta Joab Silveira sexta-feira, 8 de fevereiro de 2013 18:23
    sexta-feira, 8 de fevereiro de 2013 14:04
  • opa Fabio, 

    no primeiro momento eu tentei e obtive o seguinte erro:
    Non-static method requires a target.

    mudei o método para static e funcionou sem problemas, show de bola
    uma duvida é se so funciona com metodos estaticos (nao pretendo utilizar metodos estaticos nessa classe) e outra é caso eu tenha metodos sobrecarregados
    ex.: vamos supor que o meu adapter tenha o primeiro metodo List<Model> Read<Model>(string filter) e um outro List<Model> Read<Model>(string filter, bool useTransaction)
    nesse caso como eu faria pra chamar respectivamente os metodos

    abc

    sexta-feira, 8 de fevereiro de 2013 14:43
  • Eu cometi um equívoco.
    Em:

    method.Invoke(null, "where blablabla");

    Estou assumindo que o método é estático, porque o primeiro parâmetro "null" é a instância do objeto na qual o método será executado.

    Algo assim deve funcionar para instâncias:

    var adp = new MyAdapter();
    method.Invoke(adp, "where blablabla");

    Abs

    • Marcado como Resposta Joab Silveira sexta-feira, 8 de fevereiro de 2013 18:23
    sexta-feira, 8 de fevereiro de 2013 15:40
  • opa Fabio,

    realmente, agora funcionou
    entendi o que você fez, show de bola

    tambem consegui distinguir quando ha metodos sobrecarregados
    basta utilizar dessa maneira:

    var method = typeof(Adapter).GetMethod("Read", new Type[] {typeof(tipoDoParametro1), typeof(tipoDoParametro)}, typeof(tipoDoParametroN)});

    quando "method.Invoke(this, args)" for chamado ele direciona para o metodo correto

    ha so um problema caso precise chamar um metodo sobrecarregado que nao tenha parametro
    ex.:

    public void Metodo()
    public void Metodo(string parametro)
    public void Metodo(int parametro)

    pra carregar Metodo() ficaria assim:

    var method = typeof(Adapter).GetMethod("Metodo");

    pra carregar metodo(string parametro) ficaria assim:

    var method = typeof(Adapter).GetMethod("Metodo", new Type[] {typeof(string)});

    pra carregar metodo(int parametro) ficaria assim:

    var method = typeof(Adapter).GetMethod("Metodo", new Type[] {typeof(int)});

    a primeira chamada a metodo() da erro: "Ambiguous match found."
    as outras duas funcionam sem problemas

    no mais ja da pra trabalhar com o meu adapter, ja que o metodo Read dele nao tem sobrecarregados sem parametros

    o meu metodo ficou parecido com o que se segue abaixo:

    public List<Model> Read<Model>() where Model: new() { List<Model> result = new List<Model>(); Model obj = new Model(); result.Add(obj); PropertyInfo[] properties = typeof(Model).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (PropertyInfo property in properties) { Type propertyType = property.PropertyType; if (propertyType.IsGenericType) { Type propGenericType = propertyType.GetGenericTypeDefinition(); if (propGenericType == typeof(List<>)) {

                               

        Type propGenericType = propertyType.GetGenericTypeDefinition();

       //trecho atualizado...
       var method = typeof(Adapter).GetMethod("Read");
                                       method = method.MakeGenericMethod(listType);
                                property.SetValue(obj, method.Invoke(this, null), null);
       //...trecho atualizado

    } } } return result; }


    vlw pela ajuda Fabio

    abc







    • Editado Joab Silveira sexta-feira, 8 de fevereiro de 2013 18:29
    sexta-feira, 8 de fevereiro de 2013 18:20