Demo_Books is a technology-oriented console application demonstrating the following basic DataObjects.Net capabilities:

  • Working with the set of persistent classes (look Model.cs);
  • Multilingual properties;
  • [LoadOnDemand] attribute, built-in optimistic locking mechanism, and immediate/delayed updates (BeginUpdate and EndUpdate methods );
  • Queries, including full-text queries (so Microsoft Search is required to run this sample on the Microsoft SQL Server);
  • References and collections.

Model.cs

using System;
using System.Data;

using DataObjects.NET;
using DataObjects.NET.Attributes;
using DataObjects.NET.FullText;

namespace Demo_Books.Model
{
  public abstract class DumpableObject: FtObject
  {
    public virtual void Dump()
    {
      Console.WriteLine("");
      Console.WriteLine("ID: {0}, Type: {1}",ID,Type.Name);
    }
  }


  [IndexAttribute("IX_NSS",new string[] {"Name","Surname","SecondName"}, Unique=true)]
  public abstract class Person: DumpableObject
  {
    [Length(64)]
    [Collatable]
    [Indexed]
    public abstract string Name {get; set;}

    [Length(64)]
    [Collatable]
    [Indexed]
    public abstract string SecondName {get; set;}

    [Length(64)]
    [Collatable]
    [Indexed]
    public abstract string Surname {get; set;}
    
    public virtual string FullName {
      get {
        return Name+" "+SecondName+" "+Surname;
      }
    }

    [Indexed]
    [Nullable]
    // No DateTime support in Trial, so date is string
    public abstract string DOB {get; set;}
    
    [Translatable]
    [SqlType(SqlType.Text)]
    [LoadOnDemand]
    public abstract string Info {get; set;}
    
    public override string ProduceFtData(Culture culture) {
      return 
        "\n "+FullName+
        ((GetProperty("DOB")!=null) ? "\n DOB: "+DOB : "") +
        "\n "+GetProperty("Info",culture);
    }

    public override void Dump()
    {
      base.Dump();
      Console.WriteLine("FullName: {0}",FullName);
      Console.WriteLine("DOB: {0}",(this["DOB"]==null)?"null":DOB.ToString());
      foreach (Culture c in Domain.Cultures)
        Console.WriteLine("Info-{0}: {1}",c.Name,this["Info",c]);
    }
  }


  public abstract class Author: Person
  {
    [Indexed]
    public abstract Account Account {get; set;}

    [Translatable]
    [SqlType(SqlType.Text)]
    [LoadOnDemand]
    public abstract string Resume {get; set;}
    
    [Contained]
    [ItemType(typeof(Book))]
    [PairTo(typeof(Book),"Author")]
    public abstract DataObjectCollection Books {get;}
    
    public override string ProduceFtData(Culture culture) {
      return 
        base.ProduceFtData(culture) + 
        "\n Book count: "+Books.Count +
        ((Account!=null)?
          ("\n Account: "+Account.ProduceFtData(culture)) : "") +
        "\n "+GetProperty("Resume",culture);
    }

    public override void Dump()
    {
      base.Dump();
      try {
        Console.WriteLine("Account: {0} (bank {1})",this.Account.Number,this.Account.Bank.Title);
      } catch (Exception) {};
      foreach (Culture c in Domain.Cultures)
        Console.WriteLine("Resume-{0}: {1}",c.Name,this["Resume",c]);
      Console.WriteLine("Book count: {0}",Books.Count);
    }
  }


  public abstract class Book: DumpableObject
  {
    [Indexed(Unique=true)]
    public abstract string ISBN {get; set;}
    
    [Collatable]
    [Indexed]
    public abstract string Title {get; set;}
    
    [Translatable]
    [LoadOnDemand]
    [Indexed]
    public abstract string Description {get; set;}
    
    [Indexed]
    public abstract Author Author {get; set;}

    public override string ProduceFtData(Culture culture) {
      return 
        "\n ISBN: "+ISBN +
        "\n Title: "+Title +
        ((Author!=null) ?
          ("\n Author: "+this.Author.ProduceFtData(culture)) : "") +
        "\n "+GetProperty("Description",culture);
    }

    public override void Dump()
    {
      base.Dump();
      Console.WriteLine("ISBN:   {0}",ISBN);
      Console.WriteLine("Title:  {0}",Title);
      Console.WriteLine("Author: {0}",Author==null?"null":Author.FullName);
      foreach (Culture c in Domain.Cultures)
        Console.WriteLine("Description-{0}: {1}",c.Name,this["Description",c]);
    }
  }


  public abstract class Account: DumpableObject
  {
    [Indexed(Unique=true)]
    public abstract string Title {get; set;}
    
    [Indexed]
    public abstract Bank Bank {get; set;}
    
