Starcounter
HomeDownloadDocsCommunity
2.3.2
2.3.2
  • Starcounter Documentation
  • Getting Started
  • Starcounter
    • Collapsing the Stack
      • Complexity and Scalability Tradeoff
      • The Future of Micro-Services
      • 10 Benefits of Collapsing the Stack
    • Integrated Database and Web Server
  • Hello World - Tutorial
    • Create a Database Class
    • Create a Real Time UI
    • First Interactive UI
    • Computed Properties
    • Expense Tracker
    • Cancel and Delete
    • The Next Step
  • Guides
    • Database
      • Database Classes
      • Data manipulation
      • Object Identity and Object References
      • Querying with SQL
      • Data Types
      • Relations
      • Inheritance
      • Sharing data
      • Database Configuration
      • Comparing Database Objects
      • Referential Integrity and Constraints
    • SQL
      • Identifiers
      • Path Expressions
      • Data operators
      • Joins
      • Aggregates
      • Comparisons and Logical Operators
      • Sorting
      • Fetch
      • Offset Key
      • Indexes
      • Literals
      • Query Plan Hints
      • Reserved words
      • Query for Database Classes
      • SQL Isolation Between Applications
    • Transactions
      • Short-Running Transactions
      • Long running transactions
      • Using Transactions
      • Running Background Jobs
      • Commit Hooks
      • Post-commit hooks
    • Typed JSON
      • JSON-by-example
      • Code-Behind
      • Data Bindings
      • Callback Methods
      • Responding with JSON
      • Accepting JSON in Requests
      • Primitive Arrays and Single Value Types
      • Typed JSON Internals
    • Blendable Web Apps
      • Starcounter MVVM
      • Palindrom
      • Client-Side Stack
      • Sessions
      • HTML Views
      • App Shell
      • Web Components
      • View Attaching
      • View Composing
      • HTML Compositions
      • HTML Views Blending Guidelines
      • Avoiding CSS conflicts
      • Debugging
    • Network
      • HTTP
      • Internal Self Calls
      • Middleware
      • Anonymous or Substitute Handlers
      • URL Aliases and Redirects
      • Network Gateway
      • Static File Server
      • WebSocket
      • Avoiding URI conflicts
      • TCP Sockets
      • UDP Sockets
    • Publishing Apps
    • Working with Starcounter
      • Release Channels
      • Installation
      • Starting and Stopping Apps
      • Administrator Web UI
      • Star CLI
      • StarAdmin CLI
      • StarDump CLI
      • Working in Visual Studio
      • Error Log
      • Using HTTPS on NGINX
      • Using HTTPS on IIS
      • Run Starcounter in Production
      • Weaver
      • Investigating App Crashes
      • Configuration Structure
      • Database Refactoring
      • Using Unload/Reload to Modify Database Schema
      • Kernel Questions and Answers
      • Log Files
  • Cookbook
    • Attach an HTTP Request to an Existing Long-Running Transaction
    • Cookie-Based Authentication
    • Timestamp on Object Creation
    • Creating Strongly Typed JSON Collections
    • Migrating From 2.2 to 2.3+
    • Multiple Pages
    • Icons
    • Proposed Project Structure
    • Acceptance Testing with Selenium
    • Requesting a User to Authenticate
    • How to delete unused tables and columns
Powered by GitBook
On this page
  • Introduction
  • Creating Database Classes
  • Properties and Fields
  • Constructors
  • Column Limit
  • Nested Classes
  • Create Database Objects
  • Deserializing to Database Classes
  • Casting From Non-Database Class
  1. Guides
  2. Database

Database Classes

Introduction

Marking a class in the code as a database class is done by setting the [Database] attribute. This class becomes a part of the database schema and all instances of the class are stored in the database.

Creating Database Classes

Database classes are, for the most part, created the same way as any other class. The main difference is under the hood; Public fields and public auto-created properties in the database class become database columns and properties with explicitly declared bodies, such as FullName in the example below become code properties, which are not stored as columns, but can be accessed in SQL queries:

[Database]
public class Person
{
    public string FirstName { get; set; } // Column
    public string LastName { get; set; } // Column
    public string FullName => $"{FirstName} {LastName}"; // Property
}

Database classes have to be declared as public, otherwise Starcounter will throw ScErrEntityClassNotPublic (SCERR4220) in weave-time.

Properties and Fields

