DataObjects.Net is .NET library that dramatically simplifies development of data and business tiers of the database application. It provides:

  • Methodology that standardizes and simplifies development of persistent classes and services operating with them providing very clear separation of your business and data tiers from other parts of an application. Essentially DataObjects.Net requires you to build a hierarchy of your persistent and business objects over two base classes: DataObject and DataService. It provides more then 20 attributes controlling almost any persistence or behavior-related aspect. Don't worry about this number – you can know nearly 5 of them to start using DataObjects.Net.
  • Persistence framework handling all object persistence related tasks transparently. Moreover, this framework allows to almost forget that the underlying database exists – it handles even database schema updates. This framework fully supports inheritance, persistent interfaces, relations and collections, object queries, full-text indexing and search, multilingual properties and a lot of other features. Use of this framework makes most part of your data tier automatically compatible with Microsoft SQL Server 2005 \ 2000, MDSE 2000, Microsoft Access, Oracle, Firebird (formerly – Interbase), MaxDB (formerly – SAP DB) (free, but one of the most featured database servers) without any additional code.
  • Transactional services allowing to almost forget that your business and data objects operate in the concurrent transactional environment. Transactional services intercept calls of your business tier methods and wraps them into transactions (outermost or nested) providing that if exception or deadlock occurs, no data will be changed. These services are capable of re-processing method call on deadlock exceptions (and similar). This behavior is provided completely transparently for developers, but nevertheless it's highly configurable.
  • Security system supporting per-instance access control lists (allow-deny lists), permissions (custom permissions), security principals (users and roles) and permission inheritance. Its primary goal is to make usage of business objects completely safe, even when these objects are publicly available – via .NET Remoting, for example.
  • All is initially remotable &ndash any persistent object or business service can be marshaled to another application domain via .NET Remoting (as well as all other DataObjects.Net-related objects, e.g. Query). This means that you can access your data and business tier from a completely different network or across the Internet with almost no additional code. DataObjects.Net supports two marshalling scenarios: access-by-reference for regular DataObjects and by-value marshalling for so-called offline entities (data transfer objects).

DataObjects.Net allows you to focus on code of business tier and application data model – it completely solves a set of problems that could take up to 80% of development time. Just imagine, what does it means – to find and fix an error (e.g. thread deadlock or “the latest update wins”-like problem) that appears only under high concurrency conditions. Sometimes it's not so easy to even imagine that such a problem exists!

DataObjects.Net can be used in virtually any application that accesses a relational database. You simply add a reference to DataObjects.Net.dll to your project to start using it.

Check out Product Benefits section!

DataObjects.Net is shipped with DataObjects.Net PetShop (DoPetShop) sample. This is a DataObjects.Net-based clone of the famous Microsoft .NET Pet Shop. Here are the most interesting comparison facts:

  • DataObjects.Net PetShop Screenshot
    DoPetShop contains ~ 50 Kb of data and business tier code while .NET Pet Shop – 140 Kb (including Business Logic Layer, Model, DAL and two its implementations – SQL Server DAL and Oracle DAL, but without BLL\OrderInsert.cs – read further about this). This means that use of DataObjects.Net reduced business and data tier code size by 3 times! Note that DoPetShop shipped with latest versions of DataObjects.Net includes a lot of additional features – for example, it utilizes security system, full-text search and serialization features.
  • Moreover, it includes administration module. First version of DoPetShop that was very close to the original .NET Pet Shop by the feature set was much smaller – its data and business tier code size was less then 20 Kb, so it was nearly 7 times smaller then its original!
  • DoPetShop utilizes DataObjects.Net access control system – this means that DataObjects.Net takes complete care about the authentication and authorizes access to application's business objects
  • DataObjects.Net brings true full-text search to DoPetShop, while .NET Pet Shop always uses like to locate necessary products
  • DataObjects.Net, and consequently, DoPetShop, fully supports 6 database server platforms (Microsoft SQL Server 2005 \ 2000, MSDE 2000, Microsoft Access, Oracle, Firebird, SAP DB \ MaxDB) while .NET Pet Shop – only SQL Server 2005 \ 2000 \ MSDE and Oracle
  • DoPetShop RemotingClient Screenshot
    DoPetShop provides full, but safe access to its data and business tier via .NET Remoting (while .NET Pet Shop allows to perform only one simple operation via its web service), so generally you can perform any activity remotely, e.g. such manipulations with persistent objects as creation, changing, deletion. Allowed activities certainly depend on your security permissions. See DoPetShop Remoting Client – this WindowsForms application really does this (it shows a lot of other features also, see the screenshot).
  • There is only one Query in DoPetShop sensitive to database server version! I.e. different values are assigned to Text property of this Query instance depending on current database server.

