9 messaggi dal 24 febbraio 2011
Salve io ho questa query:

Select 
mov.idstruttura,
p.descrizione,
mov.idtipogruppo,
g.descrizione,
mov.arrivo data,
'ARRIVO' stato,
count(mov.idrecord) nmovimenti
From
Tab_movimenti mov
JOIN
dbo.Tab_comuni_nazioni p
ON mov.idprovenienza=p.idrecord
JOIN
dbo.Tab_tipogruppo g
ON mov.idtipogruppo=g.idtipogruppo
Where
mov.Idstruttura=@idstruttura and mov.arrivo between @data1 and @data2
group by mov.Idstruttura,p.descrizione,mov.idtipogruppo,g.descrizione,mov.arrivo,p.tipo
UNION ALL
Select
mov.idstruttura,
p.descrizione,
mov.idtipogruppo,
g.descrizione,
mov.partenza data,
'PARTENZA' stato,
count(mov.idrecord)
From
Tab_movimenti mov
JOIN
dbo.Tab_comuni_nazioni p
ON mov.idprovenienza=p.idrecord
JOIN
dbo.Tab_tipogruppo g
ON mov.idtipogruppo=g.idtipogruppo
Where
mov.Idstruttura=@idstruttura and mov.partenza between @data1 and @data2
group by mov.Idstruttura,p.descrizione,mov.idtipogruppo,g.descrizione,mov.partenza,p.tipo

order by data


dovrei convertirla in linq e passarla come list ad un datasource.
Adesso la union so come farla e anche passare il risultato sotto forma di list, ma non so come inserire un valore fisso nella query (esempio:ARRIVI) e fare il count di una colonna del db. In pratica convertire la query sopra in codice linq. Potete darmi una mano?
So che potrei richiamare direttamente anche il codice t-sql ma vorrei scriverlo in linq per capire meglio tutte le sue funzioni e potenzialità. Ovviamente accetto anche altri consigli sulla logica di come strutturare la query per ottenere lo stesso risultato.

Ciao e grazie
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
per prima cosa, la strumentazione: dovresti scaricare ed usare LINQPad, che ti dà l'opportunità di provare le tue query in tempo reale. E' un ottimo strumento non solo per chi inizia ma per chi chiunque debba scrivere query un pelo complesse.

Comincia a costruire la tua query LINQ poco alla volta, partendo da una sola tabella (ma sarebbe più corretto dire set di entità, perché con LINQ interroghi il modello concettuale) e poi coinvolgendo le altre.

Il primo errore da evitare in una query come la tua è quello di usare i JOIN.
In LINQ to Entities non si usano i Join, ma le proprietà di navigazione.
Quindi prima di iniziare a scrivere la query, leggi questi articoli (uno te lo avevo già inviato, assicurati di averlo compreso a fondo).
http://coding.abel.nu/2012/06/dont-use-linqs-join-navigate/
https://www.youtube.com/watch?v=4Ir4EIqxYXQ
https://msdn.microsoft.com/en-us/library/vstudio/bb896321%28v=vs.100%29.aspx?f=255&MSPPError=-2147217396

Quindi, vedendo che la tua query relaziona movimenti a comuni e tipi gruppo, dovrai creare altrettante proprietà di navigazione chiamate, ad esempio, Comuni e TipoGruppo nella tua entità Movimento.

A questo punto puoi iniziare a scrivere la tua query LINQ. Vedo che c'è una UNION, quindi di fatto dovrai scrivere due query LINQ ed unirle con il concat. Esempio:
var query1 = ....;
var query2 = ....;
var risultato = query1.Concat(query2);
//Qui assegni risultato al DataSource del controllo


