30 messaggi dal 30 gennaio 2014
Buonasera.
Mi sto cimentando (sto imparando, a dire il vero) con .net e, ogni tanto mi imbatto in alcuni problemi cui non riesco a trovare la soluzione.
Giusto per testare sul campo le mie capacità acquisite sto lavorando su un gestionale ma mi sono imbattuto in un problema. Tutto bene il recupero dei dati dal DB però il numero del brevetto viene generato utilizzando 4 parametri (il modo verbale giusto è "dovrebbe venire generato").
Ho creato un viewModel che viene mappato dal servizio associato (e fin qui tutto bene), però il mio cruccio è quello di chiamare la funzione esterna che mi genera il numero del brevetto. Provo a postare un po' di codice:
using WaseNet6.Models.Services.Commons;

namespace WaseNet6.Models.ViewModels.Certifications
{
    public class CertificationViewModel
    {
        private readonly ICommonData common;

        public int IdBrevetto { get; set; }
        public int Categoria { get; set; }
        public string Nbrevetto { get; set; }
        public string Istruttore { get; set; }
        public string DataRilascio { get; set; }
        public string DataScadenza { get; private set; }
        public CertificationViewModel(ICommonData common)
        {
            this.common = common;
        }
        public static CertificationViewModel FromEntity(Entities.Brevetti brevetti)
        {
            return new CertificationViewModel
            {
                IdBrevetto = brevetti.IdBrevetto,
                Categoria = brevetti.Level,
                Nbrevetto = common.NumeroBrevetto("123", "owd", 1, 0),
                DataRilascio = brevetti.DataBrevetto.ToShortDateString(),
                DataScadenza = brevetti.DataScadenza.HasValue ? brevetti.DataScadenza.ToString().Substring(0,9) : string.Empty
            };
        }
    } 
}


Ho provato a passare l'interfaccia tramite costruttore, ma continua a segnarmi errore sulla riga del Nbrevetto "E' necessario un riferimento all'oggetto per ....." mentre sulla riga del return mi dice "non sono stati specificati argomenti corrispondenti al parametro forrmale obbligatorio "common"
Sono sicuro che mi sto perdendo in un bicchier d'acqua, pertanto grazie in anticipo a chi mi darà una mano.

Valter
Ciao!
puoi per favore postare dove hai registrato il servizio ICommonData? immagino nella classe Startup nel modo seguente o simile?

services.AddTransient<ICommonData,CommonData>();

Confemato questo puoi per favore anche postare dove vai a utilizzare il metodo che ti restituisce l'errore?
grazie mille!

Maurizio
30 messaggi dal 30 gennaio 2014
Intanto ciao e grazie per la risposta.
Io ne faccio una a metà perchè sono in ufficio, tuttavia:
services.AddTransient<ICommonData,CommonData>();


Esattamente. Da quel servizio già pesco altri dati (ad esempio la lista dei generi che utilizzo nella pagina di registrazione);

Confemato questo puoi per favore anche postare dove vai a utilizzare il metodo che ti restituisce l'errore?


Il metodo lo vorrei utilizzare nella viewModel che ho postato esattamente qui:

...
Nbrevetto = common.NumeroBrevetto("123", "owd", 1, 0),
...

