Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "Move to the future. Think Objects, not SQL. © 2005 X-tensive.com"— Presentation transcript:

1 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/

2 Important 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

3 Presentation plan Example Architecture overview Application layout examples Features overview –Low-level \ persistence features –Integrated features –Add-ons

4 Example (from Demo_FirstStep)

5 DataObjects.NET-based application develpment: key steps Declare a persistent type(s) Write Domain build code Use your persistent types

6 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;} }

7 Step 2: Domain build code domain = new Domain( "mssql://localhost/DataObjectsDotNetDemos", productKey); // At least one culture should be registered domain.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);

8 Step 3: persistent entity usage, part 1 long instanceID = 0; // You should work with Session by the same way using (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(); }

9 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(); }

10 Architecture Overview

11 DataObjects.NET-based application layers DataSets Client connection ways Application Server RDBMS Server DataObjects.NET (DAL) DataObjects.NET RDBMS driver RDBMS WebServices, Remoting (MarshalByValue) Remoting (MarshalByRef) ObjectSets DataObjects, DataServices (BLL) Offline Layer Adapter

12 DataObjects.NET structure Drivers MS SQL Oracle Firebird MaxDB/SAPDB Native Oracle Versionizing Full-text search Versionizing Full-text search Persistence Engine Persister PersisterForCollections Models DatabaseModel ObjectModel UpdateActions Schema Update Layer Extractor DatabaseModelComparer Builders ObjectModelBuilder DatabaseModelBuilder ProxyBuilder Drivers LayerRDBMS Abstraction Layer DAL (DataObject,…) Core Integrated Features Build-time only Read-only after build Runtime Offline Aapter Binding Manager Binding Manager Add-ons Low-level Features Versionizing Preloading Caching UpdateActionTranslator NamingManager … … Serialization Full-text search NTFS-like security … …

13 Domain build process schema Proxy Assembly Extractor RDBMS ObjectModelBuilder DatabaseModelBuilder ProxyBuilder UpdateActions Schema Upgrade SQL Script Schema Upgrade SQL Script UpdateActions Translator DatabaseModel DatabaseModelComparer ExtractedDatabaseModel ObjectModel.NET Reflection-provided model

14 RDBMS Abstraction Layer: types schema Session Domain ConnectionUrl Driver … *…1 SessionBoundObject A A +Session Driver A A Creates 1…1 PersisterForCollections A A Persister A A 1…1 1…**…1 Info A A Utils A A NamingManager A A UpdateAction Translator A A 1…*

15 Core 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)

16 Core types: schema +User … +ID +VersionID +TypeID +Permissions +State … A A DataObject Domain ID VersionID TypeID … IDataObject +Session SessionBoundObject A A MarshalByRefObject A A Session 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()

17 Persistent types and their proxies +ID +VersionID … DataObject Cat_Proxy Animal_Proxy A A +Age +LegCount Animal A A +Name HomeAnimal A A +Color Cat A A +Session +Domain SessionBoundObject A A #DisableSecurity() #EnableSecuriry() MarshalByRefObject A A 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”);

18 Proxies: abstract property implementation DataObject Cat_Proxy get_Age GetProperty(“Age”) object ageValue = … Lots of other calls happen here Type conversion is performed here. (object-to-int in this case) A A

19 Instances of the same entity in the application Domain domain Session s2 Session s1 +ID=10 +LegCount=2 Cat_Proxy +ID=10 +LegCount=5 Cat_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.

20 DataObject: 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

21 DataObject: type documentation

22 DataObject: properties, part 1

23 DataObject: properties, part 2

24 DataObject: public methods, part 1

25 DataObject: public methods, part 2

26 DataObject: protected methods, part 1

27 DataObject: protected methods, part 2

28 DataObject: protected methods, part 3

29 DataObject: protected methods, part 4

30 Application layout examples

31 Web: simple ASP.NET Host BLL & DAL ASP.NET Application (UI) Thin Client Server DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) DataSets ObjectSets RDBMS Offline Layer Adapter

32 Web: separate RDBMS server ASP.NET Host BLL & DAL ASP.NET Application (UI) Thin Client Application Server DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) DataSets ObjectSets Offline Layer Adapter RDBMS

33 Web farm ASP.NET Host BLL & DAL ASP.NET Application (UI) Thin Client Servers DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) DataSets ObjectSets Offline Layer Adapter 1…n RDBMS

