none
Aggiornamento in tempo reale da tabella con FK RRS feed

  • Domanda

  • Salve a tutti,

    sto realizzando un progetto in MVC 5 dove vorrei poter aggiornare le pagine in tempo reale alla modifica delle tabelle del database. Ho trovato questo esempio funzionante http://venkatbaggu.com/signalr-database-update-notifications-asp-net-mvc-usiing-sql-dependency e sono riuscito a riprodurlo con una tabella semplice (senza FK). Ho difficoltà a fare la stessa cosa con una tabella che ha una FK. E' tutto spiegato in questo thread che ho già aperto http://stackoverflow.com/q/25845380/1752757

    In pratica vorrei fare la stessa cosa per la tabella Products solo che non so come comportarmi nella classe repository perché vorrei visualizzare il campo Vendors.Name ma non so come fare visto che tale campo non ce l'ho nella classe Product e quindi non ho una variabile a cui assegnare il valore.

    Ho provato una cosa tipo questa

            [DisplayName("Produttore")]
            public string VendorName
            {
                get
                {
                    return Vendor.Name;
                }
                set
                {
                    Vendor.Name = value;
                }
            }

    Ma mi dice che il campo VendorName non esiste nella tabella Products.

    Sapreste aiutarmi?

    Grazie

    giovedì 2 ottobre 2014 16:11

Risposte

