move to the future. think objects, not sql. © 2005 x-tensive.com

140
Move to the future. Think Objects, not SQL. © 2005 X-tensive.com http://www.x-tensive.com/Products/DataOb jects.NET /

Upload: amos-jones

Post on 14-Dec-2015

224 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Move to the future.Think Objects, not SQL.

Move to the future.Think Objects, not SQL.

© 2005 X-tensive.com

http://www.x-tensive.com/Products/DataObjects.NET/http://www.x-tensive.com/Forum/

Page 2: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Important notesImportant notes

• Be ready to spend 2-4 hours on this presentation – it’s large (~ 140 slides). But you’ll get the imagination about almost all features of DataObjects.NET after watching it

• Feel free to skip any “schema slide”, if you don’t understand it. You may return back to it after studying the code example (they’re usually located in the end of each section)

• All UML-like diagrams aren’t actually UML diagrams – by the following reasons:– Regular tools generate quite “overflooded” UML diagrams for

DataObjects.NET (too many additional classes and properties – look e.g. on .HxS/.Chm help), so it was necessary to manually “draw” all of them

– PowerPoint isn’t a good tool for authoring UML – it doesn’t provide a comfortable way to use necessary arrow types, boxes and so on

– So we decided to use the notation that:• Is still understandable and convenient (UML-like)• Can be easily authored in PowerPoint

• It’s a good idea to have DataObjects.NET Manual, Samples and .HxS/.Chm Help somewhere nearby

Page 3: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Presentation planPresentation plan

• Example• Architecture overview• Application layout examples• Features overview

– Low-level \ persistence features– Integrated features– Add-ons

Page 4: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Example

(from Demo_FirstStep)

Page 5: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObjects.NET-based application develpment: key stepsDataObjects.NET-based application develpment: key steps

• Declare a persistent type(s)• Write Domain build code• Use your persistent types

Page 6: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Step 1: persistent entity code

// This is our first persistent class.// Don't worry about abstract modifiers –// DataObjects.NET will automatically create // a descendant of this class called "proxy" // class that will implement the abstract member. // This class will be created transparently // for you in the runtime // (to be exact - during the execution of the// Domain.Build(...) method).public abstract class Animal: DataObject { // A single persistent property public abstract int Age {get; set;}}

Page 7: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Step 2: Domain build code

domain = new Domain( "mssql://localhost/DataObjectsDotNetDemos", productKey);// At least one culture should be registereddomain.RegisterCulture( new Culture("En","U.S. English", new CultureInfo("en-us",false)));domain.Cultures["En"].Default = true; domain.RegisterTypes("Demo_FirstStep");domain.Build(DomainUpdateMode.Recreate);

Page 8: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Step 3: persistent entity usage, part 1

long instanceID = 0;// You should work with Session by the same wayusing (Session session = new Session(domain)) { session.BeginTransaction(); // Let’s create our first persistent instance Animal a = (Animal)session.CreateObject(typeof(Animal)); // Instance is already persisted to the storage now // Let's set the first persistent property a.Age = 2; // And read the instance ID instanceID = a.ID; // And finally commit our work session.Commit();}

Page 9: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Step 3: persistent enrtity usage, part 2

using (Session session = new Session(domain)) { session.BeginTransaction(); // Fetching the instance by its ID Animal a = (Animal)session[instanceID]; Console.WriteLine("Age: {0}", a.Age); // Output: 2 Console.WriteLine("Creating savepoint..."); Savepoint sp = new Savepoint(session); Console.WriteLine("Savepoint created."); a.Age = 3; Console.WriteLine("Age: {0}", a.Age); // Output: 3 Console.WriteLine(

"Rolling back to the savepoint..."); sp.Rollback(); Console.WriteLine("Rollback completed."); Console.WriteLine("Age: {0}", a.Age); // Output: 2 session.Commit();}

Page 10: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Architecture Overview

Page 11: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObjects.NET-based application layersDataObjects.NET-based application layers

DataSetsDataSets

Client connection

ways

Application

Server

RDBMS Server

DataObjects.NET (DAL)DataObjects.NET (DAL)

DataObjects.NET RDBMS driverDataObjects.NET RDBMS driver

RDBMS

WebServices, Remoting(MarshalByValue)

Remoting(MarshalByRef)

ObjectSetsObjectSets

DataObjects, DataServices (BLL) DataObjects, DataServices (BLL)

Offline LayerOffline LayerAdapterAdapter

Page 12: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObjects.NET structureDataObjects.NET structure

Drivers

MS SQLMS SQL

OracleOracle

FirebirdFirebird

MaxDB/SAPDBMaxDB/SAPDB

Native OracleNative Oracle

Versionizing

Full-text search

Versionizing

Full-text search

Persistence Engine

PersisterPersisterPersisterForCollectionsPersisterForCollections

ModelsDatabaseModelDatabaseModel ObjectModelObjectModel

UpdateActionsUpdateActions

Schema Update Layer

ExtractorExtractor

DatabaseModelComparerDatabaseModelComparer

Builders

ObjectModelBuilderObjectModelBuilder

DatabaseModelBuilderDatabaseModelBuilder

ProxyBuilderProxyBuilder

Drivers Layer RDBMS Abstraction Layer

DAL (DataObject,…) Core

Integrated Features

Build-tim

e onlyR

ead-only after buildR

untime

Offlin

eO

ffline

Aa

pte

rA

ap

ter

Bin

din

g

Ma

na

ger

Bin

din

g

Ma

na

ger

Ad

d-o

ns

Low-level Features

VersionizingVersionizing

PreloadingPreloading

CachingCaching

UpdateActionTranslatorUpdateActionTranslator

NamingManagerNamingManager

……

SerializationSerialization

Full-text searchFull-text search

NTFS-like security NTFS-like security

……

Page 13: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Domain build process schemaDomain build process schema

Proxy AssemblyProxy Assembly

Extractor

RDBMS

ObjectModelBuilder

DatabaseModelBuilder

ProxyBuilder

UpdateActionsUpdateActionsSchema Upgrade

SQL Script

Schema Upgrade

SQL Script

UpdateActionsTranslator

DatabaseModelDatabaseModel

DatabaseModelComparer

ExtractedDatabaseModelExtractedDatabaseModel

ObjectModelObjectModel

.NET Reflection-provided model.NET Reflection-provided model

Page 14: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

RDBMS Abstraction Layer: types schemaRDBMS Abstraction Layer: types schema

SessionSession

DomainDomain

ConnectionUrl

Driver

*…1

SessionBoundObjectSessionBoundObject AA

+Session

DriverDriver AA

Creates

1…1

PersisterForCollectionsPersisterForCollections AAPersisterPersister AA

1…1

1…**…1

InfoInfo AA

UtilsUtils AA

NamingManagerNamingManager AA

UpdateAction TranslatorUpdateAction Translator AA

1…*

Page 15: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Core types: descriptionCore types: description

• Domain– Root object providing access for the whole object storage– Contains database connection URL & other configuration options, all

models, driver, etc.– Provides persistent type \ DataService registration methods (see

RegisterXXX methods)– Provides Build method– Allows to create Session objects

• Session– IDbConnection analogue– Allows to create \ access (fetch) DataObjects– Allows to create other SessionBoundObjects, such as DataService,

Transaction, Query• DataObject

– Abstract base class for any persistent entity– Its descendants are business objects: their transactional methods form

BLL– Its persistent properties and their attributes serve as persistence

descriptors for DAL (DataObjects.NET)

Page 16: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Core types: schemaCore types: schema

+User

+ID

+VersionID

+TypeID

+Permissions

+State

AADataObjectDataObject

DomainDomain

ID

VersionID

TypeID

IDataObjectIDataObject

+Session

SessionBoundObjectSessionBoundObject AA

MarshalByRefObjectMarshalByRefObject AA

SessionSession

1…*

*…1

To create Domain instance:

Domain domain = new Domain(…);

domain.Build(…);

To create Session instance:

domain.CreateSession(…);

[or]

Session s = new Session(domain, …)

To create DataObject instance:

DataObject o = sesion.CreateObject(

typeof(MyType)); // New

[or]

DataObject o = session[id]; // Fetch

#DisableSecurity()

#EnableSecuriry()

Page 17: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent types and their proxiesPersistent types and their proxies

+ID

+VersionID

DataObjectDataObject

Cat_ProxyCat_Proxy

Animal_ProxyAnimal_Proxy

AA

+Age

+LegCount

AnimalAnimal AA

+Name

HomeAnimalHomeAnimal AA

+Color

CatCat AA

+Session

+Domain

SessionBoundObjectSessionBoundObject AA

#DisableSecurity()

#EnableSecuriry()

MarshalByRefObjectMarshalByRefObject AA

You always work with instances of

these types.

DataObjects.NET builds them during

Domain.Build(…) execution.No Proxy – marked by

[Abstract] attribute

DataObject o = session.CreateObject(

typeof(Animal),…);

Debug.Assert(o.GetType().ShortName==

“Animal_DataObjectsDotNetProxy”);

Page 18: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Proxies: abstract property implementationProxies: abstract property implementation

DataObjectDataObjectCat_ProxyCat_Proxy

get_Age

GetProperty(“Age”)

object ageValue = …

Lots of other calls

happen here

Type conversion is performed here.

(object-to-int in this case)

AA

Page 19: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Instances of the same entity in the applicationInstances of the same entity in the application

Domain domainDomain domain

