229 messaggi dal 20 novembre 2014
Ciao a tutti,
già più di una volta mi sono imbattuto in un problema del genere e ho preso altre strade per ovviare ma sta volta voglio chiedere per capire se questa cosa è fattibile (mi sembra difficile) o se c'è una soluzione più veloce che mi permetta di fare quello che voglio. Non mi dilungo nella spiegazione perchè è difficile da spiegare preferisco postare il codice.

public ActionResult Index()
{
var lst = (from c in db.Personale
join p in db.PermessiUtente
on c.Ruolo equals p.ID
select new V_Personale
{
ID = c.ID,
Nome = c.Nome,
Cognome = c.Cognome,
NomeUtente = c.NomeUtente,
Ruolo = p.Descrizione,
Attivo = c.Attivo,
DpiConsegnatiConsegnare = getNumConsegnati(c.ID).ToString() + "/" + getDaConsegnare(c.ID).ToString()
}).ToList().OrderBy(x=>x.Cognome);

return View(lst.ToList());
}

private int getNumConsegnati(int IdOperatore)
{
int numConsegnati = (from a in db.AssociazioneDi
where a.Riconsegnato == false
select a).Count();
return numConsegnati;
}

private int getDaConsegnare(int IdOperatore)
{
int numDaConsegnare = (from ld in db.Listadi
join am in db.AssociazioneMansioni
on ld.Mansione equals am.IdMansione
where am.IdOperatore == IdOperatore
select ld).Count();

return numDaConsegnare;
}

l'alternativa quale potrebbe essere?
Grazie
11.886 messaggi dal 09 febbraio 2002
Contributi
Sì, è possibile, puoi fare degli extension methods. Io li uso per implementare determinate regole di business che mi fanno da filtro o per eseguire aggregazioni. Per esempio:

var fornitori = 
             (from c in context.Anagrafiche.Fornitori()
             select c).ToList()


Ad esempio, l'extension method Fornitori potrebbe essere implementato così:
public static IQueryable<Anagrafica>(this IQueryable anagrafiche){
  return anagrafiche.Where(anagrafica => anagrafica.Tipo == "F");
}


Usare questo sistema sarà più semplice se al posto delle varie join usassi le proprietà di navigazione.
https://coding.abel.nu/2012/06/dont-use-linqs-join-navigate/

ciao,
Moreno
Modificato da BrightSoul il 13 luglio 2016 20.00 -

Enjoy learning and just keep making
229 messaggi dal 20 novembre 2014
Ciao Moreno,
grazie mille sempre per il tuo prezioso aiuto, dunque:

Sì, è possibile, puoi fare degli extension methods. Io li uso per implementare determinate regole di business che mi fanno da filtro o per eseguire aggregazioni. Per esempio:

var daSaldareAiFornitori = 
             (from c in context.Anagrafiche.Fornitori()
             select c.TotaleDaSaldare()).ToList()


Ad esempio, l'extension method Fornitori potrebbe essere implementato così:
public static IQueryable<Anagrafica>(this IQueryable anagrafiche){
  return anagrafiche.Where(anagrafica => anagrafica.Tipo == "F");
}



Non capisco in cosa mi aiuta l'extension method? Io devo semplicemente passare dei valori all'oggetto-vista, cioè il codice che ho postato ce l'ho nell'index... non so se si è capito bene il mio problema


Usare questo sistema sarà più semplice se al posto delle varie join usassi le proprietà di navigazione.
https://coding.abel.nu/2012/06/dont-use-linqs-join-navigate/


Ho dato uno sguardo ma non ho capito molto, cioè fa il join senza fare il join... farò qualche prova, sconoscevo questa tecnica
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,


cioè fa il join senza fare il join...

Sono "proprietà di navigazione". E' una tecnica molto comune ed efficiente che devi assolutamente adottare al più presto. In Linq to Entities (Entity Framework), le join non si usano se non in casi limite.


non so se si è capito bene il mio problema

No :D perché hai scritto:

Non mi dilungo nella spiegazione perchè è difficile


quindi andrò a tentativi ma ci tengo comunque a spiegare la questione perché vedo che nei metodi getDaConsegnare e getNumConsegnati stai effettuando altre query LINQ che rendono il tutto inefficiente.
L'obiettivo è includere il tutto in una sola query LINQ. Le proprietà di navigazione aiutano in questo.

Per spiegare il concetto in maniera semplice, prendiamo due entità relazionate tra loro: Impiegato e Lavoro. Nota le proprietà di navigazione che legano le due entità.
public class Impiegato
{
    public Impiegato()
    {
        Lavori = new HashSet<Lavoro>();
    }

    [Key]
    public int IdImpiegato { get; set; }
    public string Nome { get; set; }
    public string Cognome { get; set; }
    //Proprietà di navigazione che mi permette di accedere
    //ai lavori assegnati a questo impiegato
    public virtual ICollection<Lavoro> Lavori { get; set; }
}

public class Lavoro
{
    public Lavoro()
    {
        Impiegati = new HashSet<Impiegato>();
    }

    [Key]
    public int IdLavoro { get; set; }
    public string Titolo { get; set; }
    public bool Completato { get; set; }
    //Proprietà di navigazione che mi permette di accedere
    //agli impiegati che svolgono questo lavoro
    public virtual ICollection<Impiegato> Impiegati { get; set; }
}


