SignalR + EF - automatyczne pobieranie swiezych danych z DB

0

Hej,

Znalazlem całkiem spoko kursik jak zrobić notyfikacje SignalR z bazy danych: http://venkatbaggu.com/signalr-database-update-notifications-asp-net-mvc-usiing-sql-dependency/

Chciałbym teraz trochę to pozmieniać i użyć LINQ i podejścia DB first czyli wygenerować ten Model EDMX. Zrobiłem to, mogę robić zapytania i dostaję wynik. Jednak nie działa mi automatyczne odświerzanie jeśli w bazie się coś nowego pojawi...

W tym kodzie mam takiego coś:

  private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                MessageHub.SendMessages();
            }
        }

i jest to wywołane w metodzie GetAllMessages w MessageRepository. Nie umiem tego zastosować aby działało z tym EDMX - nie wiem jak to zrobić. W kontrolerze robię to tak:

var repo = new MessageRepository();
            var data = repo.GetAllMessages();

i ten obiekt "data" wysyłam do widoku. to działa jak uzywam SqlCommand w repozytorium. Ale nie działa jak zaczynam używać tego EDMX. Proszę o pomoc.

0

http://www.codeproject.com/Articles/496484/SqlDependency-with-Entity-Framework

Który moment konkretnie nie działa?
Do handlera dependency_OnChange wpada cokolwiek w przypadku EF?

0

W przykładzie który podałeś (i przejrzałem go) oni uzywają Code First, a ja chce polecieć z tego modelu EDMX. W ogóle odpaliłem ten kod i to nie działa. Dodałem do DB jakieś wpisy i nic mi nie zgłosił...

Inaczej może. Żebym to mógł łatwiej zrozumieć.
Obecnie (code first) mam tak w projekcie:
klasa Raport (tabela w bazie).
potem mam DatabaseContext:

 public class DatabaseContext: DbContext
    {
        public DatabaseContext()
            : base("DefaultConnection")
        {
        }
   
        public DbSet<Raport> Raports{ get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

potem mam klase DatabaseInitializer w której mam tylko metodę Seed. I potem mam jeszcze klasę RaportRepository:

 public IEnumerable<Raport> GetAllRaports()
        {
            IList<Raport> raports;

            using (var db = new InformationSystemEntities())
            {
                db.Database.Connection.Open();
                raports = (from s in db.Raports
                    select s).ToList();
                db.Database.Connection.Close();
                return raports;
            }
        }

i teraz jak mam przerobić to wszystko aby nie używać Code First, tylko używać Database First oraz tego modelu (czy jak to się nazywa) typu EDMX ?

EDIT
teraz mam taki kod:

  public static IEnumerable<Raport> GetRaports()
        {
            ObjectSet<Raport> objectSet = null;
            IList<Raport> raportData = HttpContext.Current.Cache.Get("Raport") as IList<Raport>;
            if (raportData == null)
            {
                using (var context = new InformationSystemEntities())
                {
                    IQueryable<Raport> categoryDataCache = context.Raports;
                    using (SqlConnection connection = new SqlConnection(connectionString.ProviderConnectionString))
                    {
                        connection.Open();
                        var db = new DatabaseContext();
                        ObjectContext objectContext = ((IObjectContextAdapter)db).ObjectContext;
                        objectSet = objectContext.CreateObjectSet<Raport>("Raports");
                        connection.Close();
                      
                   /*     SqlCommand command = new SqlCommand(((ObjectQuery)categoryDataCache).ToTraceString(), connection);
                        SqlCacheDependency dependency = new SqlCacheDependency(command);
                        raportData = categoryDataCache.ToList();
                        HttpContext.Current.Cache.Insert("Raport", raportData, dependency);
                        command.ExecuteNonQuery();
                    */
                    }
                }
            }

            return objectSet;

i to też nie reaguje na zmianę w bazie.

0

Możesz jeszcze spojrzeć na to: https://code.msdn.microsoft.com/How-to-use-SqlDependency-5c0da0b3#content
Może w czymś pomoże.
Co do DB-first, Code-first, jak się zagłebisz np w Model.edmx -> Model.Context.tt -> Model.Context.cs, to tam znajdziesz DbContext wygenerowany z EDMXa, więc nie powinno być różnicy , bo z tego co patrzę po tych przykładach, potrzebny jest tylko DbContext, a ten mamy w obu podejściach.

0

Miałbyś jakiś pomysł aby zamiast tabeli używać widoku ?
bo jak odpalam to pierwszym sposobem (code first, który działa) na tabeli to dostaje od razu powiadomienie, natomiast jak uzywam widok z DB to wtedy to w ogóle nie działa.
Różnica jaka jest to w tym miejscu:

 private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                MessageHub.SendMessages();
            }
        }
  1. Dla tabeli:
    przy pierwszym uruchomieniu, nie dostaje nic. Natomiast jak zmienie w bazie danych to mi tu debugger staje i mam w tym "e" że jest to "Change".
  2. dla widoku:
    przy pierwszym uruchomieniu od razu staje debuger i w tym "e" jest "subscribe" i potem jak dodam rekord do tabeli/widoku to niestety nie reaguje na zminę.

dlaczego? jak to poprawić?

0

http://msdn.microsoft.com/en-us/library/ms181122.aspx

The statement must not reference a view.

Tak, więc dla widoku nie wiem czy da rade coś wykombinować.

0

To też nie jest to co ja potrzebuje. Ja potrzebuje używać EDMX!!!!

0

Zobacz, mam takie coś:

   public IEnumerable<Raport> GetAllMessages1()
        {
            var messages = new List<Raport>();
            using (var connection = new SqlConnection(_connString))
            {
                connection.Open();
                using (var command = new SqlCommand(@"SELECT [Id], [What]  FROM [dbo].[Raport]", connection))
                {
                    command.Notification = null;

                    var dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);

                    if (connection.State == ConnectionState.Closed)
                        connection.Open();

                    var reader = command.ExecuteReader();

                    while (reader.Read())
                    {
                        messages.Add(item: new Raport
                        {
                            Id= (int)reader["Id"], 
                            What = (string)reader["What"],
                            Where = "",//(string)reader["Where"], 
                            Project = "",//(string)reader["Project"],
                            Who = "",//(string) reader["Who"],
                            When = DateTime.Now,// Convert.ToDateTime(reader["When"]),
                            How = "",//(string)reader["How"],
                            Asset = "",//(string)reader["Asset"],
                            Name = "",//(string)reader["Name"],
                            Item = "",//(string)reader["Item"],
                            ItemId = "",//(string)reader["ItemId"],
                            Notes = ""//(string)reader["Notes"]
                        });
                    }
                }

            }
            return messages;


        }

