Signum Framework Logo
"Open framework that encourages convention over configuration, using C# code,
not XML files, to model at the right level of abstraction and achieve deadlines.
...but also has a full Linq provider, and syncs the schema for you!"
Login
RSS

Search

»



Main
Index
Map
Videos
Download
Source code
Tutorials
Forum
FAQ



Image



PoweredBy

ServerProxy

Server static class is the main class for the client application to communicate with the server. Internally it's just a holder of your Server WCF TransparentProxy.

It's your responsibility to plug a WPF TransparentProxy that implements IBaseServer into Server static class using SetNewServerCallBack as we seen in SettingUpClientConnection

Unfortunately, because WPF Transparent Proxies go to a corrupt state when a communication exception happens and they become unusable, the Server needs a way to restore communication. Two kind of exceptions could happend:

  • Session Expired: In this case the most convenient is to reconnect and retry the operation that causes the error. Maybe showing a log-in windows if necessary.
  • Any Unexpected Exception: Abort and show to the user.

The ability to auto-retry when the session has expired (only!), and to handle the corruption of the communication channel was left to the user in previous versions of the framework, while in the new one is already implemented.

In order to implement this features we require it to provide two lambdas:
  • GetServer: Globally set at the beginning of your application, tells Server class what are the necessary steps to connect to the server when necessary.
  • An action that performs the operation (Execute) or returns the values (Return) on the server side, like this:

Server.Return((IBaseServer s)=>s.Retrieve(typeof(PersonDN), id)); 
Server.Execute((MyServers)=>s.DeletePerson(id)); 


By using this syntax we tell Server what to do in a lambda, so he is free to retry in the case of a session expiration. Let's see how it is implemented in the server.

    public static class Server
    {
        static Func<IBaseServer> getServer;
        static IBaseServer current;
        
        public static void SetNewServerCallback(Func<IBaseServer> server)
        {
            getServer = server;
        }

public static void Connect() { if (current == null || (current is ICommunicationObject) && ((ICommunicationObject)current).State == CommunicationState.Faulted) current = getServer();

if (current == null) throw new InvalidOperationException(Properties.Resources.AConnectionWithTheServerIsNecessaryToContinue); }

static void HandleSessionException(MessageSecurityException e) { MessageBox.Show(Properties.Resources.SessionExpired, Properties.Resources.SessionExpired, MessageBoxButton.OK, MessageBoxImage.Hand); }

public static R Return<S, R>(Func<S, R> function) where S : class { retry: Connect();

S server = current as S; if (server == null) throw new InvalidOperationException(Properties.Resources.Server0DoesNotImplement1.Formato(current.GetType(), typeof(S)));

try { return function(server); } catch (MessageSecurityException e) { HandleSessionException(e); current = null; goto retry; } }

//Similar to Return public static void Execute<S>(Action<S> action);

(...) }

IBaseServer overloads

Also, since implementing IBaseServer is mandatory, it contains some convenient generic overloads (some of them extension methods) that already implement the lambda trick to make them repeatable, making server class the equivalent to Database class when you are in the Client side.

    public static class Server
    {
        (...)

public static T Save<T>(this T entidad) where T : IdentifiableEntity { return (T)Return((IBaseServer s)=>s.Save(entidad)); }

public static IdentifiableEntity Save(IdentifiableEntity entidad) public static T Retrieve<T>(int id) where T : IdentifiableEntity public static IdentifiableEntity Retrieve(Type type, int id) public static IdentifiableEntity RetrieveFromLazyAndRemember(Lazy lazy) public static T RetrieveFromLazyAndRemember<T>(this Lazy<T> lazy) where T : class, IIdentifiable

public static IdentifiableEntity RetrieveFromLazyAndForget(Lazy lazy) public static T RetrieveFromLazyAndForget<T>(this Lazy<T> lazy) where T : class, IIdentifiable public static List<T> RetrieveAll<T>() public static List<IdentifiableEntity> RetrieveAll(Type type)

public static List<Lazy<T>> RetrieveAllLazy<T>() where T : class, IIdentifiable public static List<Lazy> RetriveAllLazy(Type type) public static List<Lazy> FindLazyLike(Type type, string subString, int count)

public static List<T> SaveList<T>(List<T> list) where T: IdentifiableEntity

(...) }

Note the different overloads of RetrieveFromLazy: RetrieveFromLazyAndRemember stores the retrieved entity in the lazy entity reference (making the lazy fat), while RetrieveFromLazyAndForget returns the entity without actually storing it. It's client behaviour, there's just one server implementation

Convert

Finally, there's a pair of methods, Convert and CanConvert, that simplify using lazy objects and real entities indistinctly, building a lazy around our entity or retrieving the real entity from server when needed.

    public static class Server
    { 
        (...)   
 
        public static object Convert(object obj, Type type)
        public static bool CanConvert(object obj, Type type)      
    }
Creative Commons License Signum Framework Site by Signum Software is licensed under a Creative Commons Attribution 3.0 License.
Powered by ScrewTurn Wiki version 3.0.5.600.