Session s2Session s2Session s1Session s1

+ID=10

+LegCount=2

Cat_ProxyCat_Proxy

+ID=10

+LegCount=5

Cat_ProxyCat_Proxy

Different objects: s1[10]!=s2[10]

Moreover: s1[anyID]!=s2[anyID]

There can be several CLR instances describing the same

persistent entity (IDs of such instances are equal) in the

different Sessions. This means that the same entity may

looks different in the different Sessions.

Page 20: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: type documentationDataObject: type documentation

• Next 9 slides shows DataObject class properties and methods

• We recommend to read their names at least – this will give you an overview of capabilities of one of the most important types

• If it seems boring, feel free to skip next 9 slides

Page 21: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: type documentationDataObject: type documentation

Page 22: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: properties, part 1DataObject: properties, part 1

Page 23: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: properties, part 2DataObject: properties, part 2

Page 24: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: public methods, part 1DataObject: public methods, part 1

Page 25: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: public methods, part 2DataObject: public methods, part 2

Page 26: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: protected methods, part 1DataObject: protected methods, part 1

Page 27: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: protected methods, part 2DataObject: protected methods, part 2

Page 28: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: protected methods, part 3DataObject: protected methods, part 3

Page 29: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObject: protected methods, part 4DataObject: protected methods, part 4

Page 30: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Application layout examples

Page 31: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Web: simpleWeb: simple

ASP.NET HostASP.NET Host

BLL & DAL

ASP.NET Application (UI)ASP.NET Application (UI)

Thin ClientS

erv

er

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

DataSetsDataSets ObjectSetsObjectSets

RDBMS

Offline LayerOffline LayerAdapterAdapter

Page 32: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Web: separate RDBMS serverWeb: separate RDBMS server

ASP.NET HostASP.NET Host

BLL & DAL

ASP.NET Application (UI)ASP.NET Application (UI)

Thin ClientA

pp

licati

on

Serv

er

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

DataSetsDataSets ObjectSetsObjectSets

Offline LayerOffline LayerAdapterAdapter

RDBMS

Page 33: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Web farmWeb farm

ASP.NET HostASP.NET Host

BLL & DAL

ASP.NET Application (UI)ASP.NET Application (UI)

Thin Client

Serv

ers

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

DataSetsDataSets ObjectSetsObjectSets

Offline LayerOffline LayerAdapterAdapter

1…n

RDBMS

Page 34: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Web: single app. server, multiple UI serversWeb: single app. server, multiple UI servers

BLL & DAL

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

DataSetsDataSets ObjectSetsObjectSets

Offline LayerOffline LayerAdapterAdapter

RDBMS

ASP.NET HostASP.NET Host

ASP.NET Application (UI)ASP.NET Application (UI)

Thin Client

Server

1…n

This configuration is not

recommended (it’s inefficient in

comparison to others – there is

unnecessary .NET Remoting

interaction), but it can be

implemented

Page 35: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

WindowsForms: simpleWindowsForms: simple

BLL & DAL

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

DataSetsDataSets ObjectSetsObjectSets

Offline LayerOffline LayerAdapterAdapter

RDBMS

WindowsForms Application

(UI)

WindowsForms Application

(UI)

Rich/Smart Client

Client PCs

This configuration is not

recommended (BLL & DAL code

works on the client side here),

but it can be implemented

Page 36: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

WindowsForms: single application serverWindowsForms: single application server

BLL & DAL

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

ObjectSetsObjectSetsDataSetsDataSets

Offline LayerOffline LayerAdapterAdapter

RDBMS

Ap

plic

ati

on

Serv

er

WindowsForms Application

(UI)

WindowsForms Application

(UI)

Rich/Smart Client

Clie

nt

PC

Page 37: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Combined configuration: an exampleCombined configuration: an example

ASP.NET HostASP.NET Host

BLL & DAL

RDBMS

ASP.NET Application (UI)ASP.NET Application (UI)

Server

DataObjects.NET (DAL)

Single Domain Object

DataObjects.NET (DAL)

Single Domain Object

DataObjects, DataServices (BLL)DataObjects, DataServices (BLL)

DataSetsDataSets ObjectSetsObjectSets

Offline LayerOffline LayerAdapterAdapter

Client PC

WindowsForms Application

(UI)

WindowsForms Application

(UI)

Rich/Smart Client

Thin Client

Page 38: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Feature overview

Page 39: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Features mapFeatures map

Low-level Integrated

Persistence

Complete inheritance support

Supported property types

Relationships

Referential integrity control

Transparent persistence

VersionID/VersionCode

Transactions

(+ nested & distributed)

Savepoints

Automatic transactions

Queries (Query, SqlQuery)

Multilingual properties

Versionizing

Database schema

Automatic schema building and upgrading layer

Integrated

Configuration

Serialization

Property validators, correctors, modifiers

Full-text indexing and search

DataServices

RuntimeServices

IXxxEventWatcher

TrackingSet

Security system

Etc. (e.g. Pager, QueryPager)

Adapter

DataObjects-DataSets gateway

Models

Domain.ObjectModel

Domain.DatabaseModel

Domain. ExtractedDatabaseModel

Domain.UpdateActions

Performance

Caching Lazy instantiation

Lazy loading (load-on-demand)

Preloading

Delayed updates

Security checks caching

Manual caching support

Offline layer

Implementation of well-known DTO (Data Transfer Object) pattern

BindingManager

DataObjects.NET-ASP.NET & WindowsForms controls gateway

Add-ons

Page 40: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Automatic database schema upgardingAutomatic database schema upgarding

• Automatically adds\removes\modifies:– Tables– Columns– Indexes (& full-text indexes)– Primary\foreign keys– Views

• Upgrade modes:– Skip– SkipButExtract– SkipButExtractAndCompare– Perform– PerformSafe– PerformComplete– Recreate– Block

• Supports content integrity validation

Page 41: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

ModelsModels

• Domain.ObjectModel– Provides all necessary information much faster then Reflection in

the runtime– Tightly bound with Domain.DatabaseModel (they’re mutually

related)• Domain.DatabaseModel

– Complete model of the database schema– Tightly bound with Domain.ObjectModel (they’re mutually related)

• Domain.ExtractedDatabaseModel– Complete model of the extracted database schema

• Domain.UpdateActions– A collection of objects describing schema upgrade SQL Script

See also: Domain.DebugInfoOutputFolder – a way to get all models dumped into text files

Page 42: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Model build schema (this slide was already shown earlier)Model build schema (this slide was already shown earlier)

Proxy AssemblyProxy Assembly

Extractor

RDBMS

ObjectModelBuilder

DatabaseModelBuilder

ProxyBuilder

UpdateActionsUpdateActionsSchema Upgrade

SQL Script

Schema Upgrade

SQL Script

UpdateActionsTranslator

DatabaseModelDatabaseModel

DatabaseModelComparer

ExtractedDatabaseModelExtractedDatabaseModel

ObjectModelObjectModel

.NET Reflection-provided model.NET Reflection-provided model

Page 43: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistence featuresPersistence features

• Complete inheritance support• Supported property types• Relationships• Referential integrity control• Transparent persistence• VersionID/VersionCode• Transactions (+ nested & distributed), Savepoints• Automatic transactions• Queries (Query, SqlQuery)• Multilingual properties• Versionizing

Page 44: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistence: complete inheritance supportPersistence: complete inheritance support

• Unified instance identification (64-bit integer identifiers)• Reference properties and collection items can be of any

persistent type• Hierarchy queries: "Select Animal objects where

{LegCount}=4“: returns all Animal, Cat and Dog objects having four legs

• Persistent interfaces:– IDataObject descendants– Reference properties and collection items can be of persistent

interface type– Interface queries: " Select ILeggedEntity objects where

{LegCount}=4"– Can contain paired reference\collection properties

Page 45: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent property typesPersistent property types

• Base CLR types• Struct types• Serializable types• Reference types (DataObject\IDataObject or its

descendants)• DataObjectCollection type (collections of

references)• ValueTypeCollection type (collection of structs)• Delegates

Page 46: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent field types schemaPersistent field types schema

FieldField

ValueTypeCollectionFieldValueTypeCollectionField

BooleanFieldBooleanField

ByteFieldByteField

LongFieldLongField

IntFieldIntField

EnumFieldEnumField

DoubleFieldDoubleField

DecimalFieldDecimalField

ShortFieldShortField

SingleFieldSingleField

TimeSpanFieldTimeSpanField

BlobFieldBlobField

DateTimeFieldDateTimeField

DelegateFieldDelegateField

StringFieldStringField

ObjectFieldObjectField

NumericFieldNumericField

GuidFieldGuidField

IHasContainedFieldsFieldIHasContainedFieldsField

IReferenceHolderGroupFieldIReferenceHolderGroupField

IReferenceHolderFieldIReferenceHolderField

IPairToTargetIPairToTarget

IPairToAllowedIPairToAllowed

DataObjectCollectionFieldDataObjectCollectionField

KeyFieldKeyField

ReferenceFieldReferenceField

ObjectIDFieldObjectIDField

PrimitiveFieldPrimitiveField ExtrnalFieldExtrnalField StructFieldStructField

Instances of Field descendants handle

persistence of each persistent property:

Domain.ObjectModel.Types[“Person”].Fields[“Ag

e”] is an IntField instance handling persistence of

Age property of Person.

Page 47: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent properties: base CLR types