i jak teraz uzyc tutaj tego z EDMX ? jak mam wsadzic ten context z EDMX ?

0

W załączniku możliwie prosty przykład przerobiony z tego co podałem w pierwszym linku. Z użyciem EDMXa, SignalR już nie implementowałem, bez bawienia się ze SqlReaderem i bez własnoręcznego pisania zapytań.

0

Nie wiem czy to dobrze działa... Odpaliłem i po każdym wstawieniu nowego wiersza do DB on mi zwraca wszystkie rekordy...a nie tylko te nowe wiec pytanie czy dało by się to jakoś dorobić?

A mam pytanie do Ciebie jeszcze: jakbym chciał tego użyć w aplikacji internetowej, to gdzie to powinienem powsadzać? no bo jak wyświetle strone no to tam nie ma nic takiego co jakby "czeka" na sygnal z bazy danych ze sie cos zmienilo... a w tym programie konsolowym to tam masz jakby pętle w ktorej to siedzi. Jak to zrobić a apce webowej?

To co mam teraz jest tutaj:
Baza danych nazywa sie "InformationSystem" tabela "Raport".
http://www.filedropper.com/informationsystemtest

0

Wrzucenie fragmentu

using (var notifier = new EntityChangeNotifier<Raport, InformationSystemEntities>(x => x.Id > 0))
            {
                notifier.Changed += (sender, e) =>
                {
                    IMessageHub<IEnumerable<Raport>> hub = new MessageHub();
                    hub.SendMessages(e.Results);

                    foreach (var et in e.Results)
                    {
                        raports.Add(et);
                    }
                };
            }
 

do Application_Start w global.asax powinno rozwiązać sprawę.
Pytanie co chcesz z tym dalej zrobić. Jak SignalR to rozumiem, że wyświetlać na bieżąco na jakiejś stronie.
W tym przypadku piszesz klienta w jQuery, który będzie dodawał nowe rekordy albo wszystkie je odświeżał.

Do tego, by nie pobierało wszystkich rekordów, tylko nowe. Zdaje się, informacja jest tylko o tym że coś zostało dodane, zmienione w tabeli, nie ma co konkretnie. Można oczywiście śledzić ręcznie te zmiany, jednak zawsze dostaniesz całą tabelę. Można ew. pokombinować z fragmentem

 
Results = GetCurrent()

dopisać metodę w stylu

 
Results = GetCurrentLatest()

i dopisać kod by tylko pobierało np. tylko najnowszy rekord.

0

ale jak dodam to do appolication_start to co mam zrobic z tą lista "reports" ? gdzie mam ją jakby przekazac z applicationstart? bo to jest funkcja typu void ona nic nie zwraca....

0

Dostajesz tę listę w handlerze jako e.Results, przy każdej zmianie w tabeli. Z tego miejsca możesz ją przekazać, gdzie chcesz w zależności co chcesz dalej osiągnąć.
Przykład to np. wysłanie jej przez huba od SignalR, i na jakiejś stronie masz klienta w jQuery, który odbiera tę listę i odświeża widok na danej stronie z nowymi danymi.

1 użytkowników online, w tym zalogowanych: 0, gości: 1