Using three tier architecture
Important
This is feature of .Net 4.0 version only.
One of unique features of XData is using same application logic modules as in two-tier-architecture (client-db), as in three-tier-architecture (client-application server-db). And You can switch three-tier application in two-tier mode to simplify debugging changing configuration file only!!!
Tip
In three-tier mode no database client software needed on client workstation. And no database credential stored in configuration file.
To extract part of data aware application logic on application server level You will need:
- extract mapped classes in separated assembly (or assemblies) called "data modules"
- extract server data logic in separated assembly (or assemblies) called "server modules"
- in server modules create logic classes for some data objects extend abstract class XDataLogic<T>, where T - data object class type that uses server logic including trigger logic and call server logic from client code.
public abstract class InvoiceLogic : XDataLogic<Invoice>
{
[Action(DataActionType.AfterInsert), Action(DataActionType.AfterUpdate)]
public readonly static Trigger<Invoice> UpdateHistory =
((ref Invoice invoice, ref DataTriggerFlag flag) =>
{
if (!invoice.CheckState(DataObjectState.New)
&& !invoice.IsChanged(x => x.DocState)) return true;
var rep = invoice.GetRepository();
var hist = GetRepository<DocHistory>(rep.Layer, context: rep.Context)
.Reset()
.SetFilterValue(DocHistory.FilterByDocId,
invoice.GetProperty<long>("DocId"))
.SetFilterValue(DocHistory.FilterByDocStateId,
invoice.DocState.Key);
var newHist = hist.New();
newHist.HistoryDate = DateTime.UtcNow;
return hist.Submit(ref newHist);
});
[Action(DataActionType.BeforeDelete)]
public readonly static Trigger<Invoice> ClearHistory =
((ref Invoice invoice, ref DataTriggerFlag flag) =>
{
var i = invoice;
return GetRepository<DocHistory>(i.GetLayer(), context: i.GetContext())
.Reset()
.Clear(x => x.GetProperty<long>("DocId") == i.GetProperty<long>("DocId"));
});
[Action(DataActionType.BeforeClear)]
public readonly static RepositoryTrigger<Invoice> ClearHistoryBatch =
((IRepository<Invoice> invoiceRepository, ref DataTriggerFlag flag) =>
GetRepository<DocHistory>(invoiceRepository.Layer, context: invoiceRepository.Context)
.Reset()
.Clear(x => invoiceRepository
.Any(z => x.GetProperty<long>("DocId") == z.GetProperty<long>("DocId"))));
public static readonly CustomLogic<Invoice> TestCustomLogic = (objects =>
{
Log.Write(MessageType.Information,
() => String.Format("TestCustomLogic called with {0} objects", objects.Length));
foreach (var invoice in objects)
{
var i = invoice;
invoice.PostData("testPost", () => Encoding.UTF8.GetBytes(i.DocNumb));
var p = Encoding.UTF8.GetBytes(i.DocNumb);
var r = i.Callback("testCall", ref p);
Log.Write(MessageType.Information,
() => String.Format("Call for \"{0}\" returned \"{1}\" with data \"{2}\"",
i.DocNumb, r, p == null ? null : Encoding.UTF8.GetString(p)));
}
return true;
});
}
- set server module assembly name in data object mapping in data modules
[DataObject("D", LogicAssemblyName = "InvoiceServerLogic")] //for static mapping
//or
...
.SetLogicAssembly("InvoiceServerLogic") //for dynamic mapping
- register custom logic handlers inside mapped types in data modules
public static CustomLogic<Invoice> TestCustomLogic;
- write custom logic calls from client modules
var random = new Random();
return dataScope.GetRepository<Invoice>().ToArray().Execute(
() => Invoice.TestCustomLogic,
"testPost".SetValue((Action<byte[]>)(data =>
Console.WriteLine("Post message received (data=\"{0}\")", data == null
? null
: Encoding.UTF8.GetString(data)))).AsEnum().ToDictionary(),
"testCall".SetValue((Func<byte[], byte[]>)(data =>
{
Console.WriteLine("Call received \"{0}\")",
data == null ? null : Encoding.UTF8.GetString(data));
return random.NextDouble() >= 0.5
? null
: Encoding.UTF8.GetBytes(
string.Format("reply for \"{0}\"", data == null
? null
: Encoding.UTF8.GetString(data)));
})).AsEnum().ToDictionary());
Tip
Custom logic handlers can use synchronous and asynchronous callbacks to client code and perform analysis of synchronous callbacks result. This functionality allows to realize very complex and interactive distributed business processes.