34 Web: single app. server, multiple UI servers BLL & DAL DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) DataSets ObjectSets Offline Layer Adapter RDBMS ASP.NET Host 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

35 WindowsForms: simple BLL & DAL DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) DataSets ObjectSets Offline Layer Adapter 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

36 WindowsForms: single application server BLL & DAL DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) ObjectSets DataSets Offline Layer Adapter RDBMS Application Server WindowsForms Application (UI) WindowsForms Application (UI) Rich/Smart Client Client PC

37 Combined configuration: an example ASP.NET Host BLL & DAL RDBMS ASP.NET Application (UI) Server DataObjects.NET (DAL) Single Domain Object DataObjects.NET (DAL) Single Domain Object DataObjects, DataServices (BLL) DataSets ObjectSets Offline Layer Adapter Client PC WindowsForms Application (UI) WindowsForms Application (UI) Rich/Smart Client Thin Client

38 Feature overview

39 Features map Low-levelIntegrated 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

40 Automatic 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

41 Models 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

42 Model build schema (this slide was already shown earlier) Proxy Assembly Extractor RDBMS ObjectModelBuilder DatabaseModelBuilder ProxyBuilder UpdateActions Schema Upgrade SQL Script Schema Upgrade SQL Script UpdateActions Translator DatabaseModel DatabaseModelComparer ExtractedDatabaseModel ObjectModel.NET Reflection-provided model

43 Persistence 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

44 Persistence: 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

45 Persistent 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

46 Persistent field types schema Field ValueTypeCollectionField BooleanField ByteField LongField IntField EnumField DoubleField DecimalField ShortField SingleField TimeSpanField BlobField DateTimeField DelegateField StringField ObjectField NumericField GuidField IHasContainedFieldsField IReferenceHolderGroupField IReferenceHolderField IPairToTarget IPairToAllowed DataObjectCollectionField KeyField ReferenceField ObjectIDField PrimitiveField ExtrnalField StructField Instances of Field descendants handle persistence of each persistent property: Domain.ObjectModel.Types[“Person”].Fiel ds[“Age”] is an IntField instance handling persistence of Age property of Person.

47 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; } }

48 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;} }

49 Persistent properties: serializable types public abstract class Author: DataObject { public abstract Image Image {get; set;}... } Author a = (Author)session[aid]; a.Image.RotateFlip(RotateFlipType.Rotate90Fli pNone); a.Image = a.Image;

50 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 collection article1.Author = author; // Second way to do absolutely the same thing author.Articles.Add(article1); // So both parts of paired relationship are maintained // in sync automatically

51 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; } }

52 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);

53 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;

54 Relationship 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

55 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;} }

56 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;} }

57 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... }

58 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.");

59 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; }

60 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;} }

61 Referential 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

62 Transparent 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

63 Transparent persistence Cat sonya =... ; // Some code that fetches the Cat // instance sonya.Name = "Sonya"; // DataObjects.NET transparently // persist a new property value Cat kitty = sonya.Children[0]; // DataObjects.NET queries // a collection and fetches // the collection item Console.WriteLine(kitty.Parent.Name); // output: "Sonya"

64 VersionID/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

65 Transactions, 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

66 Automatic 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.)

67 Transactional method proxy code Animal Cat_Proxy Feed(food) r = base.Feed(food) r Actual method execution TransactionController creation code Try-Catch block Reprocessing check, Commit A A Possible jump in case with deadlock reprocessing

68 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; }

69 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(); }

70 Queries: 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)

71 Query-related types: schema QueryBase … A A SessionBoundObject A A Session Query +Text … Other QueryBase properties are overriden to be read-only SqlQuery … QueryParameter QueryParameterCall IList ICollection IEnumerable 1…1 *…1

72 Multilingual properties Multilingual ([Translatable]) properties A 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 properties A 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 Document En Ru De En Ru De [Translatable] [Indexed(FullText=true)]

73 [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; }... }

74 [Translatable], [Collatable] example: usage Culture cEn = domain.Cultures["En"]; Culture cRu = domain.Cultures["Ru"]; // [Translatable] property usage Book b = (Book)session.CreateObject(typeof(Book)); b.Title = "DataObjects.NET Internals"; session.Culture = cEn; // Switches current culture in the session b.Description = "DataObjects.NET is..."; session.Culture = cRu; // Switches current culture in the session b.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 usage Culture cEn = domain.Cultures["En"]; Culture cRu = domain.Cultures["Ru"]; session.Culture = cEn; // Switches current culture in the session QueryResult qr1 = session.CreateQuery( "Select Person object where {Name}>'Й' order by {Name}").Execute(); session.Culture = cRu; // Switches current culture in the session QueryResult qr2 = session.CreateQuery( "Select Person object where {Name}>'Й' order by {Name}").Execute();

