Show/Hide Toolbars

TMS XData Documentation

Navigation: Service Operations

TXDataOperationContext

Scroll Prev Top Next More

To help you implement your service operations, XData provides you some information under the context of the operation being executed. Such information is provided in a TXDataOperationContext object (declared in unit XData.Server.Module). The simplified declaration of such object is as followed.

 

TXDataOperationContext = class
public
  class function Current: TXDataOperationContext;
  function GetConnectionPool: IDBConnectionPool;
  function Connection: IDBConnection;
  function GetManager: TObjectManager;
  function CreateManager: TObjectManager; overload;
  function CreateManager(Connection: IDBConnection): TObjectManager; overload;
  function CreateManager(Connection: IDBConnection; Explorer: TMappingExplorer): TObjectManager; overload;
  function CreateManager(Explorer: TMappingExplorer): TObjectManager; overload;
  procedure AddManager(AManager: TObjectManager);
  function Request: THttpServerRequest;
  function Response: THttpServerResponse;
end;

 

To retrieve the current instance of the context, use the class function Current.

 

Context := TXDataOperationContext.Current.GetManager;

 

Then you can use the context to retrieve useful info, like a ready-to-use, in-context TObjectManager to manipulate Aurelius entities (using the GetManager function) or the connection pool in case you want to acquire a database connection and use it directly (using GetConnectionPool method).

 

This way your service implementation doesn't need to worry about how to connect to the database or even to create a TObjectManager with proper configuration and destroy it at the end of process. All is being handled by XData and the context object. Another interesting thing is that when you use the TObjectManager provided by the context object, it makes it easier to manage memory (destruction) of entity objects manipulated by the server, since it can tell what objects will be destroyed automatically by the manager and what objects need to be destroyed manually. See Memory Management topic for more information.

 

The example is an example of an operation implementation that uses context manager to retrieve payments from the database.

 

uses 
  {...}
  XData.Server.Module;
 
function TCustomerService.FindOverduePayments(CustomerId: integer): TList<TPayment>;
begin
  Result := TXDataOperationContext.Current.GetManager.Find<TPayment>
    .CreateAlias('Customer''c')
    .Where(TLinq.Eq('c.Id', CustomerId) and TLinq.LowerThan('DueDate', Now))
    .List;
end;

 

Note that the database connection to be used will be the one provided when you created the TXDataServerModule. This allows you keep your business logic separated from database connection. You can even have several modules in your server pointing to different databases, using the same service implementations.

 

Inspecting request and customizing response

 

The context also provides you with the request and response objects provided by the TMS Sparkle framework.

You can, for example, set the content-type of a stream binary response:

 

function TMyService.GetPdfReport: TStream;
begin
  TXDataOperationContext.Current.Response.Headers.SetValue('content-type''application/pdf');
  Result := InternalGetMyPdfReport;
end;

 

Or you can check for a specific header in the request to build some custom authentication system:

 

function TMyService.GetAppointment(const Id: integer): TVetAppointment;
var
  AuthHeaderValue: string;
begin
  AuthHeaderValue := TXDataOperationContext.Current.Request.Headers.Get('custom-auth');
  if not CheckAuthorized(AuthHeaderValue) then
    raise EXDataHttpException.Create(401'Unauthorized'); // unauthorized
 
  // Proceed to normal request processing.
  Result := TXDataOperationContext.Current.GetManager.Find<TVetAppointment>(Id);
end;

 

The default connection

 

The default database connection interface (IDBConnection) is provided in the property Connection:

 

DefConnection := TXDataOperationContext.Current.Connection;

 

It's the same connection used by the default manager, and it's retrieved from the pool when needed.

 

 

Additional managers

 

You might need to create more Aurelius managers in a service operation. You can of course do it manually, but if by any chance you want to return one entity inside one of those managers, you can't destroy the manager you've created because otherwise the object you want to return will also be destroyed.

 

In these scenario, you can use the context methods CreateManager or AddManager. The former will create a new instance of a TObjectManager for you, and you can use it just like the default manager: find objects, save, flush, and don't worry about releasing it.

 

function TSomeService.AnimalByName(const Name: string): TAnimal;
var
  Manager: TObjectManager;
begin
  Manager := TXDataOperationContext.Current.CreateManager(TMappingExplorer.Get('OtherModel'));
  Result := Manager.Find<TAnimal>
    .Where(TLinq.Eq('Name', Name)).UniqueResult;
end;

 

To create the manager you can optionally pass an IDBConnection (for the database connection), or the model (TMappingExplorer), or both, or none. It will use the default Connection if not specified, and it will use the model specified in the XData server (module) if model is not specified.

 

If you want to create the manager yourself, you can still tell XData to destroy it when the request processing is finished, by using AddManager.

 

function TSomeService.AnimalByName(const Name: string): TAnimal;
var
  Manager: TObjectManager;
begin
  Manager := TObjectManager.Create(SomeConnection, TMappingExplorer.Get('OtherModel'));

  TXDataOperationContext.Current.AddManager(Manager);
  Result := Manager.Find<TAnimal>
    .Where(TLinq.Eq('Name', Name)).UniqueResult;
end;