We recommend using auto-implemented properties instead of fields in database classes because Starcounter will only allow auto-implemented properties in future versions to reduce maintenance and make it easier to be cross-platform. For developers this also means that weave-time will be faster and that error messages for edge cases will be clearer.

Access Levels

Preventing Fields From Becoming Database Columns

Use the Transient attribute to exclude fields and properties from becoming database columns. Fields or properties with the Transient attribute remain as regular .NET fields and properties and their values are stored on the heap and garbage collected with the objects they belong to. These fields and properties can't be queried with SQL.

Since transient properties and fields are regular .NET fields and properties, you can only retrieve their values with the initial object reference. Thus, transient fields or properties of objects that have been fetched from the database return the default value of the fields or properties:

using System;
using System.Linq;
using Starcounter;

namespace TransientSampleApp1
{
    [Database]
    public class Person
    {
        public string Name { get; set; }
        
        [Transient]
        public int ProcessSessionNumber { get; set; }
    }

    class Program
    {
        static void Main()
        {
            // Create the object of the Person class defined above
            var person = Db.Transact(() => new Person()
            {
                Name = "Jane Doe",
                ProcessSessionNumber = 1234
            });

            // It's the initial reference, so retrieving 
            // the value of the transient property works
            Console.Write(person.ProcessSessionNumber); // => 1234
            
            // Fetch the object from the database. 
            // The reference is not the initial reference anymore
            person = Db.SQL<Person>(
                "SELECT p FROM Person p WHERE p.Name = ?", "Jane Doe")
                .First();

            // Retrieving the non-transient property works
            Console.Write(person.Name); // => "Jane Doe"

            // Retrieving the transient property gives back the default value
            Console.Write(person.ProcessSessionNumber); // => 0
        }
    }
}

Due to the way reference navigation works with database objects, transient fields or properties of objects that are retrieved through reference navigation return the default value of the field or property:

using System;
using Starcounter;

namespace TransientSampleApp2
{
    [Database]
    public class Parent
    {
        [Transient] public string Note { get; set; }
    }

    [Database]
    public class Child
    {
        public Parent Parent { get; set; }
    }

    class Program
    {
        static void Main()
        {
            var child = new Child
            {
                Parent = new Parent() { Note = "A note" }
            };

            // Note will be null, since the child.Parent getter 
            // always returns a new instance of the Parent class.
            Console.Write(child.Parent.Note); // => null
        }
    }
}

Constructors

Constructors in database classes work the same way as they do in any other class. For example, this works as expected:

[Database]
public class Person
{
    public Person(string name)
    {
        this.Name = name;
        this.Created = DateTime.Now;
    }

    public string Name { get; set; }
    public DateTime Created { get; set; }
}

Column Limit

Database classes can have a maximum of 112 columns for performance reasons. Thus, this is not allowed:

[Database]
public class LargeTable
{
    public string Column1 { get; set; }
    public string Column2 { get; set; }
    // ...
    public string Column113 { get; set; }
}

Nested Classes

Nested database classes are not supported. The limitation is that inner database classes cannot be queried with SQL.

Create Database Objects

Database objects are created with the native program code operator new. For example, consider the following database class:

[Database]
public class Person
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

To create a new instance of this class, the syntax new Person() would be used, like this:

new Person()
{
    FirstName = "John",
    LastName = "Doe"
};

Deserializing to Database Classes

When deserializing to a database class, the deserialization should be wrapped in a transaction since it creates a new database object:

using Starcounter;
using Newtonsoft.Json;

namespace DeserializeDemo
{
    [Database]
    public class Person
    {   
        public string Name { get; set; }
    }

    class Program
    {
        static void Main()
        {
            DeserializePerson(@"{""Name"": ""Gimli""}");
        }

        public static void DeserializePerson(string json)
        {
            Db.Transact(() =>
                JsonConvert.DeserializeObject<Person>(json));
        }
    }
}

Casting From Non-Database Class

It's not possible to cast from a non-database class to a database class. For example, this is not possible:

public void UpdatePerson(ExternalApiModel data) 
{
    (data.ExternalApiPerson as Person).Name = "John";
}

Instead, database object creation should be done with the new operator.

PreviousDatabaseNextData manipulation

Last updated 7 years ago

Properties and fields have to be public, otherwise, ScErrNonPublicFieldNotExposed will be thrown with ScErrCantBindAppWithPrivateData (SCERR2149). This also applies to properties with the attribute.

All database write operations, such as creating new database objects have to be wrapped in a .

transaction
Transient