public abstract class Person: DataObject{ public abstract string Name {get; set;} public abstract string SecondName {get; set;} public abstract string Surname {get; set;} public abstract int Age {get; set;} public abstract string Info {get; set;}

[NotPersistent] public virtual string FullName { get { return Name+" "+SecondName+" "+Surname; } } }

Page 48: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent properties: structs

public struct Point{ public double X; public double Y; public Point(double x, double y) { X = x; Y = y; }}

public abstract class Rectangle: DataObject{ public abstract Point LeftTop {get; set;} public abstract Point RightBottom {get; set;} }

Page 49: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent properties: serializable types

public abstract class Author: DataObject{ public abstract Image Image {get; set;} ...}

Author a = (Author)session[aid];a.Image.RotateFlip(RotateFlipType.Rotate90FlipNone);

a.Image = a.Image;

Page 50: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent proeprties: references, collections, pairs

public abstract class Article: DataObject{ ... public abstract Author Author {get; set;}}public abstract class Author: Person{ ... [Contained] [ItemType(typeof(Article))] [PairTo(typeof(Article), "Author")] public abstract DataObjectCollection Articles {get;}}

...

// First way to add an author to paired collectionarticle1.Author = author;// Second way to do absolutely the same thingauthor.Articles.Add(article1);// So both parts of paired relationship are maintained// in sync automatically

Page 51: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent properties: ValueTypeCollections, part 1

public struct Quote{ [Length(250)] public string Body; public Quote(string body) { Body = body; }}

public abstract class Author: DataObject{ public abstract string Name {get; set;} ... [ItemType(typeof(Quote))] public abstract ValueTypeCollection Quotes { get; }}

Page 52: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent properties: ValueTypeCollections, part 2