    [Indexed]
    public abstract long Number {get; set;}
    
    [Nullable]
    [LoadOnDemand]
    public abstract byte[] SecretInfo {get; set;}
    
    protected override void OnCreate()
    {
      throw new InvalidOperationException("This constructor can't be used with this type!");
    }
    
    // It's upper caller always requires a transaction, so
    // TransactionMode.Disabled is appropriate here.
    [Transactional(TransactionMode.Disabled)] 
    protected virtual void OnCreate(string title)
    {
      Title = title;
      base.OnCreate();
    }
    
    protected override void OnCreateDeserializable()
    {
      // We should guarant uniquenes of initial Title
      // value, because two or more instance with empty
      // Title can be persisted during the deserialization, 
      // that will lead to violation of 'unique' constraint.
      // An actual Title value will be set during the
      // deserialization.
      Title = Guid.NewGuid().ToString(); 
      base.OnCreateDeserializable();
    }
    
    protected override void OnSetProperty(string name, Culture culture, object value)
    {
      if (name=="Title" && (value==null || ((string)value)==""))
        throw new InvalidOperationException("Title shouldn't be empty.");
    }

    public override string ProduceFtData(Culture culture) 
    {
      return 
        "\n Title: "+Title +
        ((Bank!=null)?
          ("\n Bank: "+Bank.Title) : "") +
        "\n N: "+Number.ToString();
    }

    public override void Dump()
    {
      base.Dump();
      try {
        Console.WriteLine("Title:  {0}",Title);
        Console.WriteLine("Number: {0}",Number);
        Console.WriteLine("Bank:   {0}",Bank.Title);
      } catch (Exception) {};
    }
  }


  public abstract class Company: DumpableObject
  {
    [Collatable]
    [Indexed]
    public abstract string Title {get; set;}

    [Collatable]
    [Indexed]
    public abstract string Address {get; set;}

    [Indexed]
    // No DateTime support in Trial, so date is string
    public abstract string RegistrationDate {get; set;}
    
    [Nullable]
    [Indexed]
    public abstract Account Account {get; set;}
    
    [Translatable]
    [SqlType(SqlType.Text)]
    [LoadOnDemand]
    public abstract string Info {get; set;}
    
    public override string ProduceFtData(Culture culture) {
      return 
        "\n Title: "+Title+
        "\n Address: "+Address+
        "\n Registeration date: "+RegistrationDate.ToString(culture.Info) +
        ((Account!=null) ?
          ("\n Account: "+Account.ProduceFtData(culture)) : "") +
        "\n "+GetProperty("Info",culture);
    }

    public override void Dump()
    {
      base.Dump();
      Console.WriteLine("Title: {0}",Title);
      Console.WriteLine("Address: {0}",Address);
      Console.WriteLine("Reg.Date: {0}",RegistrationDate);
      try {
        Console.WriteLine("Account: {0} (bank {1})",this.Account.Number,this.Account.Bank.Title);
      } catch (Exception) {};
      foreach (Culture c in Domain.Cultures)
        Console.WriteLine("Info-{0}: {1}",c.Name,this["Info",c]);
    }
   }


  public abstract class Bank: Company
  {
    [Contained]
    [ItemType(typeof(Account))]
    [PairTo(typeof(Account),"Bank")]
    public abstract DataObjectCollection Accounts {get;}
    
    public override string ProduceFtData(Culture culture) {
      return 
        base.ProduceFtData(culture) + 
        "\n Account count: "+Accounts.Count;
    }

    public override void Dump()
    {
      base.Dump();
      Console.WriteLine("Account count: {0}",Accounts.Count);
    }
  }
}

Books.cs

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Globalization;

using System.Data;
using System.Data.SqlClient;
using DataObjects.NET;
using DataObjects.NET.FullText;
using DataObjects.NET.RuntimeServices;
using Demo_Books.Model;


namespace Demo_Books
{
  class Books
  {
    static Domain   domain;
    static string   connectionUrl = "";

    [STAThread]
    static void Main(string[] args)
    {
      Console.WriteLine("DataObjects.Net: Books demo\n");
      SelectDatabase();
      CreateDomain();
      CreateData();
      DumpData();
      ModifyData();
      DumpData();
      FullTextQuery();

      Console.Write("\nPress Enter to close... ");
      Console.ReadLine();
    }