Provo a scrivere giusto la prima query, ovvero quella che è alla sinistra della UNION ALL. Probabilmente ci saranno errori, ma serve a darti un'idea.
var data1 = DateTime.Today.AddDays(-1);
var data1 = DateTime.Today;
var idStruttura = 1;
var query1 = from movimento in ctx.Movimenti
             where movimento.IdStruttura = idStruttura && movimento.arrivo >= data1 && movimento.arrivo <= data2
             group movimento by new { movimento.IdStruttura, Comune = movimento.Comuni.Descrizione, movimento.IdTipoGruppo, TipoGruppo = movimento.TipoGruppo.Descrizione, movimento.Arrivo, TipoComune = movimento.Comuni.Tipo } into gruppo
             select new { gruppo.Key.IdStruttura, gruppo.Key.Comune, gruppo,Key.IdTipoGruppo, gruppo.Key.TipoGruppo, data = gruppo.Key.Arrivo, stato = "ARRIVO", nmovimenti = gruppo.Count() };

Il risultato sarà un tipo anonimo, composto dalle sole proprietà coinvolte nella select. Usare i tipi anonimi è utile soprattuto quando non devi lavorare ulteriormente con il risultato, ma vuoi semplicemente visualizzarlo a schermo come nel tuo caso.

ciao,
Moreno
Modificato da BrightSoul il 05 marzo 2015 07.59 -

Enjoy learning and just keep making
9 messaggi dal 24 febbraio 2011
Ciao e grazie per la tua risposta.
Mi stai dicendo che prima di tutto meglio usare direttamente i nomi delle tabelle che fanno parte del model invece che le join, se ho capito bene.
Quando mi dici: "Quindi, vedendo che la tua query relaziona movimenti a comuni e tipi gruppo, dovrai creare altrettante proprietà di navigazione chiamate, ad esempio, Comuni e TipoGruppo nella tua entità Movimento"

Andando sul model e sulla tabella movimenti clicco col tasto destra mi esce aggiunge proprietà di navigazione ma mi permette solo di scrivere il nome e basta, devo fare così oppure creare un'associazione? Nel secondo caso creando un'associazione mi esce una finestra dove mi dice quale tabella associare, se devono associarla rapporto uno a uno, uno a molto o molti a molti e mi chiede se attivare la proprietà di navigazione. Dovrei fare questo?

Purtroppo in rete non riesco a trovare un esempio che mi spiega come procedere e mi fa capire.

Comunque sulla tabella movimenti, nel designer del model, ho scelto di creare un'associazione ho indicato il rapporto molti a molti e mi ha generato queste classi, puoi vedere se ho fatto bene così nel caso penso di aver capito:

Public Class Tab_movimenti
    Public Property Idrecord As Integer
    Public Property Idstruttura As Nullable(Of Integer)
    Public Property idcategoria As Nullable(Of Byte)
    Public Property idprovincia As Nullable(Of Short)
    Public Property idcomune As Nullable(Of Integer)
    Public Property idcliente As Nullable(Of Integer)
    Public Property idrecordconnesso As Nullable(Of Integer)
    Public Property Idalloggiato As Nullable(Of Byte)
    Public Property idprovenienza As Nullable(Of Integer)
    Public Property idtipogruppo As Nullable(Of Byte)
    Public Property arrivo As Nullable(Of Date)
    Public Property partenza As Nullable(Of Date)
    Public Property nrpresenze As Nullable(Of Integer)
    Public Property idimposta As Nullable(Of Short)
    Public Property idcamera As Nullable(Of Short)
    Public Property ncomponenti As Nullable(Of Short)
    Public Property movchiuso As Nullable(Of Boolean)

    Public Overridable Property Tab_comuni_nazioni As ICollection(Of Tab_comuni_nazioni) = New HashSet(Of Tab_comuni_nazioni)
    Public Overridable Property Tab_tipogruppo As ICollection(Of Tab_tipogruppo) = New HashSet(Of Tab_tipogruppo)