Un impiegato può svolgere più lavori contemporaneamente, come è tipico in Italia. Quindi mappo la relazione molti a molti nel DbContext. Nota che non ho bisogno di alcuna entità di raccordo. Ho solo Impiegati e Lavori.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    //Per brevità ometto i nomi di colonne e tabelle coinvolte
    modelBuilder
        .Entity<Impiegato>()
        .HasMany(impiegato => impiegato.Lavori)
        .WithMany(lavoro => lavoro.Impiegati);

}


Adesso posso fare una sola query LINQ che mi permette di ottenere il nome dell'impiegato e il numero dei lavori completati e totali a cui è stato assegnato. Nessuna join necessaria. Nessun passaggio di id necessario.
var situazione = db.Impiegati.Select(impiegato => new {
  impiegato.Nome,
  impiegato.Cognome,
  LavoriInCorso = impiegato.Lavori.Where(l => !l.Completato).Count(),
  LavoriTotali = impiegato.Lavori.Count()
});


ciao,
Moreno
Modificato da BrightSoul il 13 luglio 2016 20.02 -

Enjoy learning and just keep making
229 messaggi dal 20 novembre 2014
Ciao,
l'esempio calzante mi ha fatto capire cosa intendevi e penso che debba iniziare ad usarlo perchè ho lo stesso problema da più parti...
Provo ad applicarlo e ti faccio sapere :)
Grazie intanto
229 messaggi dal 20 novembre 2014
Allora, sto cercando di non impazzire, ho fatto una piccola prova su due sole classi. Faccio la query nell'index che a sua volta è una "vista" infatti idOperatore è una stringa:

public ActionResult Index()
{
var query = (from o in db.OperatoriAbilitatiInterventi
select new V_OperatoriAbilitatiInterventi()
{
ID = o.ID,
Attivo = o.Attivo,
//IdOperatore = o.Persona.Where(x => x.ID == o.IdOperatore).Select(x => x.ID)
IdOperatore = o.Persona.Where(x => x.ID == o.IdOperatore).Select(n => n.Nome).ToString(),
IdIntervento = o.IdIntervento.ToString()
});

return View(query.ToList());
}

se non metto il .ToString() mi dice:

"Impossibile convertire implicitamente il tipo 'System.Collections.IEnumerable<string>' in 'string'"

se invece metto il .ToString() non mi da errore, però a runtime va in errore:

[NotSupportedException: Impossibile convertire in stringa i valori di tipo 'collection[Edm.String(Nullable=True,DefaultValue=,MaxLength=200,Unicode=True,FixedLength=False)]'.]

che è uguale... sai dirmi dove sbaglio?
Grazie anticipate :)
229 messaggi dal 20 novembre 2014
Moreno,
per completare il quadro ho fatto anche:

public ActionResult Index()
{
var query = (from o in db.OperatoriAbilitatiInterventi
select new V_OperatoriAbilitatiInterventi()
{
ID = o.ID,
Attivo = o.Attivo,
//IdOperatore = o.Persona.Where(x => x.ID == o.IdOperatore).Select(x => x.ID)
Persona = o.Persona.Where(x => x.ID == o.IdOperatore).ToList(),
IdIntervento = o.IdIntervento.ToString()
});

return View(query.ToList());
}

e nella view:

<td>
@*@Html.DisplayFor(modelItem => item.IdOperatore)*@
@Html.DisplayFor(modelItem => item.Persona.Nome)
</td>

ma mi da errore nella view non riesce a dereferenziare il Nome della Persona... probabilmente qualcosa non mi è del tutto chiaro
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
non conoscendo le tue entità o il mapping, devo basarmi solo su quello che vedo in questa parte di query:
IdOperatore = o.Persona.Where(x => x.ID == o.IdOperatore).Select(n => n.Nome).ToString(),

  • o.Persona è una collezione, lo deduco dal fatto che hai potuto usare il metodo Where di seguito. Suggerimento #1: dato che è una collezione dovresti chiamarla Persone, al plurale. Meglio ancora sarebbe dargli un nome significativo. Che relazione c'è tra queste persone e i OperatoriAbilitatiInterventi? Sono aiutanti? committenti? finanziatori? I nomi precisi aiutano te stesso a non impazzire quando devi tornare a manutenere il codice dopo 6 mesi averlo scritto (e aiuterebbero me a capire meglio il problema);
  • IdOperatore sembra essere di tipo string, a giudicare dall'errore. Suggerimento #2: se gli assegni un NOME non dovrebbe chiamarsi ID. Se gli assegni un ID intero, non dovrebbe essere di tipo stringa.
  • Il fatto che tu stia usando una Where che filtra per o.IdOperatore dovrebbe farti venire in mente il Suggerimento #3: creare una proprietà di navigazione che ti permetta di arrivare da o alla persona che porta l'IdOperatore.


A parte queste considerazioni, il problema si spiega subito: stai cercando di assegnare una collezione (o.Persona.Where(x => x.ID == o.IdOperatore).Select(n => n.Nome)) ad un valore scalare (IdOperatore). E' proprio ciò che l'errore diceva.

Se vuoi ottenere il Nome della prima ed unica persona che ha ID == o.IdOperatore, allora usa FirstOrDefault, che ti restituisce il primo risultato della lista:
IdOperatore = o.Persona.FirstOrDefault(x => x.ID == o.IdOperatore).Nome


ciao,
Moreno
Modificato da BrightSoul il 15 luglio 2016 20.20 -

Enjoy learning and just keep making

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.