(ovviamente quei parametri sono inventati solo per il test, dopo li sostituisco con il resultset che mi arriva dal model (faccio presente che se escludo quella riga il viewModel funziona perfettamente).

Gli errori che ho descritto nel post me li visualizza VisualStudio:
common.NumeroBrevetto mi evidenza l'errore su common dicendo: E' necessario un riferimento all'oggetto per .....

Poi mi evidenzia anche return new CertificationViewModel (quest'ultimo solo) dicendomi: non sono stati specificati argomenti corrispondenti al parametro forrmale obbligatorio "common"

Grazie ancora per il supporto.

Valter

Valter
ok grazie!
penso che il problema è che cerchi di restituire con quel metodo statico un istanza di CertificationViewModel ma non hai indicato tra parentesi il servizio ICommonData.

prova a modificare nel modo seguente (togliere STATIC e inserire tra parentesi COMMON)

return new CertificationViewModel(common)
{
IdBrevetto = brevetti.IdBrevetto,
Categoria = brevetti.Level,
Nbrevetto = common.NumeroBrevetto("123", "owd", 1, 0),
DataRilascio = brevetti.DataBrevetto.ToShortDateString(),
DataScadenza = brevetti.DataScadenza.HasValue ? brevetti.DataScadenza.ToString().Substring(0,9) : string.Empty
};

fammi sapere se così funziona!
in ogni caso, per curiosità, mi puoi postare il codice dove richiami il metodo FromEntity?
Maurizio
30 messaggi dal 30 gennaio 2014
Ciao.
fammi sapere se così funziona!

Purtoppo no.
Allora ti passo il codice (tappati gli occhi se ci sono strafalcioni, sto cercando di miglliorare):
Inizio dal controller:
 public async Task<IActionResult> MyCards(string id)
        {
            ViewData["Title"] = "Wase Diveducation - MyCards";
            ListViewModel<CertificationViewModel> brevetti = await certificationService.GetBrevettiUtenteAsync(id);

            CertificationListViewModel viewModel = new()
            {
                Certifications = brevetti
            };
            return View(viewModel);
        }

poi il model (ometto l'interfaccia):
 public async Task<ListViewModel<CertificationViewModel>> GetBrevettiUtenteAsync(string userId)
        {
            int idAnagrafica = await memberService.IdAnagraficaFromUserId(userId);
            IQueryable<Entities.Brevetti> baseQuery = dbContext.Brevetti
                .AsNoTracking()
                .Where(o => o.IdOwner == idAnagrafica)
                .OrderByDescending(b => b.Level).ThenByDescending(b => b.DataBrevetto);

            List<CertificationViewModel> viewModel = await baseQuery
                .Select(brev => CertificationViewModel.FromEntity(brev))
                .ToListAsync();

            int totalcount = await baseQuery.CountAsync(); 

            ListViewModel<CertificationViewModel> listModel = new()
            {
                Results = viewModel,
                TotalCount = totalcount
            };
            return listModel;
        }

Il viewModel già è stato postato in precedenza.
Volevo istanziarla tramite l'interfaccia ICommonData, tuttavia mentre stavo scrivendo questo post mi si è accesa una lampadina.
Ho eliminato il costruttore da cui chiamavo l'interfaccia ed ho istanziato direttamente il servizio prima del return:

 public static CertificationViewModel FromEntity(Entities.Brevetti brevetti)
        {
            CommonData common = new();
            return new CertificationViewModel()
            {
                IdBrevetto = brevetti.IdBrevetto,
                Categoria = brevetti.Level,
                Nbrevetto = common.NumeroBrevetto("123", "owd", 1, 0),
                DataRilascio = brevetti.DataBrevetto.ToShortDateString(),
                DataScadenza = brevetti.DataScadenza.HasValue ? brevetti.DataScadenza.ToString().Substring(0,9) : string.Empty
            };
        }

e funziona, tuttavia mi resta il dubbio che vorrei lavorare tramite interfaccia e non direttamente dal sevizio...
Grazie tante per il tuo supporto, comunque e, se trovi un coniglio nel cilindro per me te ne sarò grato.

Valter
Innanzitutto non c'è da tapparsi gli occhi, ho personalmente scritto di peggio!
allora, per avere le idee chiare al 100% avrei bisogno di vedere i lcodice completo ma non so se hai la possibilità di passarmelo qui o via mail (trovi contatti nel mio profilo o sul mio blog)
comunque penso che il problema sia questa parte qua, nel model:

List<CertificationViewModel> viewModel = await baseQuery
.Select(brev => CertificationViewModel.FromEntity(brev))
.ToListAsync();
il CertificationViewModel che utilizzi qua non è istanziato giusto? fai riferimento al metodo statico FromEntity ma non hai dichiarato l'istanza CertificationViewModel? se è così, il problema è che utilizzando il metodo statico senza inizializzare prima la classe, il servizio passato tramite le dependency injection (ICommonData) non sarà inizializzato e quindi va in errore.

un modo per risolvere è creare un altro servizio, un CertificationFactoryService che si occuperà della creazione di un CertificationViewModel e la relativa interfaccia e registrare questo servizio da richiamare nel model.

Interfaccia
 public interface ICertificationFactoryService
    {
        CertificationViewModel FromEntity(Brevetti brevetti);        
    }


Implementazione dove tieni la logica per la creazione di un CertificationViewModel

 public class CertificationFactoryService : ICertificationFactoryService
    {
        private readonly ICommonData _common;

        public CertificationFactoryService(ICommonData common)
        {
            this._common = common;
        }
        public CertificationViewModel FromEntity(Brevetti brevetti)
        {
            return new CertificationViewModel
            {
                IdBrevetto = brevetti.IdBrevetto,
                Categoria = brevetti.Level,
                Nbrevetto = common.NumeroBrevetto("123", "owd", 1, 0),
                DataRilascio = brevetti.DataBrevetto.ToShortDateString(),
                DataScadenza = brevetti.DataScadenza.HasValue ? brevetti.DataScadenza.ToString().Substring(0, 9) : string.Empty
            };
        }
    }


e poi registrazione nel contenitore dei servizi

services.AddTransient<ICertificationFactoryService,CertificationFactoryService >();


a questo punto rimane il richiamare il servizio ICertificationFactoryService all'interno del costruttore del model e sostituire il servizio al metodo statico nel metodo "incriminato" (nel codice seguente l'ho chiamato _myCertificationFactoryService)

 List<CertificationViewModel> viewModel = await baseQuery
                .Select(brev => _myCertificationFactoryService.FromEntity(brev))
                .ToListAsync();


senza il codice completo non ho la certezza che funzioni o che sia la soluzione migliore.. ma è solo la prima che mi è venuta in mente, quindi metto le mani avanti se ti sto facendo perdere del tempo con questa prova ulteriore!
in ogni caso, fammi sapere!
Maurizio
30 messaggi dal 30 gennaio 2014
Ciao.
senza il codice completo non ho la certezza che funzioni o che sia la soluzione migliore.. ma è solo la prima che mi è venuta in mente, quindi metto le mani avanti se ti sto facendo perdere del tempo con questa prova ulteriore!
in ogni caso, fammi sapere!


Imparare qualcosa di nuovo non è perdere tempo e comunque mi hai fatto pensare ad una cosa...
Forse potrebbe diventare un po' più macchinoso ma magari si isolano meglio alcuni pezzetti di codice.
Io, utilizzando gli esempi di BrightSoul da cui ho fatto il corso on-line, ho sempre fatto il mapping delle mie viewModel dall'interno delle stesse (e forse per un uso di un certo tipo potrebbe andare anche bene), invece come hai suggerito te, il mapping dei dati verso le viewModel avverrebbe in una classe esterna permettendo di lasciare più pulite le viewModel e non appesantirle magari con plurimi mapping all'interno delle stesse.
Io, tra oggi e domani un tentativo lo faccio seguendo il tuo input poi ti faccio sapere.
Ti arriverà anche una mail.
Grazie

Valter
30 messaggi dal 30 gennaio 2014
Direi che funziona come un bijou....
Credo anche che mi abbia aiutato a semplificare un po' il codice ripulendo le viewModel dai vari mapping che adesso li faccio fare dai vari factories (in modo da tenere tutto il più separato possibile).
Se ci fosse la possibilità meriteresti un grosso kudos.
Buon we...

Valter

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.