There are some other benefits of DoPetShop, e.g. it provides more information on cart and order-related pages, but may be the most exciting one is that it's extendable with much less amount of efforts. You can find more information about this sample further.

We believe DataObjects.Net currently is one of the best tools on the market. It's a complete DAL and RAD tool for your business tier. It offers the richest feature set. We hope you'll enjoy using it!

Major features of DataObjects.Net:

  • Transparent persistence: you don't need to write data access code for insert\update\delete operations – DataObjects.Net persists instances transparently for you. This means that you should never think about invoking a Save\Load-like methods also – DataObjects.Net handles such tasks completely transparently making you fell you're working with ordinary object instances. Transparent persistence has similar benefits as automatic garbage collection – you shouldn't worry about persisting your changes. Note that this doesn't mean all changes are persisted immediately – DataObjects.Net optimizes the update sequence (see delayed updates feature description).
  • Automatic database schema building\upgrading (database schema includes tables, views, columns, indexes, etc.): on each startup of Domain its database can be upgraded to support new persistent model (persistent model changes e.g. when you modify, add or delete some persistent classes). Upgrade process doesn't destroy existing data.
  • Instance identification: DataObjects.Net uses unique 64-bit integers in the database scope to identify instances; upcoming versions will support GUID and 32-bit integers also.
  • Querying: use DataObjects.Net query language (see Query description) or perform a direct SQL query (see SqlQuery description) to select the instances you're interested in. An example of DataObjects.Net query: "Select Animal instances where {LegCount}=4 order by {Name}" – this query fetches all four-legged Animal instances, as well as instances of Animal descendants – e.g. Cats and Dogs). More difficult example selecting grandparents instances: "Select Animal instances where {Children[{Children.count}>0].count}>0". DataObjects.Net query language supports sub-queries, joins, distinct and full-text search part in criteria.
  • Inheritance support: as it was mentioned, DataObjects.Net fully supports inheritance for persistent classes. But we went much further here: DataObjects.Net supports so-called persistent interfaces. This unique feature allows you to query for objects implementing some interface and refer to its persistent properties inside query criteria.
  • Highly configurable persistence for instance properties:
    • Multilingual properties (unique feature): you can mark any property with [Translatable] attribute to specify that it should store independent versions of its value for each Culture registered in you Domain. This and a set of other features dramatically simplifies development of multilingual database applications.
    • References: don't worry about loading referenced objects, as well as persisting them properly – just write a code like cat.Parent = grandCat. Note that if you'll try to delete the grandCat instance after executing this code, cat will be notified (because it holds a reference to grandCat). Also after grandCat deletion cat.Parent will be automatically set to null.
    • Collections: cat.Children.Add(kittyCat). Paired collections and reference properties (unique feature) are supported (so kittyCat.Parent can be automatically set to cat for the code above). “Contained” collections and references are supported as well (if cat.Children is marked by [Contained] attribute, all instances referenced by Children collection will be deleted on cat's deletion).
    • Structs: any struct type can be used as type of persistent property or item of collection. DataObjects.Net persists such properties into multiple database columns according to the structure of struct type.
    • [Serializable] properties: properties which type is marked by [Serializable] attribute are persisted without any additional coding – they're stored as byte streams (BLOBs). DataObjects.Net handles references to other persistent entities inside such serialized graphs in a special way making them to point to appropriate instances on each deserialization (note that DataObjects.Net is transactional, so you can find different versions of the same persistent instance in different sessions).
    • SQL-related attributes are available for almost any type of persistent properties ([SqlType], [Length] and similar ones)
  • Indexing: you can apply [Indexed] attribute to a persistent property to tell DataObjects.Net that an index should be created on it, or use [Index] attribute to describe a compound index
  • Full-text indexing & search (unique feature): DataObjects.Net supports full-text indexing and search. Microsoft Search (available for SQL Server 2005\2000 only) and DotLucene (free, RDBMS-independent) full-text indexing and search drivers are shipped with DataObjects.Net. Built-in managed wrapper for Microsoft Index Service filters helps to index almost any document\file type stored in database or externally. In particular, you can index the following document types: Microsoft Office files (.doc, .dot, .rtf, .xls, .ppt, etc...), HTML files (.htm, .html), Adobe PDF files and so on.
  • Built-in NTFS-like security system  (unique feature): DataObjects.Net has extremely powerful security system allowing to define the permission for any action (e.g. method execution or property access), grant or deny it for a set of security principals (users and roles) on some persistent instances (like on folders in NTFS) and enforce its presence by demanding it inside methods or property assessors of persistent types or DataServices. It brings NTFS-like security into your business tier with almost zero additional coding!
    We want to underline two very important features of this subsystem:
    • Performance: security system is extremely fast – all passed permission demands are cached, effective permission sets for any cached DataObject instance are cached too, internal security notification layer allows any cached DataObject instance to notify all dependent cached instances on changes in its effective permission set… Normally a subsequent permission demand on the same instance is quite fast. This allows DataObjects.Net to execute up to 4000000 permission demands per second on 2,8GHz P4! It's almost impossible to implement a security system having the similar features and performance without implementing all other caching features DataObjects.Net has. Just imagine the nightmare of implementing the similar part in your DAL!
    • Immediate effect: all security restrictions take effect immediately on any security-related changes in Session – so it's not necessary to reopen Session \ invoke some method to apply new security restrictions. When you adding a User to some Role, granting permission for him or for some role it belongs to, this immediately affects on its security restrictions in the current Session – so all is transparent even in this case. Even rollback of nested transaction (or rollback to savepoint) immediately affects on security restrictions.
  • Rich data import and export capabilities:
    • Serialization: DataObjects.Net completely supports .NET Serialization, so you can serialize or deserialize a graph with persistent instances using binary or SOAP formatters. Custom formatters are supported as well.
    • Adapter component exports persistent instances to DataSet and imports back the changes.
    • Offline layer provides advanced implementation of well-known DTO (Data Transfer Object) pattern. It allows to export a set of DataObject instances to serializable, marshal-by-value ObjectSets, pass them to the client to operate with them locally and propagate the changes made to them back to the application server.
    • BindingManager component (unique feature) establishes two-way bindings between ASP.NET\WindowsForms controls and object properties. It brings Property-PropertyEditor bindings in contrast to common Property-ControlProperty bindings – we think this approach is also interesting for developers.
    • All import\export tools support VersionID\VersionCode validation (optimistic updates).
  • Versionizing provides an ability to “see” the database at any previous state (point in time). This feature works only if it's turned on for the whole Domain. Currently Versionizing is supported by SQL Server 2005 \ 2000 driver only.
  • Automatic transactions (unique feature – it quite differs from standard COM+ automatic transactions, read more about it further): DataObjects.Net is completely transactional. It supports automatic transactions (started with a method call or property access and committed or rolled back based on result; transactional behavior is controlled by attributes) as well as manual transactions (you can also start/commit/rollback a transaction manually); nested transactions and savepoints are also supported.
  • Transparent deadlock handling (unique feature): in a highly concurrent environment deadlocks are regular. In case of a deadlock one of the deadlocked transactions (deadlock victim) is aborted by the database server. Normally it should be re-executed later by theapplication – this situation is called “deadlock handling”. DataObjects.Net is capable of handling deadlocks transparently – if a deadlock occurs during an automatic transaction, it can be transparently handled by re-processing of the outermost transaction (re-calling of corresponding method).
  • Data services (unique feature) allows to use all DataObjects.Net transactional capabilities (automatic transactions, transparent deadlock handling) with non-persistent classes (DataService descendants). This feature greatly simplifies development of services operating with persistent instances. It's very convenient to use this conception to develop common services of an application, such as Logging Service. Data services are very close to ServicedComponents of .NET, the difference is that they are much faster (method calls are intercepted by the runtime proxies rather then .NET transparent proxies, no COM+ services are used) and easier to use.
  • Runtime services (unique feature) are data services of special type (RuntimeService descendants) that can be periodically executed in a special Thread and Session inside Domain. The purpose of runtime services is to perform various maintenance tasks periodically. For example, all IFtIndexer implementors are runtime services that periodically update full-text indexes.

Transparent persistence, built-in security system, automatic transactions, data services, .NET Remoting and DTO pattern support allows you to build not only the data access tier with DataObjects.Net, but the whole business tier of your application.

Supported technologies\platforms:

  • 6 RDBMS platforms: DataObjects.Net makes most part of your business tier automatically compatible with Microsoft SQL Server 2005\2000, MDSE 2000, Microsoft Access, Oracle, Firebird (formerly – Interbase), MaxDB (formerly – SAP DB) without any additional code
  • 2 full-text search engines: Microsoft Search (supports SQL Server 2005\2000 only) and DotLucene (free, RDBMS-independent)
  • .NET Framework 2.0: DataObjects.Net supports all essential features of .NET 2.0 (generics, nullable types)
  • Mono: core part of DataObjects.Net runs on Mono, you can find some screenshots here
  • .NET Framework 1.1/2.0 languages: C# and VisualBasic.NET are supported. DataObjects.Net also should support almost any programming language available for .NET – JScript.NET, J#, Managed C++ (actually this is a feature of almost any .NET application)
  • .NET Remoting: any persistent instance or DataService can be marshaled to another Application Domain via .NET Remoting (as well as all other DataObjects.Net-related objects, e.g. Query). This means that you can access these objects from a completely different network or across the Internet with almost no additional code; moreover, you can use offline layer – it's an implementation of DTO (data transfer object) pattern for DataObjects.Net.
  • Distributed transactions: you can enlist any Session instance in the MS DTC\COM+ distributed transaction.

Performance-related features:

  • Caching speeds up queries and access to referenced instances by reusing already fetched data. There are two-level caching in DataObjects.Net:
    • Global cache is shared between all Sessions operating in the same Domain (usually there is one Domain object per application except NLB clustering case). Global cache has fixed size, and contains instantiation data for a set of most frequently accessed objects. The information fetched from this cache is always validated – i.e. DataObjects.Net uses it only when it really actual. Usually validation requires much less time then fetching, and moreover, DataObjects.Net gathers validation data on any query (since it's also quite cheap) – e.g. running a query with LoadOnDemand option (such queries are quite fast, since they fetch only two numeric columns from the storage) may lead to zero subsequent fetches (queries), because all necessary data could be taken from global cache, as well as validated.
    • Session cache is a WeakReference-based storage caching already instantiated objects, as well as information necessary to instantiate them or validate the instantiation data cached in the global cache. For example, if you execute two subsequent queries and process two object sets returned by these queries, none of processed objects with the same ID will be instantiated twice, moreover, instantiation data necessary to create it won't be even fetched twice from the underlying IDataReaders (certainly if these operations are executed in the same transaction).
    • Two-level caching layer is only a part of caching techniques used in DataObjects.Net. Lot of data \ intermediates are cached internally – for example, all evaluated effective permission sets and effective user's role sets are cached by the security system.
  • Lazy instantiation: DataObjects.Net instantiates (creates in-memory object and fetches its state from the database) referenced objects on the first access attempt. E.g. 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] and cat.Children[0].Friends[1].Parent). The actual number of fetched objects depends on if these instances were accessed before and the state of caches.
  • Lazy loading (or load-on-demand): [LoadOnDemand] attribute applied on the persistent property notifies DataObjects.Net that value of this property should be fetched from the database on the first attempt to access it. This feature is highly required while working with instances containing large amount of rarely accessed data (e.g. BLOBs). The similar behavior is available for queries – it's possible to specify that QueryResult should internally contain only IDs of selected objects rather then complete instantiation data, although will transparently transform these IDs into the DataObject instances for you (by performing additional queries). This feature helps to keep very large result sets in memory.
  • Preloading: DataObjects.Net provides a set of ways to preload a group of load-on-demand properties or collections for the specified set of objects, or specified set of objects itself – by minimal amount of queries.
  • Delayed updates: almost all types of updates are delayed by default and flushed to the database as late as it's possible. Late update sequence is normally performed by much less number of queries. This feature is almost equivalent to using BeginUpdate()\EndUpdate() blocks in the early versions of DataObjects.Net, but always whenever it's possible.
  • DataObjects.Net is optimized for Microsoft SQL Server 2005\2000. Our performance tests & profiling are performed mainly on SQL Server 2005. Moreover, we provide support for a set of special features of this database server family (for example, full-text search and *(max) data types).
  • Only low-level ADO.NET classes are used. We don't use DataSet, DataTable and other high-level ADO.NET classes to increase the performance.

DataObjects.Net is NOT an ordinary object-relation mapping tool or API. The main differences in comparison with the most commonly used tools are:

  • DataObjects.Net is more than a persistence layer – its feature set (transparent persistence, built-in security system, automatic transactions, data services and .NET Remoting support) allows you to build not only the data access tier, but the whole business tier on it.
  • DataObjects.Net uses classes, their properties and special attributes as the only information source. No additional files or a database with existing structures is required. For example, if you want some field to be indexed, you can apply [Indexed] attribute on it and DataObjects.Net will automatically create a new index on corresponding database column.
  • DataObjects.Net can automatically upgrade existing database schema to a new version without any data loss.

Note that you can still use the database built by DataObjects.Net in another application or even in the DataObjects.Net-based application by any traditional way. So if your problem would be better solved by direct SQL queries you can use them.

The benefits of using existing relational database to store objects are obvious: the evolution of the relational database is a process that we have been watching for the past 30 years; the most successful relational databases are quite reliable and fast, so to build a new level on the top of them is one of the most risk-free ways to implement a new object-oriented database system. This way of storing objects data is also known as Object-Relational Mapping (ORM).

Here you can find detailed description of DataObjects.Net features.