Come gestisci oggetti che richiedono un comportamento personalizzato e devono esistere come entità nel database?

1

Per un semplice esempio, supponi che la tua applicazione invii notifiche agli utenti quando accadono vari eventi. Quindi nel database potrei avere le seguenti tabelle:

TABLE Event
    EventId uniqueidentifier
    EventName varchar

TABLE User
    UserId uniqueidentifier
    Name varchar

TABLE EventSubscription
    EventUserId
    EventId
    UserId

Gli eventi stessi sono generati dal programma. Pertanto, nell'applicazione sono presenti punti codificati in cui viene generata un'istanza dell'evento e devono essere notificati a tutti gli utenti iscritti.

Pertanto, l'applicazione stessa non modifica la tabella Event , tranne durante l'installazione iniziale e durante un aggiornamento in cui potrebbe essere creata una nuova Event .

Ad un certo punto, quando viene generato un evento, l'applicazione deve cercare Event e ottenere un elenco di User s. Qual è il modo migliore per collegare l'evento nel codice sorgente all'evento nel database?

Opzione 1:

Memorizza EventName nel programma come costante fissa e cerca il nome.

Opzione 2:

Memorizza EventId nel programma come Guid statico, e cerca per ID.

Credito extra

In altre circostanze simili, potrei voler includere un comportamento personalizzato con il tipo di evento. Cioè, voglio sottoclassi della mia classe di entità Event con comportamenti diversi, e quando cerco un evento, voglio che restituisca un'istanza della mia sottoclasse.

Ad esempio:

class Event
{
    public Guid Id { get;  }
    public Guid EventName { get;  }
    public ReadOnlyCollection<EventSubscription> EventSubscriptions { get; }
    public void NotifySubscribers()
    {
        foreach(var eventSubscription in EventSubscriptions)
        {
            eventSubscription.Notify();
        }
        this.OnSubscribersNotified();
    }
    public virtual void OnSubscribersNotified() {}
}

class WakingEvent : Event
{
    private readonly IWaker waker;
    public WakingEvent(IWaker waker)
    {
        if(waker == null) throw new ArgumentNullException("waker");
        this.waker = waker;
    }

    public override void OnSubscribersNotified()
    {
        this.waker.Wake();
        base.OnSubscribersNotified();
    }
}

Quindi, questo significa che devo mappare WakingEvent a qualsiasi chiave che sto usando per cercarlo nel database. Diciamo che è il EventId . Dove immagazzino questa relazione? Va nella classe di repository degli eventi? Il WakingEvent conosciuto dovrebbe dichiarare il proprio ID in un membro statico o in un metodo?

... e poi, è tutto indietro? Se tutti gli eventi hanno una sottoclasse, invece di recuperare gli eventi per ID, dovrei chiedere al mio repository il WakingEvent come questo:

public T GetEvent<T>() where T : Event
{
    ... // what goes here?
}

Non posso essere il primo ad affrontarlo. Qual è la migliore pratica?

    
posta Scott Whitlock 08.11.2012 - 18:01
fonte

2 risposte

2

Presumo che i dati in Event siano veramente statici, non dinamici. Se questo è il caso, un enum potrebbe rappresentare i tipi di evento. Se possibile, sia il nome che l'ID corrispondono a quelli del database. Almeno 1 dovrebbe corrispondere.

enum EventType
{
    Waking = 1, //it would be nice if both enum's name/int matched DB name/ID
    Sleeping,
    FooBar
}

Per ottenere l'oggetto Event passare un enum a un metodo factory.

IEvent getImp(EventType type)
{
    IEvent imp = null;

    switch (type)
    {
        case EventType.Waking:
            imp = new WakingEvent();
            break;
        case EventType.Sleeping:
            imp = new SleepingEvent();
            break;
        case EventType.FooBar:
            imp = new FooBarEvent();
            break;
    }
    return imp;
}

Potrebbe sembrare "sbagliato" perché tutto è codificato nell'app. Ma questo presuppone che il comportamento non possa essere guidato dai dati. Il comportamento complesso casuale non può essere definito come dati a meno che il dato stesso non sia un codice imperativo.

    
risposta data 08.11.2012 - 19:06
fonte
2

No questo è un sottosistema comune di applicazioni più grandi, lo chiamo Avvisi e Notifiche. Le entità Event e Subscriber che hai citato sono un punto di partenza.

Qui abbiamo un semplice diagramma per il nucleo del sistema. Hai un EventType, che descrive solo i vari eventi che possono essere generati dal sistema. Un abbonamento (che potrebbe aver sospeso diversi tipi di abbonamento, ad esempio e-mail, SMS, ecc.). Quando viene generato un evento, il sistema controlla il tipo di quell'evento e genera un avviso per ogni abbonamento.

La parte difficile è come hai detto tu, come creare una gerarchia di eventi. Il tipo di evento è ciò che è noto nel mondo della modellazione degli oggetti come descrittore. I descrittori hanno una conoscenza intrinseca di come generare il tipo di oggetto che stanno descrivendo. Cioè, agiscono come fabbriche di oggetti. Abbiamo utilizzato un trucco ORM in cui abbiamo archiviato il nome completo delle sottoclassi Event nella tabella EventType per ciascun tipo. Pertanto, quando abbiamo avuto un'istanza di un EventType, restituirebbe la sottoclasse dell'Evento tramite Reflection.

La maggior parte degli O / RM supporta il mapping di una gerarchia di classi in un database (almeno nel mondo .NET).

C'è molto di più che si può dire sull'implementazione di un sistema di allerta e notifica. Ma lascerò aperta la discussione per chiedere eventuali domande di follow-up.

    
risposta data 08.11.2012 - 19:26
fonte

Leggi altre domande sui tag