    static void SelectDatabase()
    {
      while (connectionUrl=="") {
        Console.Write("Select database server to use:\n" +
                      "1) Microsoft SQL Server\n" +
                      "2) Oracle\n" +
                      "3) Native Oracle\n" +
                      "4) SAP DB\n" +
                      "> ");
        string dbType = Console.ReadLine();
        switch (dbType) {
          case "1":
            Console.WriteLine("Selected: Microsoft SQL Server.");
            connectionUrl = "mssql://localhost/DataObjectsDotNetDemos";
            break;
          case "2":
            Console.WriteLine("Selected: Oracle.");
            connectionUrl = "oracle://admin:admin@localhost/Demos";
            break;
          case "3":
            Console.WriteLine("Selected: Native Oracle.");
            connectionUrl = "nativeoracle://admin:admin@localhost/Demos";
            break;
          case "4":
            Console.WriteLine("Selected: SAP DB.");
            connectionUrl = "sapdb://admin:admin@localhost/Demos";
            break;
          default:
            Console.WriteLine("Illegal selection.");
            break;
        }
      }
    }
    
    static void CreateDomain()
    {
      Console.WriteLine("Reading product key...");
      string productKeyFile = @"..\..\..\..\ProductKey.txt"; 
      string productKey = "";
      if (File.Exists(productKeyFile))
        using (StreamReader sr = new StreamReader(productKeyFile)) {
          productKey = sr.ReadToEnd().Trim();
        }

      Console.WriteLine("");
      Console.WriteLine("Building domain...");
      domain = new Domain(connectionUrl, productKey);
      Console.WriteLine("Connection URL: {0}",domain.ConnectionURL);
      Console.WriteLine("Driver:         {0}",domain.Driver.Info.Description);

      domain.RegisterCulture(new Culture("En","U.S. English", new CultureInfo("en-us",false)));
      domain.RegisterCulture(new Culture("Ru","Russian", new CultureInfo("ru-ru",false)));
      domain.Cultures["En"].Default = true;
      domain.RegisterTypes("Demo_Books.Model");
      #if DEBUG
        domain.DebugInfoOutputFolder = @"C:\Debug";
      #endif
      domain.Build(DomainUpdateMode.Recreate);
      Console.WriteLine("  OK");
    }
    
    static void CreateData()
    {
      Console.WriteLine("Creating data...");

      StringBuilder sbBigStringEn = new StringBuilder();
      for (int i = 0; i<5; i++)
        sbBigStringEn.Append("About author... ");
      
      StringBuilder sbBigStringRu = new StringBuilder();
      for (int i = 0; i<5; i++)
        sbBigStringRu.Append("Об авторе... ");
      
      Culture cEn = domain.Cultures["En"];
      Culture cRu = domain.Cultures["Ru"];
      
      using (Session s = domain.CreateSession()) {
        s.BeginTransaction();

        // Authors
      
        Author a1  = (Author)s.CreateObject(typeof(Author));
        a1.Name    = "Alex";
        a1.Surname = "Yakunin";
        a1.Info    = "DataObjects.Net, " + sbBigStringEn.ToString();
        s.Culture  = cRu;
        a1.Info    = sbBigStringRu.ToString();
        s.Culture  = cEn;
        
        Author a2  = (Author)s.CreateObject(typeof(Author));
        a2.Name    = "Dmitriy";
        a2.Surname = "Voronov";
        a2.Info    = sbBigStringEn.ToString();
        s.Culture  = cRu;
        a2.Info    = sbBigStringRu.ToString();
        s.Culture  = cEn;
        
        Author a3  = (Author)s.CreateObject(typeof(Author));
        a3.Name    = "Alex";
        a3.Surname = "Chernikh";
        a3.Info    = sbBigStringEn.ToString();
        s.Culture  = cRu;
        a3.Info    = sbBigStringRu.ToString();
        s.Culture  = cEn;
        
        // Books
        
        Book bk1   = (Book)s.CreateObject(typeof(Book));
        bk1.ISBN   = "AABBCC";
        bk1.Title  = "DataObjects.Net Internals";
        bk1.Description = "DataObjects.Net is... " + sbBigStringEn.ToString();
        s.Culture  = cRu;
        bk1.Description = "DataObjects.Net есть... " + sbBigStringEn.ToString();
        s.Culture  = cEn;
        bk1.Author = a1;
        
        Book bk2   = (Book)s.CreateObject(typeof(Book));
        bk2.ISBN   = "BBCCDD";
        bk2.Title  = "DataObjects.Net Manual";
        bk2.Description = "DataObjects.Net is... " + sbBigStringEn.ToString();
        s.Culture  = cRu;
        bk2.Description = "DataObjects.Net есть... " + sbBigStringEn.ToString();
        s.Culture  = cEn;
        bk2.Author = a2;
        
        Book bk3   = (Book)s.CreateObject(typeof(Book));
        bk3.ISBN   = "1DHF23";
        bk3.Title  = "DataObjects.Net at Glance";
        bk3.Description = "DataObjects.Net is... " + sbBigStringEn.ToString();
        s.Culture  = cRu;
        bk3.Description = "DataObjects.Net есть... " + sbBigStringEn.ToString();
        s.Culture  = cEn;
        bk3.Author = a1;
        
        // Persons
        
        Person p1  = (Person)s.CreateObject(typeof(Person));
        p1.Name    = "Vladimir";
        p1.Surname = "Putin";
        p1.Info    = "Putin "+sbBigStringEn.ToString();
        s.Culture  = cRu;
        p1.Info    = "Путин "+sbBigStringRu.ToString();
        s.Culture  = cEn;

        // Banks

        Bank b1    = (Bank)s.CreateObject(typeof(Bank));
        b1.Title   = "Bank of Russia";
        
        Bank b2    = (Bank)s.CreateObject(typeof(Bank));
        b2.Title   = "Other Bank";
        
        // Accounts

        Account ac1= (Account)s.CreateObject(typeof(Account), "Account 1");
        ac1.Number = 10102020;
        ac1.Bank   = b1;
        ac1.SecretInfo = new byte[] {1,2,3,4};
        a1.Account = ac1;
        a2.Account = ac1;
        
        Account ac2= (Account)s.CreateObject(typeof(Account), "Account 2");
        ac2.Number = 10103030;
        ac2.Bank   = b1;
        ac2.SecretInfo = new byte[] {2,3,4};
        a3.Account = ac2;
        
        Account ac3= (Account)s.CreateObject(typeof(Account), "Account 3");
        ac3.Number = 20303030;
        ac3.Bank   = b2;
        
        s.Commit();
      }
      Console.WriteLine("  OK");
    }