End Class

 Public Class Tab_comuni_nazioni
    Public Property idrecord As Integer
    Public Property codcomune As Nullable(Of Short)
    Public Property descrizione As String
    Public Property codprovincia As Nullable(Of Short)
    Public Property codregione As Nullable(Of Short)
    Public Property codistat As Nullable(Of Integer)
    Public Property codquestura As Nullable(Of Integer)
    Public Property cap As String
    Public Property cessazione As Nullable(Of Date)
    Public Property tipo As String

    Public Overridable Property Tab_movimenti As ICollection(Of Tab_movimenti) = New HashSet(Of Tab_movimenti)
End Class

Public Class Tab_tipogruppo
    Public Property Idtipogruppo As Byte
    Public Property nomegruppo As String

    Public Overridable Property Tab_movimenti As ICollection(Of Tab_movimenti) = New HashSet(Of Tab_movimenti)
End Class


Infien ho installato linqpad e ho connesso il dataabse sql server con la connessione classica linq to sql anche perchè con gli latri metodi non ci riuscivo mi chiedeva la path dell'assembly ma a me è un sito internet.
Comunque come faccio a testareil codice linq scritto con proprietà di navigazione se non c'è la connessione al model del mio progetto?
Modificato da trinity78 il 05 marzo 2015 10.45 -
9 messaggi dal 24 febbraio 2011
Nell'attesa ho provato a creare un pezzo di query:

                Dim query = (From m In Context.Tab_movimenti
                                 Where m.Idstruttura = 5 And m.arrivo = data.SelectedDate
                                 Group By codstruttura = m.Idstruttura, idprovenienza = m.idprovenienza, strprovenienza = m.Tab_comuni_nazioni.descrizione,
                                          idtipogruppo = m.idtipogruppo, strtipogruppo = m.Tab_tipogruppo.nomegruppo, dataelaborazione = m.arrivo, movchiuso = m.movchiuso, tipo = m.Tab_comuni_nazioni.tipo, stato = "ARRIVO" Into mov = Group
                                 Select New With {codstruttura, idprovenienza, strprovenienza, idtipogruppo, strtipogruppo, dataelaborazione, movchiuso, .nmov = mov.Count, stato, .tipomov = If(tipo = "ITA", "ITALIANI", "STRANIERI")})


in base al model modificato (scritto nel precedente post) con la creazione di proprietà di navigazione, sperando che abbiamo fatto bene li...ma nella quely il compilatore mi da tre errori di sentissi:

'descrizione' non è un membro di 'System.Collections.Generic.ICollection(Of Tab_comuni_nazioni)
'nomegruppo' non è un membro di 'System.Collections.Generic.ICollection(Of Tab_tipogruppo)
'tipo' non è un membro di 'System.Collections.Generic.ICollection(Of Tab_comuni_nazioni)'

Perchè?
Modificato da trinity78 il 05 marzo 2015 11.24 -
9 messaggi dal 24 febbraio 2011
Ciao allora in sql server con il management ho creato le relazioni tra le tabelle che mi servono e poi le ho aggiornate nel model a questo punto si sono create le proprietà di navigazione correttamente appunto ora il codice funziona.

ecco il mio codice:

                Dim query = (From m In Context.Tab_movimenti
                                 Where m.Idstruttura = 5
                                 Group By codstruttura = m.Idstruttura, idprovenienza = m.idprovenienza, strprovenienza = m.Tab_comuni_nazioni.descrizione,
                                          idtipogruppo = m.idtipogruppo, strtipogruppo = m.Tab_tipogruppo.nomegruppo, dataelaborazione = m.arrivo, movchiuso = m.movchiuso, tipo = m.Tab_comuni_nazioni.tipo, stato = "ARRIVO" Into mov = Group
                                 Select New With {codstruttura, idprovenienza, strprovenienza, idtipogruppo, strtipogruppo, dataelaborazione, movchiuso, .nmov = mov.Count, stato, .tipomov = If(tipo = "ITA", "ITALIANI", "STRANIERI")}).Concat _
                                 (From m In Context.Tab_movimenti
                                  Where m.Idstruttura = 5
                                  Group By codstruttura = m.Idstruttura, idprovenienza = m.idprovenienza, strprovenienza = m.Tab_comuni_nazioni.descrizione,
                                          idtipogruppo = m.idtipogruppo, strtipogruppo = m.Tab_tipogruppo.nomegruppo, dataelaborazione = m.partenza, movchiuso = m.movchiuso, tipo = m.Tab_comuni_nazioni.tipo, stato = "ARRIVO" Into mov = Group
                                  Select New With {codstruttura, idprovenienza, strprovenienza, idtipogruppo, strtipogruppo, dataelaborazione, movchiuso, .nmov = mov.Count, stato, .tipomov = If(tipo = "ITA", "ITALIANI", "STRANIERI")}).ToList


                Dim result = From q In query Select q Order By q.stato Ascending

                RadGrid1.DataSource = result  'read.GetMovimenti(Session("idstruttura"), data.SelectedDate)
                RadGrid1.DataBind()


