/Faxback/Interface/Interfaces.cs
C# | 232 lines | 101 code | 30 blank | 101 comment | 5 complexity | 268e3c78a4aa864cb42deffd16d1df48 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using FaxbackModel.Implementation.Logging;
- namespace Faxback.Interface
- {
- /// <summary>
- /// An interface for starting/stopping the emission of faxes.
- /// </summary>
- public interface IObservableFaxProcess
- {
- /// <summary>
- /// Initiates a process/thread.
- /// </summary>
- void Execute();
- void EnableProcess();
- /// <summary>
- /// Interrupts a process/thread.
- /// </summary>
- void InterruptProcess();
- /// <summary>
- /// Instance of a subject delegate in the subject/observer pattern. The implementer of this interface is the subject, those who call 'Subscribe' on this IReporter are the observers.
- /// </summary>
- IReporter Reporter { get; set; }
- /// <summary>
- /// Whether the observable process is still running.
- /// </summary>
- bool IsRunning { get; set; }
- }
- public class Message
- {
- public string Name { get; set; }
- public string Content { get; set; }
- public DateTime TimeStamp { get; } = DateTime.UtcNow;
- public static Func<Message, bool> MessageWithName(string name) => (m) => m.Name == name;
- public static Func<Message, bool> MessageWithNames(params string[] names) => (m) => names.Contains(m.Name);
- public override string ToString() => $"({Name}, {Content ?? "NULL"} at {TimeStamp.ToString()})";
- public static implicit operator Message((string Name, string Content) rhs)
- {
- return new Message { Name = rhs.Name, Content = rhs.Content };
- }
- }
- /// <summary>
- /// Subject delegate in the subject/observer pattern.
- /// </summary>
- public interface IReporter : IObservable<Message>
- {
- /// <summary>
- /// Update all observers with a new array of keys, so that they can act accordingly.
- /// </summary>
- /// <param name="messages"></param>
- void Update(params Message[] messages);
- }
- public class MultiChannelReporter : IReporter
- {
- /// <summary>
- /// Dictionary with the following hierarchy:
- /// Name of channel : {
- /// List of channel observers,
- /// Last refreshed message on channel
- /// }
- /// </summary>
- List<(string name, (List<(IObserver<Message> observer, bool picky)> pickyObservers, Message state) channel)> _namedChannels;
-
- public MultiChannelReporter(params string[] channelNames)
- {
- this._namedChannels =
- channelNames.Select(
- _ =>
- (
- // key is simply the string name itself
- name: _,
- // value is a tuple of message-observers list and last known message state.
- channel : (
- pickyObservers: new List<(IObserver<Message> observer, bool picky)>(),
- state: new Message { Name = _ }
- )
- )
- ).ToList();
- }
- public IDisposable Subscribe(IObserver<Message> observer) => Subscribe(observer, false, ((m) => true));
- public IDisposable Subscribe(IObserver<Message> observer, bool isPicky, Func<Message, bool> observationCriteria)
- {
- var subChannels = this._namedChannels
- .Where(k => observationCriteria(k.channel.state))
- .Select(k => k.channel)
- .ToList();
- subChannels.ForEach(s => s.pickyObservers.Add((observer, isPicky)));
- return new Unsubscriber<Message>(subChannels.Select(s => s.pickyObservers.Select(p => p.observer).ToList()), observer);
- }
- public void Update(params Message[] messages)
- {
- // update the necessary messages
- messages.ToList().ForEach(msg => {
- this._namedChannels
- .ToList()
- .ForEach(nch =>
- {
- if (msg.Name == nch.name)
- {
- var messageChanged = nch.channel.state.Content != msg.Content;
- nch.channel.state.Content = msg.Content;
- notifyNonPickys(nch, messageChanged);
- }
- });
- });
- }
- private static void notifyNonPickys((string name, (List<(IObserver<Message> observer, bool picky)> observers, Message state) channel) nch, bool messageChanged) =>
- nch.channel.observers
- .Where(obs => !obs.picky || messageChanged)
- .ToList()
- .ForEach(obs =>
- obs.observer.OnNext(nch.channel.state));
- }
- /// <summary>
- /// A client interface for emitting faxes, and updating state of a generic fax type with information on the state of the fax job.
- /// </summary>
- /// <typeparam name="TFax">A generic fax type.</typeparam>
- public interface IFaxClient<TFax>
- {
- /// <summary>
- /// Attempts to emit a generic fax.
- /// </summary>
- /// <param name="fax">A generic fax type.</param>
- void Send(TFax fax);
- /// <summary>
- /// Attempts to update the status regarding a generic fax.
- /// </summary>
- /// <param name="fax">A generic fax type.</param>
- void UpdateStatus(TFax fax);
- }
- /// <summary>
- /// Serves up a stream of generic objects types matching true for some a boolean query criterion (optional, results to (e) => true otherwise).
- /// </summary>
- /// <typeparam name="TQueryable">A generic queryable type.</typeparam>
- public interface IQueryableStream<TQueryable>
- {
- /// <summary>
- /// Retrieves the next generic queryable type from the stream matching the input expression.
- /// </summary>
- /// <param name="expression">A generic queryable type => boolean expression explaining which queryables may be picked up through this interface.</param>
- /// <returns>The next available generic queryable matching the input expression.</returns>
- TQueryable Next(Expression<Func<TQueryable, bool>> expression = null);
- /// <summary>
- /// Checks if there are any available generic queryables in the stream.
- /// </summary>
- /// <param name="expression">A generic queryable type => boolean expression explaining which queryables may be picked up through this interface.</param>
- /// <returns>True if there is an available generic queryable in the stream, false otherwise.</returns>
- bool HasNext(Expression<Func<TQueryable, bool>> expression = null);
-
- /// <summary>
- /// Throws away changes to the currently checked out record.
- /// </summary>
- void DiscardChanges();
- /// <summary>
- /// Number of elements in the current queryable stream
- /// </summary>
- int Count { get; }
- }
- /// <summary>
- /// A generic data-service for interfacing with a Data Access Layer to get lists of objects.
- /// It takes into account that new objects may be added to the Data Access Layer by other processes over
- /// time, and provides a means of refreshing the list accordingly. It does not however, provide any
- /// means of collision control between two entities trying to write to the DAL simultaneously.
- /// </summary>
- /// <typeparam name="TData">The generic type to be buffered by this data service.</typeparam>
- public interface IBufferedList<TData> : IList<TData>
- {
- /// <summary>
- /// Saves the data currently stored in the object back to the data access layer.
- /// </summary>
- void SaveBuffer();
- /// <summary>
- /// Saves objects in the buffered list which meet the predicate
- /// </summary>
- /// <param name="predicate">A control for whether an object should be saved or not.</param>
- void SaveBuffer(Func<TData, bool> predicate);
- /// <summary>
- /// Introduces elements into this BufferedList meeting the predicate criterion, And returns the subset of elements that matched, but not necessarily are "new" to the list itself.
- /// </summary>
- /// <param name="predicate">The filtering expression for objects that should </param>
- /// <returns></returns>
- void Refresh(Func<TData, bool> predicate);
- }
- //public interface IBufferedList<TData> : IList<TData>
- //{
- // /// <summary>
- // /// Saves the data currently stored in the object back to the data access layer.
- // /// </summary>
- // void SaveBuffer();
- // /// <summary>
- // /// Saves objects in the buffered list which meet the predicate
- // /// </summary>
- // /// <param name="predicate">A control for whether an object should be saved or not.</param>
- // void SaveBuffer(string predicate);
- // /// <summary>
- // /// Introduces elements into this BufferedList meeting the predicate criterion, And returns the subset of elements that matched, but not necessarily are "new" to the list itself.
- // /// </summary>
- // /// <param name="predicate">The filtering expression for objects that should </param>
- // /// <returns></returns>
- // void Refresh(string predicate);
- //}
- }