Tutte le risposte

  • Potresti allegare la tua classe completa?

    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    giovedì 2 ottobre 2014 16:27
    Moderatore
  • Ciao,

    questa è la classe Product.cs:

        [Table("Products")]
        public class Product
        {
            [Key]
            [DisplayName("ID Prodotto")]
            public long ProductID { get; set; }
    
            [DisplayName("ID Produttore")]
            public long VendorID { get; set; }
    
            [DisplayName("Prodotto")]
            [StringLength(50)]
            [Required(AllowEmptyStrings = false, ErrorMessage = "E' richiesto un prodotto")]
            public string Name { get; set; }
    
            public virtual Vendor Vendor { get; set; }
        }

    Mentre questa è classe Vendor.cs:

        [Table("Vendors")]
        public class Vendor
        {
            [Key]
            [DisplayName("ID Produttore")]
            public long VendorID { get; set; }
    
            [DisplayName("Produttore")]
            [StringLength(50)]
            [Required(AllowEmptyStrings = false, ErrorMessage = "E' richiesto un produttore")]
            public string Name { get; set; }
        }

    Questo è un mini schema del database:

    Come vedete al relazione è semplicissima ed io nella pagina vorrei visualizzare le colonne Products.ProductID, Vendors.Name e Products.Name. Con le colonne Products.ProductID, Products.VendorID e Products.Name mi riesce l'aggiornamento in tempo reale ma con il join non so come comportarmi.

    Grazie

    venerdì 3 ottobre 2014 06:42
  • Quando recuperi i prodotti hai incluso i Vendor:

     var products = db.Products.Include(p => p.Vendor);

    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    venerdì 3 ottobre 2014 07:57
    Moderatore
  • Il mio problema non è in realtà quello.

    Nel link d'esempio che ho postato c'è questo metodo simile che ho riadattato alla mia classe Products:

            public IEnumerable<Product> GetAllProducts()
            {
                List<Product> products = new List<Product>();
                using (var connection = new SqlConnection(connString))
                {
                    connection.Open();
                    using (SqlCommand command = new SqlCommand(@"SELECT ProductID, VendorID, Name FROM Products", connection))
                    {
                        command.Notification = null;
    
                        SqlDependency dependency = new SqlDependency(command);
                        dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
    
                        if (connection.State == ConnectionState.Closed)
                            connection.Open();
    
                        var reader = command.ExecuteReader();
    
                        while (reader.Read())
                        {
                            products.Add(new Product { ProductID = (long)reader["ProductID"], VendorID = (long)reader["VendorID"], Name = (string)reader["Name"] });
                        }
                    }
                }
                return products;
            }

    OK, così funziona. Quando modifico direttamente in SQL Management Studio il campo Products.Name anche la mia pagina web si aggiorna automaticamente.

    Ma cose si vede io visualizzo il campo Products.VendorID che è la FK, invece io vorrei visualizzare il campo Vendors.Name così da mostrare il nome del produttore invece che un ID che non dice nulla. So riscrivere la query per ottenere il campo ma poi nel while a cosa associo il campo se nella classe Products.cs non ho una variabile per Vendors.Name?

    Grazie

    venerdì 3 ottobre 2014 11:17
  • Il problema è che non viene valorizzata la proprietà Vendor del tuo oggetto Product.

    Considerando che carichi manualmente l'elenco di Prodotti (Product) dovresti aggiungere sempre manualmente il/i Vendor sul singolo Prodotto.

    In alternativa potresti crearti una nuova classe (ViewModel) che comprenda anche la descrizione del Vendor che utilizzi per la sola visualizzazione dell'elenco dei prodotti.

    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    lunedì 6 ottobre 2014 07:18
    Moderatore
  • Sono nuovo in questo genere di cose.

    In entrambi i modi come dovrei fare?

    lunedì 6 ottobre 2014 07:23
  • La cosa più semplice sarebbe per esempio:

     public IEnumerable<Product> GetAllProducts()
            {
                List<Product> products = new List<Product>();
                using (var connection = new SqlConnection(connString))
                {
                    connection.Open();
                    using (SqlCommand command = new SqlCommand(@"SELECT ProductID, VendorID, Name FROM Products", connection))
                    {
                        command.Notification = null;
    
                        SqlDependency dependency = new SqlDependency(command);
                        dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
    
                        if (connection.State == ConnectionState.Closed)
                            connection.Open();
    
                        var reader = command.ExecuteReader();
                        Product prod;
                        while (reader.Read())
                        {
    
                            prod = new Product { ProductID = (long)reader["ProductID"], VendorID = (long)reader["VendorID"], Name = (string)reader["Name"] };
                            prod.Vendor = ...; //Recupero il Vendor
                            products.add(prod);
                        }
                    }
                }
                return products;
            }

    In alternativa ti crei una classe ProductsViewModel.cs:

     public class ProductsViewModel
        {
            [DisplayName("ID Prodotto")]
            public long ProductID { get; set; }
    
            [DisplayName("Produttore")]
            public string VendorName { get; set; }
    
            [DisplayName("Prodotto")]
            public string Name { get; set; }
    
        }
    E poi modifichi il metodo GetAllProducts con 
     public IEnumerable<ProductsViewModel> GetAllProducts()
            {
                List<Product> products = new List<ProductsViewModel>();
                using (var connection = new SqlConnection(connString))
                {
                    connection.Open();
                    using (SqlCommand command = new SqlCommand(@"SELECT ProductID,Vendors.VendorId, Vendors.Name as VendorName, Name FROM Products Innser join Vendors on Products.VendorId=Vendors.VendorId", connection))
                    {
                        command.Notification = null;
    
                        SqlDependency dependency = new SqlDependency(command);
                        dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
    
                        if (connection.State == ConnectionState.Closed)
                            connection.Open();
    
                        var reader = command.ExecuteReader();
    
                        while (reader.Read())
                        {
                            products.Add(new ProductsViewModel { ProductID = (long)reader["ProductID"], VendorName = (string)reader["VendorName"], Name = (string)reader["Name"] });
                        }
                    }
                }
                return products;
            }
    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    lunedì 6 ottobre 2014 07:52
    Moderatore
  • Ciao,

    sto facendo dei tentativi ed intanto ho provato il secondo metodo e quando provo ad avviare il progetto mi dice che la colonna VendorName è inesistente.

    Mentre con il metodo più semplice non so come recuperare prod.Vendor.

    Che poi secondo te è possibile riscrivere il repository usando l'EntityFramework invece che scrivere la query o serve proprio all'oggetto SqlDependency?

    lunedì 6 ottobre 2014 08:49
  • Prova a dare uno sguardo qui:

    http://code.msdn.microsoft.com/How-to-use-SqlDependency-5c0da0b3

    http://weaklinglifter.blogspot.it/2014/02/sqldependencysqlcachedependency-with.html

    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    lunedì 6 ottobre 2014 09:05
    Moderatore
  • Per recuperare prod.Vendor come faccio?
    lunedì 6 ottobre 2014 10:11
  • Potresti recuperare l'elenco dei vendor e poi associarlo al prodotto tramite una query linq utilizzando la chiave VendorID.

    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    lunedì 6 ottobre 2014 11:21
    Moderatore
  • Ciao y_chen, con i suggerimenti dati da Luca hai risolto?
    Se si, ricordati di marcare le risposte che ti sono state utili.

    Andrea Dottor
    Microsoft MVP - ASP.NET/IIS
    http://blog.dottor.net

    martedì 14 ottobre 2014 14:34
    Moderatore
  • Diciamo che intanto ho trovato una soluzione parziale.

    Prendendo come esempio la tabella dbo.Devices ho aggiunto alla classe Device.cs delle variabili per i campi dbo.Models.Name e dbo.Suplliers.Name con l'attributo [NotMapped] così da non avere più l'errore della colonna non trovata. Così la classe Device.cs diventa:

        [Table("Devices")]
        public class Device
        {
            [Key]
            [DisplayName("ID Dispositivo")]
            public long DeviceID { get; set; }
    
            [DisplayName("ID Modello")]
            public long ModelID { get; set; }
    
            [DisplayName("ID Fornitore")]
            public long SupplierID { get; set; }
    
            [DisplayName("Numero di serie")]
            [StringLength(50)]
            [Required(AllowEmptyStrings = false, ErrorMessage = "E' richiesto un numero di serie")]
            public string SerialNumber { get; set; }
    
            [DisplayName("Codice fornitore")]
            [StringLength(50)]
            public string SupplierCode { get; set; }
    
            [DisplayName("Data di acquisto")]
            public DateTime DatePurchase { get; set; }
    
            public virtual Model Model { get; set; }
            public virtual Supplier Supplier { get; set; }
    
            [NotMapped]
            [DisplayName("Modello")]
            public string ModelName { get; set; }
    
            [NotMapped]
            [DisplayName("Fornitore")]
            public string SupplierName { get; set; }
        }

    E nel metodo GetAllDevices() all'interno del ciclo while ho una variabile a cui assegnare i valori estratti dal database:

    public IEnumerable<Device> GetAllDevices() { List<Device> devices = new List<Device>(); using (var connection = new SqlConnection(connString)) { StringBuilder query = new StringBuilder(); query.Append("SELECT dbo.Devices.DeviceID, dbo.Models.Name AS ModelName, dbo.Suppliers.Name AS SupplierName, "); query.Append("dbo.Devices.SerialNumber, dbo.Devices.SupplierCode, dbo.Devices.DatePurchase "); query.Append("FROM dbo.Devices "); query.Append("INNER JOIN dbo.Models ON dbo.Devices.ModelID = dbo.Models.ModelID "); query.Append("INNER JOIN dbo.Suppliers ON dbo.Devices.DeviceID = dbo.Suppliers.SupplierID"); connection.Open(); using (SqlCommand command = new SqlCommand(query.ToString(), connection)) { command.Notification = null; SqlDependency dependency = new SqlDependency(command); dependency.OnChange += new OnChangeEventHandler(dependency_OnChange); if (connection.State == ConnectionState.Closed) connection.Open(); var reader = command.ExecuteReader(); while (reader.Read()) { devices.Add(new Device { DeviceID = (long)reader["DeviceID"], ModelName = (string)reader["ModelName"], SupplierName = (string)reader["SupplierName"], SerialNumber = (string)reader["SerialNumber"], SupplierCode = (string)reader["SupplierCode"], DatePurchase = (DateTime)reader["DatePurchase"] }); } } } return devices; }

    Così sembra tutto perfetto. Aggiungo un record e mi compare, aggiorno un record e mi si aggiona subito anche nella pagina etc.

    Dico sembra per da notare che nella query uso un INNER JOIN, se usassi il LEFT JOIN (che in altre situazioni ne ho bisogno perché se no mi perdo dei recordi che hanno campi NULL) l'aggiornamento in tempo reale già non funziona e stessa cosa se per esempio cambio la colonna dbo.Devices.SupplierCode a NULL.

    Quindi problema risolto a metà, ho ancora questi problemi legati a JOIN ed ai valori NULL.



    • Modificato y_chen giovedì 16 ottobre 2014 14:15
    giovedì 16 ottobre 2014 14:12
  • Prora a lasciare la left join e a mettere ISNULL(campo,'') as campo sui campi che potrebbero essere null.

    Ciao


    Luca Congiu (congiuluc)
    Personal Blog: blog.dotnetcode.it

    Se hai trovato la soluzione all'interno del Forum, ricorda di segnalare il post come risposta, in alternativa puoi postare la soluzione da te adottata. Questo aiuterà altri utenti, che hanno riscontrato la stessa problematica, ad identificare rapidamente la soluzione/risposta corretta.

    giovedì 16 ottobre 2014 15:14
    Moderatore
  • Indagando in giro credo di aver campito perché non funziona.

    http://technet.microsoft.com/en-us/library/ms181122%28v=sql.105%29.aspx

    Nel link, fra le caratteristiche della query per SqlDependency, c'è scritto:

    The statement must not contain subqueries, outer joins, or self-joins

    Quindi semplicemente il sistema non funziona con i LEFT JOIN.

    Beh, per la situazione mia ho creato degli utenti fittizzi a cui assegno tutti i messaggi che arrivano da numeri sconosciuti.

    Grazie dell'aiuto

    venerdì 17 ottobre 2014 12:49