da come puoi vedere il concat l'ho usato direttamente in una query.
poi devevo fare un order by in base al tipo di movimento e ho scritto così.
Va bene secondo te?

Togliemi due curiosità, perchè non utilizzare il join dato che linq lo permette di fare e perchè non union ma concat?
Infine c'è qualche book (in italaino magari) che insegna linq to entities le vari query che si possono realizzare e come gestire le entità.

Ciao e grazie
Modificato da trinity78 il 05 marzo 2015 14.13 -
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

trinity78 ha scritto:

con il management ho creato le relazioni tra le tabelle che mi servono

Ottimo, questa è una buona pratica che va svolta a prescindere che il db sia usato con EF o no. L'integrità referenziale dei dati va sempre garantita ponendo gli appositi vincoli di Foreign Key.

trinity78 ha scritto:

devevo fare un order by in base al tipo di movimento e ho scritto così.

Ok. Se metti l'order by prima di invocare il .ToList(), l'ordinamento verrà effettuato lato server.

Il .ToList(), infatti, causa subito l'esecuzione della query al database. I risultati vengono estratti e posti in memoria. A quel punto, se esegui un order by, lo esegui su oggetti in memoria. Questo tutto sommato non è un problema se la lista è formata da pochi elementi.

trinity78 ha scritto:

perchè non utilizzare il join dato che linq lo permette

Perché, usando le proprietà di navigazione, la tua query è molto più succinta e leggibile. In quel modo lasci che sia EF a fare il "lavoro pesante" di tradurre una query LINQ nella controparte SQL - del resto conosce le relazioni che esistono fra le tabelle e dunque tu hai la libertà di non doverlo ribadire esplicitamente con delle join.

Usando un'analogia: perché la gente preferisce andare in superstrada anziché usare i tornanti di montagna, dato che esistono? Perché è più comoda e rapida da percorrere.

Le join le puoi usare in Linq to Objects, ovvero quando hai delle liste di oggetti in memoria che non possiedono proprietà di navigazione. E' un ripiego. Se la superstrada è chiusa, vado volentieri sui tornanti, altrimenti non riuscirei ad arrivare a destinazione.


perchè non union ma concat?

Union applica una DISTINCT sui risultati, mentre concat no. Vedendo che nella tua query SQL originale avevi usato UNION ALL, ho pensato di suggerirti l'uso di Concat, che si limita semplicemente ad unire i risultati delle due query.
Qui è spiegato approfonditamente:
http://blog.cincura.net/233043-union-and-concat-in-linq-to-entities/

trinity78 ha scritto:

Infine c'è qualche book (in italaino magari) che insegna linq to entities

Inizia su MSDN, trovi vari esempi:
https://msdn.microsoft.com/library/vstudio/bb399367

I migliori libri su Entity Framwork sono quelli di Julie Lerman e Rowan Miller. Eccone uno (in inglese):
http://www.amazon.com/dp/1449312969/?tag=stackoverfl08-20

ciao,
Moreno

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.