    static void ModifyData()
    {
      Console.WriteLine("Modifying data...");
      using (Session s = domain.CreateSession()) {
        s.BeginTransaction();
        
        Query q = new Query(s,"Select Book instances");
        DataObject[] qr = q.ExecuteArray();
        foreach (Book b in qr) {
          b.Title += " - Second edition";
        }
        
        s.Commit();
      }

      Console.WriteLine("  Transaction rollback");
      using (Session s = domain.CreateSession()) {
        s.BeginTransaction(IsolationLevel.Serializable);

        SqlQuery q = new SqlQuery(s,typeof(Book));
        DataObject[] qr = q.ExecuteArray();
        foreach (Book b in qr) {
          b.Title += " - Third edition";
        }

        s.Rollback();
      }
      Console.WriteLine("  OK");
    }

    static void FullTextQuery()
    {
      Console.WriteLine("Full-text search...");
      if (!domain.ExtractedDatabaseModel.ExtractedInfo.FullTextIndexingRunning) {
        Console.WriteLine("  Full-text search\\indexing isn't available.");
        Console.WriteLine("  LikeExpression mode (slow) will be used.");
      }
  
      // Let's update full-text data immediately!
      using (Session s = domain.CreateSession()) {
        FtIndexer ftIndexer = (FtIndexer)s.CreateService(typeof(FtIndexer), 1000000);
        ftIndexer.Execute();
      }
      
      string  searchString;
      Culture searchCulture = null;
      do {
        Console.Write("  Type the text to search: ");
        searchString = Console.ReadLine();
        Console.Write("  Select the culture to search in (1-En, 2-Ru): ");
        switch (Console.ReadLine()) {
        case "1":
          searchCulture = domain.Cultures["En"];
          break;
        case "2":
          searchCulture = domain.Cultures["Ru"];
          break;
        }
      } while (searchString.Trim()=="" || searchCulture==null);
      Console.WriteLine("  Searching for \"{0}\":", searchString);
      using (Session s = domain.CreateSession()) {
        DataObject[] result;
        if (domain.ExtractedDatabaseModel.ExtractedInfo.FullTextIndexingRunning)
          result = s.CreateQuery(
            "Select DumpableObject instances " +
              "textsearch freetext "+s.Utils.QuoteString(searchString)+" " +
                "within "+s.Utils.QuoteString(searchCulture.Name)+" culture " +
              "order by {FullTextRank} desc").ExecuteArray();
        else
          result = s.CreateQuery(
            "Select DumpableObject instances " +
              "textsearch likeExpression "+s.Utils.QuoteString("%"+searchString+"%")+" " +
                "within "+s.Utils.QuoteString(searchCulture.Name)+" culture " +
              "order by {ID}").ExecuteArray();
        foreach (DumpableObject o in result)
          o.Dump();
      }
      Console.WriteLine("  OK");
    }

    static void DumpData()
    {
      Console.WriteLine("\nDatabase dump:");
      using (Session s = domain.CreateSession()) {
        s.BeginTransaction();
        
        Query q = new Query(s,"Select DumpableObject instances");
        DataObject[] qr = q.ExecuteArray();
        foreach (DumpableObject o in qr)
          o.Dump();
          
        s.Commit();  
      }
      Console.WriteLine("");
    }
  }
}