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

Introduction

Signum.Service is the folder in Signum.Entities that contains the WCFService Contracts of the server required by Signum.Windows.

In SF 2.0 we have merged Signum Services into Signum Entities to simplify the architecture. This contracts are not needed for Web Applications thought.

The great benefit of Signum.Windows is that it provides controls that allow working with entities in a generic way, reducing dramatically the amount of code you have to write. For this to work the server has to provide a generic way of saving and retrieving any entity, this way is defined in IBaseServer interface.

There are other useful contracts:

  • IQueryServer: necessary for Search.
  • IAlertsServer: for Alerts widget support.
  • INotesServer: for Notes widget support

ILoginServer and IExcelReportServer has been moved out to Signum.Extensions (not public yet).


All of these interfaces share a common philosophy. They use NetDataContractAttribute.

NetDataContractAttribute is like WCF's Cinderella. A hidden gem completely despised by Microsoft. It allows SharedType communication between client and server. That means that the same types are going to be used on both sides of the channel, using old binary or xml soap serialization, so the assembly where these entities are defined has to be available on both sides as well.

No need to generate a proxy class for your services, neither for the entities being sent, it behaves like old Remoting.

Microsoft is trying to discourage this approach because it tightly couples client and server: they have to use the same technology (.Net Framework) and have to evolve together. They are not wrong, but we were already prepared for that:
  • We are using .Net on both sides
  • Almost any change in an Entitiy will affect client and server (i.e. Validation). Very often it will impact Database also (adding field) and the UI (adding an imput box). So they were indirectly coupled already.

If you are looking for a practical example of how to write the Server, look at Tutorial: Writing the Server Code

Let's focus on all these interfaces.

IBaseServer

IBaseServes is the only mandatory interface you need to implement in the server in order to use Signum.Windows on the client side. It provides general purpose operations for Saving and Retrieving mainly used by Entity Controls and Entity Windows.

[ServiceContract(SessionMode = SessionMode.Required)]
public interface IBaseServer
{
    //Is used when you need to navigate to an entity, or when you need to retrieve a Lazy. 
    [OperationContract, NetDataContract]
    IdentifiableEntity Retrieve(Type type, int id);

//Used by [EntityWindows|NormalWindow] to save an entity. [OperationContract, NetDataContract] IdentifiableEntity Save(IdentifiableEntity entidad);

//Retrives all the entities of a given type. Expensive operation, used by AdminWindows for administrating simple types with few instances on the database. [OperationContract, NetDataContract] List<IdentifiableEntity> RetrieveAll(Type type); //Saves all the entities in a list. Used by AdminWindows when clicking save. [OperationContract, NetDataContract] List<IdentifiableEntity> SaveList(List<IdentifiableEntity> list);

//Retrieves the lazy version of all the entities of a given type, used by EntityCombo. When the EntityCombo has Implementations, types is filled. [OperationContract, NetDataContract] List<Lite> RetrieveAllLite(Type liteType, Type[] types);

//Retrieves the lazy version of the top ''count'' entities of a given type which ToStr looks like the subString provided, used by EntityLine when auto-complete is enabled. //When the EntityList has Implementations, types parameter is set. [OperationContract, NetDataContract] List<Lazy> FindLiteLike(Type liteType, Type[] types, string subString, int count);

//Given the type of an IdentitiableEntity, and path of MemberInfo (presumably PropertyInfo) returns the Type[] //with the different implementations of the ImplementedBy reference the path points to, otherwise null. [OperationContract, NetDataContract] Type[] FindImplementations(Type type, MemberInfo[] implementations);

//Return the list of all the server types (entity controls use it to avoid calling FindImplementations). New in SF 2.0 [OperationContract, NetDataContract] Dictionary<Type, TypeDN> ServerTypes();

//Returns DateTime.Now in the server. Handy for custom client logic. New in SF 2.0 [OperationContract, NetDataContract] DateTime ServerNow();

//Returns the registered types in the server that are assignable to 'type'. Handy for custom client logic. New in SF 2.0 [OperationContract, NetDataContract] List<Lite<TypeDN>> TypesAssignableFrom(Type type); }

Implementing these methods is trivial, most of them have an equivalent with the same name in Database class. All but FindLazyLike and RetrieveAllLazy have an implementation on DynamicQueryUtils, and you can find FindImplementations on your current Schema.

In SF 2.0 you can find a ServiceBasic class that already implements IBaseServer, IQueryServer, INotesServer and IAlertsServer

It's up to you to add any Security, error logging or trace performance on the Service implementation.

Trick: If you are planning to repeat this additional authorization/logging/tracing code on any Service operation, instead you can refactor the code using lambdas this way:

  [ServiceContract(SessionMode = SessionMode.Required)]
  public interface IMyCustomServer: IBaseServer
  {
      
  }

public class ServerBugs : IMyCustomServer { #region Internal Methods protected T Return<T>(MethodBase mi, Func<T> function) { return Return(mi, mi.Name, function); } protected virtual T Return<T>(MethodBase mi, string description, Func<T> function) { try { return function(); } catch (Exception e) { throw new FaultException(e.Message); } }

protected void Execute(MethodBase mi, Action action) { Return(mi, mi.Name, () => { action(); return true; }); }

protected void Execute(MethodBase mi, string description, Action action) { Return(mi, description, () => { action(); return true; }); } #endregion

public IdentifiableEntity Retrieve(Type type, int id) { return Return(MethodInfo.GetCurrentMethod(), "Retrieve {0}".Formato(type.Name), () => Database.Retrieve(type, id)); }

public IdentifiableEntity Save(IdentifiableEntity entidad) { return Return(MethodInfo.GetCurrentMethod(), "Save {0}".Formato(entidad.GetType()), () => { Database.Save(entidad); return entidad; }); }

public List<Lite> RetrieveAllLite(Type liteType, Type[] types) { return Return(MethodInfo.GetCurrentMethod(), "RetrieveAllLite {0}".Formato(liteType), () => AutoCompleteUtils.RetriveAllLite(liteType, types)); }

(...) }

IQueryServer

IQueryServer interface contains the main methods you need to implement in order to enable search windows in the client application.

    [ServiceContract]
    public interface IQueryServer
    {
        //returns the QueryDescription (columns and filter names and types).
        [OperationContract, NetDataContract]
        QueryDescription GetQueryDescription(object queryName);

//returns the QueryResult (actual data) given a queryName, some filters, and an optional limit to reduce the number of results. [OperationContract, NetDataContract] QueryResult GetQueryResult(object queryName, List<Filter> filters, int? limit);

//Used by CountSearchControl [OperationContract, NetDataContract] int GetQueryCount(object queryName, List<Filter> filters);

//Handy to retrieve an associated entity using a view. Used bu FindUnique [OperationContract, NetDataContract] Lite GetUniqueEntity(object queryName, List<Filter> filters, UniqueType uniqueType);

//Return the names of all the available queries in the server. [OperationContract, NetDataContract] List<object> GetQueryNames(); }

As you see, object (not string) is used for queryName. This is very convenient since we use the System.Type to identify the default view used for a type, and you can define a enum type, shared by client and server, for all the other special views so you don't have to rely on nasty string to identify views.


DynamicQueryManager class is designed to be the back-end implementation of these methods. Usually we fill it in your starter method like this:

public static class Starter
{   
   //Call this method when start application in Global.asax, after Connection.Default is set-up
   public static void Start()
   {
       SchemaBuilder sb = (...)
       DynamicQueryManager dqm = new DynamicQueryManager();
        
       dqm[typeof(ProjectDN)] = from p in Database.Query<ProjectDN>()
                                 select new
                                 {
                                    Entity = p.ToLazy(),
                                    p.Name,
                                    p.IsInternal
                                 };
      
      //Add as many queries as you want
      ConnectionScope.Default = new Connection(connectionString, sb.Schema, dqm); 

} }

As you see, DynamicQueryManager is meant to be implemented with Linq to Signum queries. It is possible to make an implementation that works with plain sql views but we discourage it so we don't provide it: While you stay writing Linq to Signum queries you get refactoring and compile time checking of your views, making it easier for your application to grow and evolve. Also you have Lite objects support.

Then, implementing the interface is as easy as:

    public QueryDescription GetQueryDescription(object queryName)
    {
        return Return(MethodInfo.GetCurrentMethod(),
        () => DynamicQueryManager.Current.QueryDescription(queryName));
    }

public QueryResult GetQueryResult(object queryName, List<Filter> filters, int? limit) { return Return(MethodInfo.GetCurrentMethod(), () => DynamicQueryManager.Current.DynamicQueryManager.ExecuteQuery(queryName, filters, limit)); }

public List<object> GetQueryNames() { return Return(MethodInfo.GetCurrentMethod(), () => DynamicQueryManager.Current.DynamicQueryManager.GetQueryNames()); } (...)

It provides two methods, one for logging-in that is expected to throw an exception if no user is found with this username and password, and another for logging-out that closes the WCF session.

Actually there's no control in Signum.Windows that requires this interface, so you are free to implement your log-in/out strategy in a different way.
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.