75 Versionizing 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

76 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();

77 Performance-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)

78 Caching: instance caching architecture Domain 1…1 GlobalCache RDBMS SessionCache 1…* 1…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 Session Persister

79 Caching: collection content caching #Implementation Collection Session RDBMS Makes “dirty” job,- interacts with RDBMS driver [Implementation] PersisterForCollectioins - Allways empty or fully loaded - Handles Collection[int index] – like operations [ItemByIndex cache] - Can be partionally loaded - Handles Collection[long itemId] - like operations (Contains, Remove) [ItemByItemID cache] *…1 1…1 Interact with each other DataObjectCollection or ValueTypeCollection

80 Lazy 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

81 Lazy 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

82 Preloading 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

83 Delayed 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

84 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 here Console.WriteLine("Number of countries: {0}", qr.Count); Thread.Sleep(500); // Can be different here Console.WriteLine("Number of countries: {0}", qr.Count);

85 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 }

86 Integrated 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)

87 Configuration section example <section name="DataObjects.NET" type="DataObjects.NET.ConfigurationSectionHandler,Dataobjects.NET" />... <domain connectionUrl="mssql://localhost/DoPetShop" productKeyPath="..\..\..\ProductKey.txt" debugInfoOutputFolder="C:\Debug" foreignKeyCreationMode="CreateForeignKeys, CreateNullRows" securityMode="Modern" securityOptions="Standard, AllowCreateUnauthenticatedSessions" sessionSecurityOptions="Standard" updateMode="Perform" > <culture name="En" title="U.S. English" cultureName="en-US" compareOptions="IgnoreWidth,IgnoreKanaType" />...

88 Configuration section usage Domain domain = Configuration.DefaultDomain;... domain.Build();

89 Serialization 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.

90 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); }

91 Full-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).

92 Full-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

93 Full-text I&S: persistent types IFtObject ProduceFtData(FtData) ProduceFtData(FtData,Culture) string ProduceFtData(Culture) 1…1 [Translatable] [Indexed(FullText=true)] DataObject A A FtObject A A In fact – empty, since IFtObject support is implemented on DataObject level IsFtRecordUpToDate IDataObject +FtData +FtObject +IsIndexed FtRecord +ProduceFtData(…) +UpdateFtData(…) Derive your types from these to get full-text I&S support for them

94 Full-text I&S: drivers schema SBO A A Session 1…1 RuntimeService A A *…1 FtsDriver A A Creates full-text indexers LuceneFtsDriver NativeFtsDriver LuceneFtsIndexer NativeFtsIndexer *…1 1…* Execute full-text queries, create full-text indexers Domain FtsConnectionUrl FtsDriver Indexes IFtObjects (creates, updates and removes FtRecord objects) DataService A A

95 Full-text I&S: FtIndexer.Execute() steps IsFtRecordUpToDate IFtObject A null FtRecord RA IFtObject B IFtObject C FtRecord RB IsFtRecordUpToDate FtData IsIndexed FtData = “Old” IsIndexed FtRecord RC FtData IsIndexed FtRecord RX FtData IsIndexed 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

96 Full-text I&S: query execution schema QueryBase FtsDriver Persister Execute IDbCommand cmd yesno FtsDriver is native? FtsDriver.ExecuteQuery(ftsCondition) ftsResult BuildQueryCommand(…,ftsCondition,ftsResult) FtsResult==null ? Build IDbCommand yesno Build full-text search restriction & “order by” clause Build.”in (…)” restriction Execute Command ArrayList result yesno Reorder result by ranks Fts.Condition.OrderByRank==true && FtsResult!=null ? …

97 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; }

98 Full-text I&S example, step 2: using XxxFtIndexer // Adding full-text indexer to // Domain.RuntimeServices collection // to make it to be periodically executed domain.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();

99 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);

100 DataServices, 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

101 DataServices Domain +Session SessionBoundObject A A +RegisterService(…) +RegisterServices(…) +GetService(…) +CreateService(…) Session DataService A A SomeSharedService A A SomeService A A SomeSharedService_Proxy *…1 1…* SomeSharedService_Proxy *…1 1…1 - Actually – proxy objects - Supports [Transactional] attribute - Don’t support persistent properties [ServiceType(DataServiceType.Shared)]