Author Tyler = (Author)session.CreateObject(typeof(Author));Tyler.Name = "Tyler Durden";Tyler.Quotes.Add(new Quote("You're not your job."));Tyler.Quotes.Add(new Quote("You're not how much money you

have in the bank."));Tyler.Quotes.Add(new Quote("You're not the car you

drive."));Tyler.Quotes.Add(new Quote("You're not the contents of your

wallet."));Tyler.Quotes.Add(new Quote("You are not a beautiful or

unique snowflake."));

Console.WriteLine("Quotes with length > 30:");q = new Query(session, "Select Author.Quotes values where

len({Body}) > 30");ValueTypeQueryResult vr = q.ExecuteValueTypeQueryResult();Console.WriteLine(" Result: {0} values.", vr.Count);foreach (ValueTypeQueryResultEntry entry in vr) Console.WriteLine(" {0}", ((Quote)entry.Value).Body);

Page 53: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Persistent properties: delegates

public delegate void CallbackDelegate(DataObject sender); public abstract class DelegateOwner: DataObject { [LoadOnDemand(Threshold=5)] public abstract CallbackDelegate Callback {get; set;} public virtual void OnCallback() { if (Callback!=null) Callback(this); } }

...CallbackDelegate staticDelegate = new CallbackDelegate( SomeClass.SomeStaticMethodThatConformsToCallbackDelegateSpec); someDelegateOwner.Change = staticDelegate;

CallbackDelegate regularDelegate = new CallbackDelegate( SomeClass.SomeMethodThatConformsToCallbackDelegateSpec); someDelegateOwner.Change += regularDelegate; someDelegateOwner.Change = someDelegateOwner.Change;

Page 54: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Relationship typesRelationship types

• Regular references (to one)• Mutually dependent references (one-to-one)• Collections• Mutually dependent reference-collection pair (one-

to-many)• Mutually dependent collections (many-to-many,

symmetric)• N-ary relationships (via ValueTypeCollections)• Mutually dependent N-ary relationships

Page 55: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Mutual relationship: one-to-one

public abstract class Passport: DataObject{ public abstract string SerialNumber {get; set;} public abstract Person Person {get; set;}}

public abstract class Person: DataObject{ ... [PairTo(typeof(Passport),"Person")] public abstract Passport Passport {get; set;} }

Page 56: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Mutual relationships: one-to-many

public abstract class Article: DataObject{ ... public abstract Author Author {get; set;}}

public abstract class Author: Person{ ... [ItemType(typeof(Article))] [PairTo(typeof(Article), "Author")] public abstract DataObjectCollection Articles {get;}

}

Page 57: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Mutual relationship: many-to-many

public abstract class Principal: FtObject{ [ItemType(typeof(Role))] public abstract RoleCollection Roles {get;} // RoleCollection is "typed" DataObjectCollection // descendant ...}

public abstract class Role: Principal{ [ItemType(typeof(Principal))] [PairTo(typeof(Principal),"Roles")] // !!! public abstract PrincipalCollection Principals {get;} // PrincipalCollection is "typed" DataObjectCollection // descendant ...}

Page 58: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Mutual relationship: symmetric

public abstract class Person: DataObject { public abstract string Name {get; set;}

[ItemType(typeof(Person))] [Symmetric] // == [PairTo(typeof(Person),"Friends")] public abstract DataObjectCollection Friends {get;}}

...

Person me = (Person)session.CreateObject(typeof(Person));me.Name = "Alex";Person bob = (Person)session.CreateObject(typeof(Person));bob.Name = "Bob";me.Friend.Add(bob);if (me.Friends.Contains(bob))

Console.WriteLine("Bob is a friend of my.");if (bob.Friends.Contains(me))

Console.WriteLine("I'm a friend of Bob.");

Page 59: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Mutual relationship: n-ary relationship (ternary), part 1

[Serializable] public struct OrderPosition { public int Index; // In Order's collection public Order Order; public Product Product; public Vendor Vendor; public double Quantity; public OrderPosition(int index, Product product, Vendor

vendor, double quantity) { Index = index; Order = null; Product = product; Vendor = vendor; Quantity = quantity; } }

Page 60: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Mutual relationship: n-ary relationship (ternary), part 2

public abstract class Order: DataObject { ... [ItemType(typeof(OrderPosition), OwnerField = "Order")] public abstract ValueTypeCollection Positions {get;} } public abstract class Product: DataObject { ... [ItemType(typeof(OrderPosition), OwnerField = "Product")] [PairTo(typeof(Order), "Positions")] public abstract ValueTypeCollection OrderPositions {get;} } public abstract class Vendor: DataObject { ... [ItemType(typeof(OrderPosition), OwnerField = "Vendor")] [PairTo(typeof(Order), "Positions")] public abstract ValueTypeCollection OrderPositions {get;} }

Page 61: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Referential integrity controlReferential integrity control

• Automatic for all references and collections• Controlled via [AutoFixup(AutoFixupAction)]• AutoFixupAction types:

– Clear (default): a reference property should be automatically set to null on removal of its target

– Block: prevents removal of reference target. An exception will be thrown on attempt to do this except the case when reference holder object is also removing.

– None: reference fixup is turned off for a particular reference property – useful e.g. when there is a service similar to XxxFtIndexer that actually performs the same, but more efficiently

• Session.RemoveObjects allows to remove a group of objects “at once” – i.e. the number of reference search queries won’t be proportional to the number of removing objects (usually just few queries will be executed)

• Session.RemoveObjects is used by DataObjects.NET to remove groups of [Contained] objects

Page 62: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Transparent persistenceTransparent persistence

• Transparent change propagation– No Apply changes / Save – like methods

• Transparent cached data invalidation \ reloading– No necessity to use Reload– See TransactionContext for additional information

Page 63: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Transparent persistence

Cat sonya = ... ; // Some code that fetches the Cat // instancesonya.Name = "Sonya";

// DataObjects.NET transparently // persist a new property valueCat kitty = sonya.Children[0];

// DataObjects.NET queries // a collection and fetches // the collection itemConsole.WriteLine(kitty.Parent.Name);

// output: "Sonya"

Page 64: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

VersionID/VersionCode supportVersionID/VersionCode support

• VersionID– Int32 value– Updated automatically on any change (actually – once per each persist

operation)– Automatically used for cached data invalidation– Supported by Adapter \ Offline layer \ BindingManager– Can be used by developers (e.g. to perform optimistic updates)

• VersionCode– String value– “VersionID” analogue for UI – shouldn’t be changed on any modifications

made by services (e.g. by workflow engine) to prevent unexpected “Object was modified by another user”-like messages in UI

– Can be based on any other combination of persistent properties (e.g. ModifyDate.ToString())

– Checked by DataObject.CheckVersionCode method– Initially – VersionID-based– Can be reimplemented by developers

Page 65: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Transactions, SavepointsTransactions, Savepoints

• Transactions– Explicit \ automatic– Nested transactions are supported– Distributed transactions are supported

• Savepoints– Nested transactions implementation is based on

Savepoints in DataObjects.NET– Some RDBMS (e.g. MaxDB) doesn’t support

savepoints, but supports true nested transactions – in this case underlying DataObjects.NET RDBMS driver internally uses nested transactions to simulate them

Page 66: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Automatic transactionsAutomatic transactions

• Provided for DataObject\DataService methods• All transactional methods should be virtual

(otherwise ProxyBuilder will be unable to override them to add automatic transaction handlers)

• [Transactional] attribute controls automatic transaction handler behavior

• Automatic deadlock re-processing is supported• All built-in methods accessing the data are already

transactional (e.g. DataObject.GetProperty, DataObjectCollection.Count, etc.)

Page 67: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Transactional method proxy codeTransactional method proxy code

AnimalAnimalCat_ProxyCat_Proxy

Feed(food)

r = base.Feed(food)

r

Actual method execution

TransactionController

creation code

Try-Catch block

Reprocessing check,

Commit

AA

Possible jump in case

with deadlock

reprocessing

Page 68: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Transactional method example 1: transactional property & method

public abstract class Person: DataObject{ public abstract string Name {get; set;} public abstract string SecondName {get; set;} public abstract string Surname {get; set;}

[NotPersistent] [Transactional(TransactionMode.TransactionRequired)] public virtual string FullName { get { return Name+" "+SecondName+" "+Surname; } }

[Transactional(TransactionMode.TransactionRequired)] public virtual string GetFullName() { return Name+" "+SecondName+" "+Surname; } }

Page 69: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Transactional method example 2: direct SQL query execution

[ServiceType(DataServiceType.Shared)] public abstract class AggregateInfo : DataService { [Transactional(TransactionMode.TransactionRequired)] public virtual double MinimalAnimalAge() { Demand(...); DisableSecurity(); // Otherwise Session.CreateRealCommand() // call will fail. try { IDbCommand cmd = Session.CreateRealCommand(); cmd.CommandText = "Select min([Age]) from " + Session.Types[typeof(Animal)].RelatedView.Name; return cmd.ExecuteScalar(); } finally { EnableSecurity(); } } }

Page 70: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Queries: features, examplesQueries: features, examples

• Text-based (Query object), support:– Options:

Select top 5 Item instances with (LoadOnDemand, FastFirst, ForUpdate) where {Name} <> 'Rattleless‘

– Parameters: Select Category instances where {Products.Count} > @Min

– Type casts: Select Cat instances where exists{(Dog)Friends.(Mouse)Friends}

– Distinct, joins: Select distinct Author instances inner join $root.Articles as $a order by {$a.Date}

– Reference\collection queries: Select Building.Addresses.Item.City instances

– ValueTypeCollection queries: Select Author.Quotes values where len{Body} > 100

– Subqueries: Select top 100 IFtObject instances as $fto with (SkipLocked) where not exists {Select FtRecord objects where {FtObject}={$fto} }

– Full-text queries: Select Author instances where {Name}>='D' textsearch top 5 freetext 'Jungle' order by {FullTextRank} desc

• Sql-based (SqlQuery object)

Page 71: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Query-related types: schemaQuery-related types: schema

QueryBaseQueryBase

AA

SessionBoundObjectSessionBoundObject AA SessionSession

QueryQuery

+Text

Other QueryBase properties are

overriden to be read-only

SqlQuerySqlQuery

QueryParameterQueryParameter

QueryParameterCallQueryParameterCall

IListIList

ICollectionICollection

IEnumerableIEnumerable

1…1

*…1

*…1

Page 72: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Multilingual propertiesMultilingual properties

• Multilingual ([Translatable]) propertiesA set of values is maintained for such property, one for each Culture registered in the Domain. Each of such values is stored in its own column having a culture suffix (e.g. if property name is "Title", the name of the column can be "Title-En" for a culture having "En" name)

• [Collatable] string propertiesA set of columns with different collations (one per each registered Culture) is created in the database to keep the value of single string to allow use different sorting rules for this property

ID

CreateDate

Title

Content

DocumentDocument

EnEn RuRu DeDe

EnEn RuRu DeDe[Translatable]

[Indexed(FullText=true)]

Page 73: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

[Translatable], [Collatable] example: declaration

public abstract class Person: DataObject{ [Collatable] [Indexed] public abstract string Name {get; set;} ...}

public abstract class Book: DataObject{ [Indexed] public abstract string Title {get; set;}

[Translatable] public abstract string Description {get; set;}

[Translatable] [ItemType(typeof(Comment))] public abstract ValueTypeCollection Comments { get; }

...}

Page 74: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

[Translatable], [Collatable] example: usage

Culture cEn = domain.Cultures["En"];Culture cRu = domain.Cultures["Ru"];

// [Translatable] property usageBook b = (Book)session.CreateObject(typeof(Book));b.Title = "DataObjects.NET Internals";session.Culture = cEn; // Switches current culture in the sessionb.Description = "DataObjects.NET is...";session.Culture = cRu; // Switches current culture in the sessionb.Description = "DataObjects.NET есть...";

Book b = (Book)session.CreateObject(typeof(Book));b.Title = "DataObjects.NET Internals";bk1["Description",cEn] = "DataObjects.NET is... ";bk1["Description",cRu] = "DataObjects.NET есть... ";

// [Collatable] property usageCulture cEn = domain.Cultures["En"];Culture cRu = domain.Cultures["Ru"];session.Culture = cEn; // Switches current culture in the sessionQueryResult qr1 = session.CreateQuery( "Select Person object where {Name}>'Й' order by {Name}").Execute();session.Culture = cRu; // Switches current culture in the sessionQueryResult qr2 = session.CreateQuery( "Select Person object where {Name}>'Й' order by {Name}").Execute();

Page 75: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

VersionizingVersionizing

• Enables to switch any Session into read-only “browse past” mode to see the storage at any previous point of time (after completion of any previous successful transaction)

• All data necessary to enter “browse past” mode is stored automatically when versionizing is activate in the domain (see Domain.DatabaseOptions)

• No any storage modifications are performed on entering the “browse past” mode – this happens immediately on invocation of special method of Session object

• All “read-only” code works the same in the “browse past” mode – you can read any available data in the version you’re browsing, execute queries, use Adapter or Offline layer to export the data, etc.

• “Regular” to “Versionized” schema upgrade is performed automatically on versionizing mode activation

• Such additional information as transaction history and set of stored versions for each instance is maintained automatically and is always available on request

Page 76: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Versionizing example (from Demo_Versionizing)

session.BeginTransaction();

Query q = session.CreateQuery("Select Person instances");DataObject[] persons = q.ExecuteArray(1);Person p = (Person)persons[0];Account a = p.Accounts[0];

Console.WriteLine("Person: {0}", p.FullName);VersionInfo[] versions = a.GetVersionInfo();for (int i = 0; i < versions.Length; i++) { VersionInfo version = versions[i]; session.BrowsePast(version.RemovedInTransaction); Console.WriteLine(“ Account operations history till {0}", version.RemovedInTransactionID==0 ? "Now" : version.RemovedInTransaction.StartTime.ToString( "dd/MM/yyyy HH:mm:ss:fff"));

... Console.WriteLine(" Balance: {0}", a.Balance); Console.WriteLine(" Total Profits: {0}", a.TotalProfits());}

session.Commit();

Page 77: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Performance-related featuresPerformance-related features

• Caching– Built-in transparent caching of instances and collection content– Two-level caching architecture:

• WeakReference-based Session-level cache• Queue-based Domain-level (global) cache with any specified size

• Lazy instantiation & loading (load-on-demand)– Reference target \ collection item is instantiated on

the first traversal attempt– [LoadOnDemand] attribute, LoadOnDemandAttribute.Threshold– With (LoadOnDemand) option– FastLoadData column

• Preloading (Session.Preload(…) methods)• Delayed updates• Security checks caching• Manual caching support (see CacheableValue,

CacheableCollection, CacheableQuery)

Page 78: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Caching: instance caching architectureCaching: instance caching architecture

DomainDomain

1…1

GlobalCacheGlobalCache

RDBMS

SessionCacheSessionCache

1…*

1…11…1

Session cache interacts with Persister:

-When new instantiation info is fetched by RDBMS

driver, it's sent to Session cache

- When new (ID, VersionID) pair is fetched, Session

cache uses it to validate or invalidate corresponding

cached instance or instantiation info

Session cache interacts with Global cache:

- When no data is found in the Session cache, it’s requested from the

Global cache

-When new instantiation info is fetched by RDBMS driver, it’s

propagated into Global cache

- When new (ID, VersionID) pair is fetched, Global cache uses it to

possibly invalidate corresponding cached instantiation info

- Keeps instantiation info

- Queue-based (last accessed

entry moves to the top)

- Size-limited, thread-safe

- WeakReference-based

- Keeps reference to DataObject

instances or instantiation info

SessionSession

PersisterPersister

Page 79: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Caching: collection content cachingCaching: collection content caching

#Implementation

CollectionCollection

SessionSession

RDBMS

Makes “dirty” job,- interacts

with RDBMS driver

[Implementation][Implementation]PersisterForCollectioinsPersisterForCollectioins

- Allways empty or fully loaded- Handles Collection[int index] – like operations

[ItemByIndex cache][ItemByIndex cache]

- Can be partionally loaded- Handles Collection[long itemId] - like operations (Contains, Remove)

[ItemByItemID cache][ItemByItemID cache]

*…1

1…11…

1

1…1

1…1

1…1

Interact with each

other

DataObjectCollection or

ValueTypeCollection

Page 80: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Lazy instantiationLazy instantiation

• DataObjects.NET transparently instantiates (creates in-memory object and fetches its state data from the database) referred objects on the first access attempt:– Evaluation of cat.Children[0].Friends[1].Parent can lead to instantiation of

up to 3 new objects:• cat.Children[0]• cat.Children[0].Friends[1] • cat.Children[0].Friends[1].Parent

– Actual number of fetched objects depends on content of Session & Domain-level caches

• Even if Query\SqlQuery had fetched the data for some instance, its instantiation is delayed until the first attempt to access it (e.g. via QueryResult[index], or by any other way):– So if you don’t access some instances from QueryResult, time+memory

isn’t spent on their instantiation– Nevertheless its instantiation data (a struct containing all base fields, such

as ID, TypeID, VersionID, FastLoadData, Permissions) is stored both in Session and Domain-level caches, so it can be used further

Page 81: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Lazy loadingLazy loading

• [LoadOnDemand] attribute applied on the persistent property notifies that value of this property should be fetched only on the first attempt to access it, but not on the first attempt to access the instance– [LoadOnDemand] attribute supports Threshold specification – it

allows to setup the size of the [LoadOnDemand] property until which property is loaded by a regular way

– Threshold can be specified for collection properties – in this case no additional queries are necessary to fetch the collection data while its size is lower then Threshold value

• With (LoadOnDemand) option of Query (see QueryOptions also) \ SqlQuery specifies that only (ID, VersionID) pairs should be actually fetched on query execution– Nevertheless produced QueryResult behaves absolutely the same – all

data is transparently fetched on attempt to access its item, if necessary– This option helps to significantly reduce the traffic between SQL and

application server in case when it’s well-known when Domain or Session-level cache contains most part of required objects

– Fetched information (ID, VersionID pairs) is also internally used to validate\invalidate the data contained in caches

Page 82: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

PreloadingPreloading

• Preloading is useful when you’re planning to process large amount of data – it allows to optimize its fetching sequence

• Session.Preload(…):– Fills Session-level cache with instances \ collections \

[LoadOnDemand] properties that are planned to be accessed further

– Executes minimal necessary number of queries on preload operations

– Fetches only those data that isn’t available in Session-level or Domain-level caches

• DataObjectCollection \ ValueTypeCollection.Preload()– Loads the whole collection (populates its ItemByIndex cache)

• QueryResult.Preload()– Loads all QueryResult items that aren’t available in the Session\

Domain-level caches

Page 83: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Delayed updatesDelayed updates

• When you change a property value or collection content, DataObjects.NET normally doesn’t immediately flushes the change to the database

• Almost all types of such updates are delayed by default and flushed as late as it's possible (usually – right before the first Query\SqlQuery execution or Transaction.Commit)

• Delayed update sequence is normally executed via much less number of queries

Page 84: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Manual caching example: CacheableQuery usage

CacheableQuery qr = new CacheableQuery( session.CreateQuery( "Select Country instances with (Count)"), new TimeSpan(1000));

Console.WriteLine("Number of countries: {0}", qr.Count);

Thread.Sleep(500);// Is the same anyway hereConsole.WriteLine("Number of countries: {0}",

qr.Count);

Thread.Sleep(500);// Can be different here Console.WriteLine("Number of countries: {0}",

qr.Count);

Page 85: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Manual caching example: CacheableValue usage (calculated property caching)

public abstract class CIsAMultipliedByB: DataObject { public abstract double A {get; set;} public abstract double B {get; set;}

private CacheableValue cachedC; private void CalculateC(object source, object sender, CacheableValueEventArgs e) { e.CacheableValue.Value = A*B; }

public double C { get { if (cachedC==null) cachedC = new CacheableValue(Session, new TimeSpan(60000), new CacheableValueEventHandler(CalculateC)); return (double)cachedC.Value; } }

protected override void OnPropertyChanged(string name, Culture culture, object value) { base.OnPropertyChanged(name, culture, value); if (name=="A" || name=="B") cachedC = null; // Invalidation of cached value }}

Page 86: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Integrated features mapIntegrated features map

• Configuration• Serialization• Property validators, correctors, modifiers• Full-text indexing and search• DataServices

– RuntimeServices– IDataObjectEventWatcher, ITransactionEventWatcher,

IQueryEventWatcher

• TrackingSet• Security system• Etc. (e.g. Pager, QueryPager)

Page 87: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Configuration section example

<?xml version="1.0" encoding="utf-8"?><configuration> <configSections> <section name="DataObjects.NET" type="DataObjects.NET.ConfigurationSectionHandler,Dataobjects.NET" /> </configSections> ... <DataObjects.NET> <!-- connectionUrl="oracle://admin:admin@localhost/DoPetShp" --> <!-- connectionUrl="sapdb://admin:admin@localhost/DoPetShp" --> <domain connectionUrl="mssql://localhost/DoPetShop" productKeyPath="..\..\..\ProductKey.txt" debugInfoOutputFolder="C:\Debug" foreignKeyCreationMode="CreateForeignKeys, CreateNullRows" securityMode="Modern" securityOptions="Standard, AllowCreateUnauthenticatedSessions" sessionSecurityOptions="Standard" updateMode="Perform" > <cultures defaultCulture="En"> <culture name="En" title="U.S. English" cultureName="en-US" compareOptions="IgnoreWidth,IgnoreKanaType" /> </cultures> <types> <assembly name="DoPetShop.Model" /> </types> <services> <assembly name="DoPetShop.Model" /> </services> </domain> </DataObjects.NET> ...</configuration>

Page 88: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Configuration section usage

Domain domain = Configuration.DefaultDomain;...domain.Build();

Page 89: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

SerializationSerialization

• DataObjects.NET completely supports .NET Serialization. It allows to serialize or deserialize a graph of objects containing persistent instances using binary or SOAP formatters, user-specified formatters are also supported.

• DataObject descendants aren’t serializable by their nature – any such instance belongs to some Session and has relationships with set of other non-serializable instances. So it’s impossible to serialize a DataObject instance by the usual way. To perform serialization or deserialization, you should use special class – Serializer. This class is bound to Session (see Session.CreateSerializer) and capable to serialize\deserialize a graph of objects that contains some DataObject instances using BinaryFormatter\SoapFormatter or user-specified formatter.

• Serializer internally uses a set of helper objects to handle its job. Basically its task is to configure a formatter (add special SurrogateSelector and SerializationBinder) in the way allowing to serialize DataObject instances without difficulties.

Page 90: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Serialization example

Serializer serializer = session.CreateSerializer();serializer.FormatterType = FormatterType.Soap;serializer.SerializationOptions =

SerializationOptions.IncludeContainedInstances;

using (StreamWriter sw = new StreamWriter(“Data.xml")) { serializer.Serialize(sw.BaseStream, myObjectGraph); Console.WriteLine("Serialized: {0} instance(s), {1}

external(s).", serializer.LastOperationInstanceCount, serializer.LastOperationExternalInstanceCount);}

using (StreamReader sr = new StreamReader(“Data.xml")) { myObjectGraph = serializer.Deserialize(sr.BaseStream); Console.WriteLine("Deserialized: {0} instance(s), {1}

external(s).", serializer.LastOperationInstanceCount, serializer.LastOperationExternalInstanceCount);}

Page 91: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text indexing and search featuresFull-text indexing and search features

• Unified, but absolutely customizable full-text data population• Two fill-text indexing & search driver types:

– Native: uses RDBMS-provided full-text indexing and search features. Requires support of these features by underlying RDBMS driver. Currently only MS SQL driver supports native full-text indexing and search.

– External: uses external full-text search engine. These driver types are RDBMS-independent, i.e. you can use such driver even while RDBMS doesn’t support full-text indexing and search at all (e.g. Firebird). DotLucene full-text indexing and search driver is built-in into DataObjects.NET.

• Unified full-text search queries:– Query-based: Select Author instances where {Name}>='D‘ textsearch top 5

freetext 'Jungle‘ order by {FullTextRank} desc – SqlQuery-based (see SqlQuery.FtsCondition)– Three full-text search modes:

• FreeText – well-known free-text search mode, supported by all FtsDrivers• Condition – allows to specify exact full-text search condition on the language of

underlying full-text search engine• LikeExpression – doesn’t require full-text search engine at all, uses SQL “like”

predicate to execute the query (not recommended mode, since it’s actually can be very slow on >1000000-object database)

• Built-in managed wrapper for Microsoft Index Service Filters allows to index content of almost any imaginable file type (such as HTML, Adobe PDF and Microsoft Office files).

Page 92: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S: implementation stepsFull-text I&S: implementation steps

1. Inherit the types that require full-text search from FtObject or implement IFtObject (actually – tagging interface), override\implement ProduceFtData method(s)

2. Select full-text search driver by specifying Domain.FtsConnectionUrl. Examples:

native://localhost/ lucene://localhost/?IndexPath=C:\Debug\LuceneIndex

&Analyzer-Ru=Lucene.Net.Analysis.RU.RussianAnalyzer,%20\Lucene.Net&CreateSummaryFieldsForCultures=true

3. Add full-text indexer service to RuntimeServices collection of Domain(FtsDriver-provided RuntimeService that gathers full-text content produced by your IFtObject implementers and sends it to the underlying full-text indexing and search service)

4. Use Query or SqlQuery+FtsCondition to execute full-text search queries

Page 93: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S: persistent typesFull-text I&S: persistent types

IFtObjectIFtObject

ProduceFtData(FtData)

ProduceFtData(FtData,Culture)

string ProduceFtData(Culture)

1…1

[Translatable]

[Indexed(FullText=true)]

DataObjectDataObject AA

FtObjectFtObject AAIn fact – empty, since IFtObject

support is implemented on

DataObject level

IsFtRecordUpToDate

IDataObjectIDataObject +FtData

+FtObject

+IsIndexed

FtRecordFtRecord

+ProduceFtData(…)

+UpdateFtData(…)

Derive your types from these

to get full-text I&S support for

them

Page 94: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S: drivers schemaFull-text I&S: drivers schema

SBOSBO AASessionSession

1…1

RuntimeServiceRuntimeService AA

*…1

FtsDriverFtsDriver AA

Creates full-text indexers

LuceneFtsDriverLuceneFtsDriver

NativeFtsDriverNativeFtsDriver

LuceneFtsIndexerLuceneFtsIndexer

NativeFtsIndexerNativeFtsIndexer

*…1

1…*

Execute full-text queries,

create full-text indexers

DomainDomain

FtsConnectionUrl

FtsDriver

Indexes IFtObjects

(creates, updates and removes

FtRecord objects)

DataServiceDataService AA

Page 95: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S: FtIndexer.Execute() stepsFull-text I&S: FtIndexer.Execute() steps

IsFtRecordUpToDate

IFtObject AIFtObject A

null

FtRecord RAFtRecord RA

IFtObject BIFtObject B

IFtObject CIFtObject C

FtRecord RBFtRecord RB

IsFtRecordUpToDate

IsFtRecordUpToDate

FtDataIsIndexed

FtData = “Old”IsIndexed

FtRecord RCFtRecord RC

FtDataIsIndexed

FtRecord RXFtRecord RX

FtDataIsIndexed

FtData = “New”IsIndexed

First step:

XxxFtIndexer creates new FtRecord objects for all IFtObject

instances not having them

Second step:

- NativeFtIndexer updates FtData property (for each culture)

by executing FtRecord.UpdateFtData method on each

FtRecord having FtObject.IsFtRecordUpToDate==false;

- LuceneFtIndexer sets IsIndexed property to true on each

FtRecord object having

FtObject.IsFtRecordUpToDate==false || IsIndexed==false

and sends the result of FtRecord.ProduceFtData() execution

to Lucene

The last step:

XxxFtIndexer removes all FtRecord objects having no

associated IFtObject instances

Page 96: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S: query execution schemaFull-text I&S: query execution schema

QueryBaseQueryBase FtsDriverFtsDriver PersisterPersister

Execute

IDbCommand cmd

yes no

FtsDriver is native?

FtsDriver.ExecuteQuery(ftsCondition)

ftsResult

BuildQueryCommand(…,ftsCondition,ftsResult)

FtsResult==null ?

Build IDbCommand

yes no

Build full-text search restriction

& “order by” clause

Build.”in (…)” restriction

Execute Command

ArrayList result

yes no

Reorder result by ranks

Fts.Condition.OrderByRank==true

&& FtsResult!=null ?…

Page 97: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S example, step 1: producing full-text search data

public abstract class FtFile: FtObject{ [Length(256)] [Length(83, DriverTypes="Firebird")] // Firebird - specific declaration [Indexed] public abstract string Name {get; set;}

[NotPersistent] public virtual string Extension { get { return Path.GetExtension(Name).Substring(1); } }

[LoadOnDemand] [StorageValueModifier(typeof(Compressor))] public abstract byte[] Content {get; set;}

public override string ProduceFtData(Culture culture) { string filteredContent = ""; try { filteredContent = Filter.Create(Extension).GetFilteredContent(Content); } catch {} return base.ProduceFtData(culture) + "\n File name: "+Name + "\n Content: "+filteredContent; }}

Page 98: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S example, step 2: using XxxFtIndexer

// Adding full-text indexer to// Domain.RuntimeServices collection// to make it to be periodically executeddomain.RuntimeServices.AddRuntimeService( "FtIndexer", domain.FtsDriver.GetFtIndexerType());

// Immediate full-text indexer execution// (use this approach only if necessary)RuntimeService ftIndexer = domain.FtsDriver.CreateFtIndexer(s, 1000000);

ftIndexer.Execute();

Page 99: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Full-text I&S example, step 3: full-text search queries

result = session.CreateQuery( "Select FtFile instances "+ "textsearch freetext “+ s.Utils.QuoteString(searchString)+" "+ "order by {FullTextRank}”).ExecuteArray();

Console.WriteLine(" Found files: {0}", result.Length);foreach (FtFile f in result) Console.WriteLine(" "+f.Name);

Page 100: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataServices, RuntimeServicesDataServices, RuntimeServices

• DataServices– Allow to use automatic transactions & transparent deadlock handling with

non-persistent classes (DataService descendants)

– Greatly simplify the development of services operating with persistent instances

– Two DataService types:• Shared: only one instance of such service can be created in each Session.

Session.GetService(…) method provides access to such services• Regular: any number of instances of such service can be created in each

Session; Session.CreateService(…) method allows to create such services

• RuntimeServices– DataServices of special type (RuntimeService descendants) can be

periodically executed in the separate Thread and Session maintained by the Domain

– Use Domain.RuntimeServices collection to add or remove RuntimeService from the execution queue

– RuntimeServices’ purpose is to execute maintenance tasks periodically

Page 101: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataServicesDataServices

DomainDomain

+Session

SessionBoundObjectSessionBoundObject AA

+RegisterService(…)

+RegisterServices(…)

+GetService(…)

+CreateService(…)

SessionSessionDataServiceDataService AA

SomeSharedServiceSomeSharedService AA

SomeServiceSomeService AASomeSharedService_ProxySomeSharedService_Proxy

*…1

1…*

SomeSharedService_ProxySomeSharedService_Proxy

*…1

1…1

- Actually – proxy objects

- Supports [Transactional] attribute

- Don’t support persistent properties

[ServiceType(DataServiceType.Shared)]

Page 102: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

RuntimeServicesRuntimeServices

DomainDomain

+Session

SessionBoundObjectSessionBoundObject AA

+RuntimeServices(…)

+GetService(…)

+CreateService(…)

SessionSession

+Add(RuntimeService)

+Remove(…)

RuntimeServicePoolRuntimeServicePool

-Session

-Thread

1…1

RuntimeServiceRuntimeService AA

+Execute(…)

+GetDelay(…)

DataServiceDataService AA

ThreadThread

1…1

1…1

*…1

*…1

Page 103: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataService example: direct SQL query execution

[ServiceType(DataServiceType.Shared)] public abstract class AggregateInfo : DataService { [Transactional(TransactionMode.TransactionRequired)] public virtual double MinimalAnimalAge() { Demand(...); DisableSecurity(); // Otherwise Session.CreateRealCommand() // call will fail. try { IDbCommand cmd = Session.CreateRealCommand(); cmd.CommandText = "Select min([Age]) from " + Session.Types[typeof(Animal)].RelatedView.Name; return cmd.ExecuteScalar(); } finally { EnableSecurity(); } } }

Page 104: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

RuntimeService example: XxxFtIndexe registration

// Adding full-text indexer to// Domain.RuntimeServices collection// to make it to be periodically executeddomain.RuntimeServices.AddRuntimeService( "FtIndexer", domain.FtsDriver.GetFtIndexerType());

Page 105: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

IDataObjectEventWatcher, ITransactionEventWatcherIDataObjectEventWatcher, ITransactionEventWatcher

• Implement IDataObjectEventWatcher interface by a shared DataService to make it being notified on DataObject-related events, such as instance creation, modification and deletion– All DataObject.OnXXX events are also “forwarded” to

IDataObjectEventWatcher implementors

• Implement ITransactionEventWatcher interface by a shared DataService to make it being notified on Transaction-related events, such as transaction creation, commit or rollback

• Implement IQueryEventWatcher interface by a shared DataService to make it being notified on Query\SqlQuery-related events, such as query execution. This interface allows to transparently modify underlying IDbCommand – e.g. to apply an additional restriction to its “where” clause

Page 106: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

TrackingSetTrackingSet

Purpose:• Tracks different types of activity of DataObject instances in the Session it was created in,

such as:– Instance creation– Modification– Deletion

• Its usage allows UI tier to get the information about changes made by arbitrary BLL action (method execution) – to properly refresh the controls displaying modified objects or their properties

Features:• Provides StartTracking() \ StopTracking() methods• Constantly maintains a set of IDs of instances that tried to perform specified activity

types while tracking is turned on• Automatically stops tracking on commit of transaction it was created in• Internally uses TrackingService – an implementor of IDataObjectEventWatcher and

ITransactionEventWatcher interfaces• Used by Adapter on Adapter.Update execution – to properly re-fill all changed objects

existing in it (as you know, changing a single property may lead to lots of modifications in the whole storage – all BLL logic is fully depend on you)

• Used by Offline layer on ObjectSet.ApplyChanges execution

Page 107: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security system featuresSecurity system features

• NTFS-like:– Based on Permissions, Principals (Users and Roles) and secured objects (DataObject

instances)– Supports Allowed \ Denied permissions– Permission inheritance:

• Each DataObject has SecurityParent property (DataObject)• There is a root object: ISecurityRoot implementor• Permissions are inherited by NTFS rules

• Completely extendable– Declare your custom permission types– Demand them in BLL code (e.g. in OnGetProperty methods)– Override built-in permission checks:

• All built-in permission demands are implemented in DataObject.OnXXX(…) methods, so they can be overridden

• You can override even DataObject.IsAllowed(…) and DataObject.Demand(…)– Declare your own User and Role types (descendants of our base classes)– Implement your own authentication methods

• Transparent– All security restrictions take effect immediately on any security-related changes in the Session -

it’s not necessary to reopen the Session or to invoke some method to apply new security restrictions:

• When you adding a User to some Role, this immediately affects on its security restrictions• Even rollback of the inner transaction immediately affects on security restrictions

• Extremely fast– Up to 4000000 permission demands per second on 2,8GHz P4!

Page 108: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security classes schemaSecurity classes schema

+SecurityParent

+Permissions

+Demand(…)

+IsAllowed(…)

DataObjectDataObject AA

+Roles

+AllRoles

PrincipalPrincipal AA

+Principals

RoleRole AA

+IsDisabled

+Authenticate(…)

UserUser AA -Password

+SetPassw.(…)

StdUserStdUser

+Inherit

AccessControlListAccessControlListIListIList

ICollectionICollection

IEnumerableIEnumerable

1…1

1…*

*…1

Provides access to the

following entries

PrincipalPrincipal

Allowed Permission SetAllowed Permission Set

Denied Permission SetDenied Permission Set

Built-in Permissions

ReadPermissionReadPermission

ChangePermissionChangePermission

AdministrationPermissionAdministrationPermission

+Clear()

+Union(…)

+Subtract(…)

+Intersect(…)

+IsSubsetOf(…)

+IsSupersetOf(…)

PermissionSetPermissionSet

IPermissionIPermission

*…*

*…1

1…1

Page 109: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security system: implementation stepsSecurity system: implementation steps

1. Implement your custom permission type(s)2. Put demands of new permissions into BLL code3. Allow\deny permissions on necessary entities 4. Authenticate Users in Sessions to get security

system working

Note: You may disable using of unauthenticated Sessions at all – see Domain.SecurityOptions.

Page 110: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security implementation example, step 1: delcaring custom permission

[Serializable]public class ChangeAccountPropertiesPermission: Permission{ private static ChangeAccountPropertiesPermission _value = new ChangeAccountPropertiesPermission(); private static ReadOnlyPermissionCollection gigList = new ReadOnlyPermissionCollection(new IPermission[] {});

public static ChangeAccountPropertiesPermission Value { get {return _value;} }

public override ReadOnlyPermissionCollection GrantedIfGrantedAnyOf {

get {return gigList;} }}

Page 111: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security implementation example, step 2: putting demands into BLL code

public abstract class Account: DataObject, IPerson { ... protected override void OnSetProperty(string name, Culture culture, object value) { if (name=="CurrentOrder") { Demand(OwnerPermission.Value); return; } if (this.Type.Fields[name]!=null && this.Type.BaseType.Fields[name]==null) { Demand(ChangeAccountPropertiesPermission.Value); return; } base.OnSetProperty(name, culture, value); } ...}

Page 112: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security implementation example, step 3: allowing permissions

Account a = (Account)session[...];

// Granting ChangeAccountPropertiesPermission// for someUser on aa.Permissions.Allow( someUser, ChangeAccountPropertiesPermission.Value);

// Granting OwnerPermission// for someRole on aa.Permissions.Allow( someRole, OwnerPermission.Value);

Page 113: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Security implementation example, step 4: using authenticated Session

Session session = domain.CreateSession("Alex", "AlexPassword");

// Security is enforced for User // with Name=="Alex“ here...

session.Authenticate("Dmitry", "DmitryPassword");

// Security is enforced for User // with Name=="Dmitry“ here...

Page 114: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Property validators, correctors, modifiersProperty validators, correctors, modifiers

• DataObjects.NET provides [Validator], [Corrector] and [StorageValueModifier] attributes

• [Validator] \ IPropertyValueValidator validates DataObject property value before it is actually set by DataObject.SetProperty method

• [Corrector] \ IPropertyValueCorrector corrects DataObject property value before it is actually set by DataObject.SetProperty method

• [StorageValueModifier] \ IPropertyStorageValueModifier pre-processes internal property value before persisting it to the database (e.g. compress it) and do the same on fetching it (e.g. decompress it)

• Such attribute-based approach allows you to implement custom validators, correctors and storage value modifiers

Page 115: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

[Validator], [Corrector], [StorageValueModifier] example

public abstract class Person: DataObject{ [Validator(typeof(DisallowLessThan), "Abraham")] [Validator(typeof(DisallowGreaterThan), "Zorro")] [Validator(typeof(DisallowEqualTo), "Guest")] [Validator(typeof(DisallowLongerThan), 32)] [Corrector(typeof(Truncator), 64)] public abstract string FullName {get; set;}

[LoadOnDemand] [StorageValueModifier(typeof(Compressor), CompressionMethod.Zip, CompressionLevel.Best)] public abstract System.Drawing.Image Image {get; set;}}

Page 116: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Add-ons mapAdd-ons map

• Adapter (DataObjects – DataSets gateway)• Offline layer (DataObjects.NET.Offline

namespace)• BindingManager (DataObjects – ASP.NET\

WindowsForms controls gateway)

Page 117: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Adapter componentAdapter component

• DataObjects.NET-DataSetgateway

• VisualStudio.NET integration• Well-known Fill-Update pattern• VersionID/VersionCode

validation (optimistic updates)• Uses TrackingSet on Update method execution –

to properly re-fill all rows related to all changed objects• Mapping:

– Stored in Adapter – Can be automatically generated in VS.NET Adapter designer– Can be automatically generated in the runtime

Page 118: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

DataObjects.NET.Offline namespaceDataObjects.NET.Offline namespace

• Advanced implementation of well-known DTO (Data Transfer Object) pattern

• Fill-Update pattern• VersionID\VersionCode validation• Offline analogues of almost all “online” types:

– ObjectSet (Session)– DataObject, DataObjectCollection– Savepoint, QueryResult, …

• Fill descriptors (“what to disconnect?”)• Update actions (“how to send back the updates?”)• Transparent “downloading”• Merge• Savepoints

Page 119: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: DTO (data transfer object) pattern concepts, part 1Offline layer: DTO (data transfer object) pattern concepts, part 1

• Server-side objects are MBR objects, so accessing their properties one-by-one may require significant amount of time, since each property access operation is actually executed are remote method invocation– It’s completely unacceptable to bind such objects to

WindowsForms UI controls – remote interaction is quite slow for this

– Moreover, server-side objects (“online” objects) are “live” – i.e. each operation on them requires a transaction. Consequences:

• If we’ll explicitly setup “wide” transaction boundaries (e.g. for the whole form fill\update operation), we’re risking to get large amount of deadlocks

• Otherwise (when no transaction boundaries are explicitly specified) we’re getting another problem: we may display inconsistent data (i.e. a data fetched in different transactions)

• How to solve these problems?

Page 120: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: DTO (data transfer object) pattern concepts, part 2Offline layer: DTO (data transfer object) pattern concepts, part 2

• Original DTO pattern (came from EJB) implies that a DTO type should be added for each persistent business entity:– All DTO types are serializable MarshalByValue types– Each DTO holds all property values of corresponding persistent

entity– A set of DTOs form a kind of “snapshot” of a part of the storage– DTO graphs are built on the application server and sent to the

client in the serialized form– Client may modify DTOs and send the modified graph back to

allow application server to detect and apply the changes

• How this pattern solves enumerated problems?– Lots of remote calls are replaced by a single call– DTO graph is formed in a single transaction, so it’s consistent– Updates from changed graph are also extracted in a single

transaction; optimistic locking is usually used to detect update conflicts in this case.

Page 121: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: DTO (data transfer object) pattern concepts, part 3Offline layer: DTO (data transfer object) pattern concepts, part 3

Disadvantages of regular DTO pattern:• No possibility to explicitly say which property values should

be available in each particular DTO graph (i.e. all properties of the object are “exported” to it). It’s quite inefficient to always send some large BLOB values to the client, even if it’s known that they aren’t required for the current client-side operation

• No possibility to transparently “download” the data that isn’t available in the DTO graph from the application server

• Sending back the whole DTO graph on update is also inefficient - usually relatively large graphs are delivered to the clients, but most of clients perform only few or even no changes at all. It’s better to send back the changes only.

Page 122: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: ObjectSet as advanced DTO pattern implementation, part 1Offline layer: ObjectSet as advanced DTO pattern implementation, part 1

ObjectSet brings all advantages of regular DTO pattern:– It is serializable, as well as any type it contains– It’s a MarshalByValue object, as well as any of

types it can contain, so it can freely traverse AppDomain boundaries in serialized form

– “Online” types provide ToOffline(…) methods producing their “offline” analogues (export feature)

– ObjectSet.ApplyChanges method sends all changes to the Session object bound to it

Page 123: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: ObjectSet as advanced DTO pattern implementation, part 2Offline layer: ObjectSet as advanced DTO pattern implementation, part 2

And in addition, ObjectSet resolved most annoying disadvantages of it:• FillDescriptors provide a way to explicitely say what types and properties

should be exported into the ObjectSet on each ToOffline(…) operation• ObjectSet supports transparent downloading of required, but not

available content (unavailable instances, property values and collection content). Since such operations are performed in different transactions, VersionID\VersionCode checks can be used to ensure the integrity of the ObjectSet after such operation. See LoadOptions enumeration for details.

• During the whole lifetime ObjectSet gathers information about all calls to methods of offline types marked by [OfflineBusinessMethod] attribute.

– This information is stored in MethodCallDescriptor objects. It's forwarded by ObjectSet to the application server when its ApplyChanges method is invoked, where completely the same sequence of method calls is executed, but on online objects

– Offline objects passed as method call arguments are certainly properly converted to corresponding online objects

– By doing this, we reproduce the whole sequence of updates made to the client-side ObjectSet on actual business entities "living" on application server – so our BLL code still works only on the server side

– Moreover, all changes made to online objects during ApplyChanges operation are transparently propagated to the offline entities on its completion!

Page 124: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: ObjectSet as advanced DTO pattern implementation, part 3Offline layer: ObjectSet as advanced DTO pattern implementation, part 3

Other ObjectSet features:• Excellent Savepoint support

– ObjectSet supports multiple Savepoints– Savepoint objects internally keeps their own undo log and MethodCallDescriptors list– Savepoint can be either rolled back, or removed. In the last case its undo log and

MethodCallDescriptors collection are merged with the previously created Savepoint– There is a SavepointController object those purpose is very similar to TransactionController in

the “online” layer – all base operations “require” savepoint, thus an exception can’t lead to impossibility to use the ObjectSet (e.g. because this exception was thrown in the middle of operation - when a part of ObjectSet contains inconsistent data) – ObjectSet-level transaction will be simply rolled back in this case, and it will revert to its previous consistent state

• Merge support– Any number of ObjectSets can be merged into one– VersionID\VersionCode validation can be enforced during this operation, see MergeOptions for

details– Lots of built-in operations in the ObjectSet (such as transparent content downloading) internally

use Merge method• TrackingSet is used on ApplyChanges execution

– A server-side service that actually executes all MethodCallDescriptors on the application server uses TrackingSet to additionally gather the information about all modified objects. Finally it delivers all changes back to the ObjectSet to make it being properly updated

• WindowsForms databinding support– DataObject type supports IEditableObject interface– DataObjectCollection type supports ITypedList and IListSource– IBindingList support and support of ITypedList \ IListSource in other types (such as

Offline.QueryResult) is currently in development.

Page 125: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: important notes, part 1Offline layer: important notes, part 1

• ObjectSet isn’t an ordered collection similar to QueryResult. Its purpose is to maintain and provide access to a set of Offline.DataObject instances by nearly the same fashion as Session provides access to their online analogues. So ObjectSet is much closer to Session, then to a QueryResult.

• Offline.QueryResult object is analogue of “online” QueryResult – use it if you need to pass an ordered collection of Offline.DataObjects. By the way, you can use even array in this case.

• ObjectSet is very similar to DataSet by its serialization behavior – i.e. when you searialize any object from the ObjectSet, the whole ObjectSet is serialized (since any ObjectSetBoundObject contains a reference to ObjectSet, but ObjectSet has references to all Offline.DataObject instances stored in it). So any ObjectSetBoundObject traverses AppDomain boundaries with its ObjectSet.

Page 126: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Offline layer: important notes, part 2Offline layer: important notes, part 2

• You should properly configure ClientSideCache object to successfully utilize ObjectSets on it - see Demo_DisconnectedSets for examples

• ClientSideCache object is used to cache ObjectModel and proxy assembly with offline type proxies on the client – both these parts can be very large (several megabytes in size), thus they’re delivered from the server just once, and further their locally cached snapshots are used

Page 127: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager componentBindingManager component

• DataObjects.NET-ASP.NET\WindowsForms controls gateway• VisualStudio.NET integration• Fill-Update pattern• Property-PropertyEditor bindings (in contrast to common Property-

ControlProperty bindings)• Brings two-way bindings to ASP.NET• Binds controls not to only DataObjects.NET types, but to any object

graphs• Supports DataObjects.NET-specific features – [Translatable]

properties, automatic transactions• DataBinder.Eval-like paths• Advanced error reporting via IErrorInfoControls:

– IPropertyErrorInfoControls show property-related errors on updates– IFormErrorInfoControls show combined error reports for the whole update

• BindingManager extenders (IBindingExtenders):– Common property-control binding behavior provided DefaultExtender– VersionCode validation via VersionCodeValidator– Custom (third-party) extenders

Page 128: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: implementation stepsBindingManager component: implementation steps

1. Add BindingManager to Windows or ASP.NET form2. Fill new properties (BindingExpression, BindingProperties) for each

control you want to bind with property of an object. Use DataBinder.Eval-like paths in BindingExpression.

3. Use BindingManager.Fill method to fill the controls with property values

4. Use BindingManager.Update method to fill object properties with values stored in controls

5. Extend\customize BindingManager functionality:– Register extenders for extending BindingManager behavior:

• Built-in: BindingManager.RegisterExtender(new VersionCodeValidator())• Create your own extenders by implementing IBindingExtender

– Implement IBindableControl to add BindingManager support to your custom control (the same can be achieved by implementing IBindingExtender)

– Implement IPropertyErrorInfoControl and IFormErrorInfoControl, and put implementers of these interfaces to the form to show error messages in a custom fashion

Page 129: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: correct updateBindingManager component: correct update

Page 130: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: BLL check failedBindingManager component: BLL check failed

Page 131: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: VersionCode check failedBindingManager component: VersionCode check failed

Page 132: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: .aspx code example

<uc1:PropertyEditorControl id="peName" runat="server" BindingExpression="p.Name"></uc1:PropertyEditorControl><BR>Surname =<asp:textbox id="tbSurname" runat="server" BindingExpression="p.Surname"></asp:textbox><uc1:ErrorInfoControl id=eSurname runat="server"></uc1:ErrorInfoControl><br><br><asp:button id="bSave" runat="server" Text="Save"></asp:button><asp:button id="bReload" runat="server" Text="Reload"></asp:button><br><br><asp:label id="lName" runat="server" Width="160px" Height="20px">Name</asp:label><asp:label id="lSurname" runat="server" Width="152px"

Height="20px">Surname</asp:label><br><hr><asp:Repeater id="repeater" runat="server"> <ItemTemplate> <asp:textbox id="tbFriendName" runat="server" BindingExpression="Container.DataItem.Name"></asp:textbox> <uc1:ErrorInfoControl id="eFriendName" runat="server" BindingExpression="Container.DataItem.Name" DESIGNTIMEDRAGDROP="17"></uc1:ErrorInfoControl> <asp:textbox id="tbFriendSurname" runat="server" BindingExpression="Container.DataItem.Surname"></asp:textbox> <uc1:ErrorInfoControl id="eFriendSurname" runat="server" BindingExpression="Container.DataItem.Surname" DESIGNTIMEDRAGDROP="17"></uc1:ErrorInfoControl><br> </ItemTemplate></asp:Repeater>

Page 133: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: .aspx.cs code example, part 1

private void Page_Load(object sender, System.EventArgs e){ DataObjects.NET.Session s = DataContext.Session; long personId = (long)Application["PersonID"]; p = (Person)s[personId]; if (!IsPostBack) { repeater.DataSource = p.Friends; repeater.DataBind(); bm.Fill(this); }}

private void bReload_Click(object sender, System.EventArgs e) { DataObjects.NET.Session s = DataContext.Session; long personId = (long)Application["PersonID"]; p = (Person)s[personId]; repeater.DataSource = p.Friends; repeater.DataBind(); bm.ClearErrors(this); bm.Fill(this);}

Page 134: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: .aspx.cs code example, part 2

private void bSave_Click(object sender, System.EventArgs e) { DataObjects.NET.Session s = DataContext.Session; long personId = (long)Application["PersonID"]; p = (Person) s[personId]; repeater.DataSource = p.Friends; // NOTE: do not call repeater.DataBind here as it would recreate // child controls and so reading of data would be impossible bm.ClearErrors(this); TransactionController tc = s.CreateTransactionController( DataObjects.NET.TransactionMode.NewTransactionRequired); try { bm.Update(this); if (!errors.HasErrors) { tc.Commit(); repeater.DataSource = p.Friends; repeater.DataBind(); bm.Fill(this); } else tc.Rollback(); } catch { tc.Rollback(); }}

Page 135: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: correct updateBindingManager component: correct update

Page 136: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: BLL check failedBindingManager component: BLL check failed

Page 137: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: VersionCode check failedBindingManager component: VersionCode check failed

Page 138: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: BLL check failedBindingManager component: BLL check failed

This screenshot from our upcoming product shows how BindingManager is used to detect and show errors thrown by BLL code on attempt to assign a new property value. Undelying .ascx control (“Summary” region) has no fill \ update \ error detection code at all – all these tasks are handled by BindingManager that is automatically provided for all regions (by parent .aspx page). Errors are automatically shown by special (usually - invisible) control implementing IXxxErrorInfo interfaces.

Page 139: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

BindingManager component: VersionCode check failedBindingManager component: VersionCode check failed

This screenshot shows how BindingManager is used to detect concurrent document update made by other user (via DataObject.VersionCode\CheckVersionCode methods). Undelying .ascx control (“Summary” region) has no fill \ update \ concurrent update detection code at all – all these tasks are handled by BindingManager that is automatically provided for all regions (by parent .aspx page). Errors are automatically shown by special (usually - invisible) control implementing IXxxErrorInfo interfaces.

Page 140: Move to the future. Think Objects, not SQL. © 2005 X-tensive.com

Thank you for watching!You’re ready to see DataObjects.NET Samples, Manual and .HxS\.Chm Help now

Thank you for watching!You’re ready to see DataObjects.NET Samples, Manual and .HxS\.Chm Help now

Product web site: http://www.x-tensive.com/Products/DataObjects.NET/DataObjects.NET Support Forum: http://www.x-tensive.com/Forum/

Product web site: http://www.x-tensive.com/Products/DataObjects.NET/DataObjects.NET Support Forum: http://www.x-tensive.com/Forum/