102 RuntimeServices … Domain +Session SessionBoundObject A A +RuntimeServices(…) … +GetService(…) +CreateService(…) Session +Add(RuntimeService) +Remove(…) RuntimeServicePool -Session -Thread 1…1 RuntimeService A A +Execute(…) +GetDelay(…) … DataService A A Thread 1…1 *…1

103 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(); }

104 RuntimeService example: XxxFtIndexe registration // Adding full-text indexer to // Domain.RuntimeServices collection // to make it to be periodically executed domain.RuntimeServices.AddRuntimeService( "FtIndexer", domain.FtsDriver.GetFtIndexerType());

105 IDataObjectEventWatcher, 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

106 TrackingSet 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

107 Security 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!

108 Security classes schema +SecurityParent +Permissions … +Demand(…) +IsAllowed(…) DataObject A A +Roles +AllRoles Principal A A +Principals Role A A +IsDisabled … +Authenticate(…) User A A -Password … +SetPassw.(…) … StdUser +Inherit … AccessControlList IList ICollection IEnumerable 1…1 1…* *…1 Provides access to the following entries Principal Allowed Permission Set Denied Permission Set Built-in Permissions ReadPermission ChangePermission AdministrationPermission … +Clear() +Union(…) +Subtract(…) +Intersect(…) +IsSubsetOf(…) +IsSupersetOf(…) PermissionSet IPermission *…* *…1 1…1

109 Security system: implementation steps 1.Implement your custom permission type(s) 2.Put demands of new permissions into BLL code 3.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.

110 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;} }

111 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); }... }

112 Security implementation example, step 3: allowing permissions Account a = (Account)session[...]; // Granting ChangeAccountPropertiesPermission // for someUser on a a.Permissions.Allow( someUser, ChangeAccountPropertiesPermission.Value); // Granting OwnerPermission // for someRole on a a.Permissions.Allow( someRole, OwnerPermission.Value);

113 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...

114 Property 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

115 [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;} }

116 Add-ons map Adapter (DataObjects – DataSets gateway) Offline layer (DataObjects.NET.Offline namespace) BindingManager (DataObjects – ASP.NET\WindowsForms controls gateway)

117 Adapter component DataObjects.NET-DataSet gateway 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

118 DataObjects.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

119 Offline 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?

120 Offline 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.

121 Offline 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.

122 Offline 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

123 Offline 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!

124 Offline 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.

125 Offline 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.

126 Offline 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

127 BindingManager 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

128 BindingManager component: implementation steps 1.Add BindingManager to Windows or ASP.NET form 2.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

129 BindingManager component: correct update

130 BindingManager component: BLL check failed

131 BindingManager component: VersionCode check failed

132 BindingManager component:.aspx code example <uc1:PropertyEditorControl id="peName" runat="server" BindingExpression="p.Name"> Surname = <asp:textbox id="tbSurname" runat="server" BindingExpression="p.Surname"> Name Surname <asp:textbox id="tbFriendName" runat="server" BindingExpression="Container.DataItem.Name"> <uc1:ErrorInfoControl id="eFriendName" runat="server" BindingExpression="Container.DataItem.Name" DESIGNTIMEDRAGDROP="17"> <asp:textbox id="tbFriendSurname" runat="server" BindingExpression="Container.DataItem.Surname"> <uc1:ErrorInfoControl id="eFriendSurname" runat="server" BindingExpression="Container.DataItem.Surname" DESIGNTIMEDRAGDROP="17">

133 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); }

134 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(); }

135 BindingManager component: correct update

136 BindingManager component: BLL check failed

137 BindingManager component: VersionCode check failed

138 BindingManager 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.

139 BindingManager 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.

140 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/http://www.x-tensive.com/Products/DataObjects.NET/ DataObjects.NET Support Forum: http://www.x-tensive.com/Forum/http://www.x-tensive.com/Forum/ Product web site: http://www.x-tensive.com/Products/DataObjects.NET/http://www.x-tensive.com/Products/DataObjects.NET/ DataObjects.NET Support Forum: http://www.x-tensive.com/Forum/http://www.x-tensive.com/Forum/


Download ppt "Move to the future. Think Objects, not SQL. © 2005 X-tensive.com"

Similar presentations


Ads by Google