Object definition

Object definition of standard way:

public class User : DbObjectModel<User>
{
    public string Name { get; set; }
}

This will create an object model, and the object has a primary key named "Id", and some functions like Save, Delete, it also has some static functions like Find, FindById, FindBySql etc.

The default type of primary key "Id" is long, but we can define it as int or Guid:

public class User : DbObjectModel<User, int>
{
    public string Name { get; set; }
}

public class Room : DbObjectModel<Room, Guid>
{
    public string Name { get; set; }
}

Attention for this mode, the generic argument of DbObjectModel must same as defined class, otherwise it will cause something we do not expect.

If an object inherits from DbObjectModel, it provides partial update for the object, in partial update mode, the Update function only update the columns which changed, it doesn't call database if nothing changed.

var u = new User {Name = "tom"};
u.Save();
var u1 = User.FindById(u.Id);

For decimal type of field, we can define the precision of it by attribute, the default precision is (16,2):

public class Product : DbObjectModel<Product>
{
    [Precision(10,2)]
    public decimal Price;
}

Also we can create an object inherits from DbObject, and doesn't use property style:

public class User : DbObject
{
    public string Name;
}

In this way, it doesn't has extra functions in it, DbObject only provides an primary key column named "Id", so it means in this object it has two columns: Name and Id, the Id column type is long, and it's aoto increments primary key of this table.

It need to use DbEntry to operate it:

var u = new User();
u.Name = "tom";
DbEntry.Save(u);
var u1 = DbEntry.GetObject<User>(u.Id);

Also we can create an object inherits from System.Object by implements the interface IDbObject:

public class User : IDbObject
{
    public string Name;
}

In this way, it only has one column named "Name", nothing else. This type of objects can operate like other objects inherits from DbObject, only it don't has the primary key "Id", but you can define another primary key or multi-key for it, and this is the only way to define primary key by ourself in DbEntry.

For example, if we want the Name column to be the primary key of this table, we just need change the object model as following:

public class User : IDbObject
{
    [DbKey(IsDbGenerate=false), Length(50)]
    public string Name;
}

It is almost like the other base class NamedDbObject, so if we want use string type Name column as primary key, we can inherits from NamedDbObject, the little difference is the max length of Name column is 255 in NamedDbObject:

public class User : NamedDbObject
{
}

But in this case, the object does not have a system generated primary key, so the Save function could not work, we should use Insert or Update function by ourself.

If we want to define the auto increments primary key as other name, we can difine it as following:

public class User : IDbObject
{
    [DbKey]
    public long userID;
 
    public string Name;
}

The UnsavedValue is set to tell DbEntry how to judge the object is saved or not. In this case, if the userID equals 0, it means it is a new object, it should use INSERT sql to operate, otherwise, it means it is a saved object, it should use UPDATE sql to operate. This is how the Save function works.

The multi-key example:

public class MKey : IDbObject
{
    [DbKey(IsDbGenerate = false), Length(50)]
    public string Name;
 
    [DbKey(IsDbGenerate = false)]
    public int Age;
 
    public bool Actived;
}

All the object we just shows, all using the default information by itself like class name, field name and property name, so we don't need to defind other information for it, but if we need, we can map it as another name:

[DbTable("User")]
public class MyUser : IDbObject
{
    [DbColumn("Name")]
    public string theName;
}

This class also defined to operate same table in database, but in C#, it has a different name. it could used for sometimes we need to change table or column name but do not want change the C# code name. Or it can use for some column name which is not a legal C# identity:

public class User : IDbObject
{
    [DbColumn("User Name")]
    public string Name;
}

There are some other attributes we can use:

public class User : IDbObject
{
    [Length(50), AllowNull]
    public string Name;
 
    [StringColumn(IsUnicode=true, Regular=CommonRegular.EmailRegular)]
    public string Email;
}

The Length attribute defined the max length of the string field, it will use for generate create table sql statement and use for validate function. Length only works for string field, don't define it to other type field.

If a string field is not defined by Length attribute, it means it has unlimited size, the mapped database type is "text" or "ntext" (in sql server).

The AllowNull attribute defined the field which allows null value, in DbEntry, it also using for genernate create table sql and for validate function. In DbEntry, the field which is defined AllowNull attribute or Nullable type field will be deemed as allow null field. You don't need to define this attribute to Nuallable field, in fact, it's not allowed as well, so the AllowNull attribute only works for string field.

StringColumnAttribute also works for create table and validate function. IsUnicode tells DbEntry if the column type is unicode, by default, this argument is true, and the Regular tell the validate function if it need check the field by using a regular expression. CommonRegular provides two common regulars: Email and Url.

By now, all fields we defined are mapped to a table column. In DbEntry, it works for field and property which has both get and set. And it only works for public and protected member. So if we want a member does not map to a column, we can set it as private. Also, there is an attribute named ExcludeAttribute, the field or property which defined by this attribute will not mapped by DbEntry too:

public class User : IDbObject
{
    public string Name;
 
    [Exclude]
    public bool isUserInput;
 
    private int times; // exclude too
 
    public int code { get { return 0; } } // exclude too
}

IndexAttribute is set to tell DbEntry create index in create table function. It has 3 fields. ASC is bool type to tell DbEntry which sort type of this index. UNIQUE is bool type to tell DbEntry if this index is unique. IndexName is string type to tell DbEntry what name of this index, if this argument is not set, it will use “IX_” plus table name plus column name as the index name. And if two or more columns set as the same index name, it means it is a composed index.

The following code shows a composed index Name_Age with DESC, UNIQUE mode:

class MyTest : IDbObject
{
    [DbKey]
    public long Id = 0;
 
    [Index("Name_Age", ASC = false, UNIQUE = true)]
    public string Name = null;
 
    [Index("Name_Age", ASC = false, UNIQUE = true)]
    public int Age = 0;
}

The join table syntax is not in query syntax, it is an attribute set on object definition.

The following code shows two tables SampleData and TheAge join on SampleData.Id equals TheAge.Id:

[JoinOn(0, typeof(SampleData), "Id", typeof(TheAge), "Id")]
public class JoinTable1 : IDbObject
{
    [DbColumn("SampleData.Id")] public long Id;
    public string Name;
    public UserRole Role;
    public DateTime JoinDate;
    public int Age;
}

Because we can join more than 2 tables, but we can not ensure the order of the attributes we get by using .net reflection, so it has an order argument to tell DbEntry the order of join syntax.

The following code shows 3 tables join by using JoinOnAttribute:

[JoinOn(0, typeof(SampleData), "Id", typeof(TheAge), "Id", CompareOpration.Equal, JoinMode.Inner)]
[JoinOn(1, typeof(SampleData), "Id", typeof(LeafingEnum), "Id", CompareOpration.Equal, JoinMode.Inner)]
public class JoinTable2 : IDbObject
{
    [DbColumn("SampleData.Id")] public long Id;
    [DbColumn("SampleData.Name")] public string Name;
    public UserRole Role;
    public DateTime JoinDate;
    public int Age;
    [DbColumn("LeafingEnum.Name")] public string EnumName;
}

There are 4 attributes for relation objects -- HasOne, HasMany, BelongsTo, HasAndBelongsToMany. These attributes only works for the classes which inherits from DbObjectModel.

For the model which defined these 4 attributes, also allowed DbColumn attribute, if there is no DbColumn attribute on it, it will use table name plus "_Id" as the column name. And for the property which defined HasOne HasMany HasAndBelongsToMany, the OrderBy paramter could be used for define order by clause of relation SQL.

[DbTable("People")]
public class Person : DbObjectModel<Person>
{
    public string Name { get; set; }

    [HasOne(OrderBy = "Id DESC")]
    public PersonalComputer PC { get; set; }
}

public class PersonalComputer : DbObjectModel<PersonalComputer>
{
    public string Name { get; set; }

    [BelongsTo, DbColumn("Person_Id")]
    public Person Owner { get; set; }
}

More details about relation object will be discussed in Relations.

The following object defined normal fileds, the type of those fields include string, enum, DateTime, bool and Nullable int, by this point, it will be used for many samples of this tutorials:

public enum UserRole
{
    Manager,
    Worker,
    Client
}
 
public class SampleData : DbObjectModel<SampleData>
{
    [Length(50)] public string Name { get; set; }
    public UserRole Role { get; set; }
    public DateTime JoinDate { get; set; }
    public bool Enabled { get; set; }
    public int? NullInt { get; set; }
  
    public static SampleData New(string name, UserRole role,
	DateTime joinDate, bool enabled, int? nullInt)
    {
        return new SampleData
        {
            Name = name,
            Role = role,
            JoinDate = joinDate,
            Enabled = enabled,
            NullInt = nullInt,
        };
    }
}

ComposedOf Field

In DbEntry 4.0 we can use ComposedOf attribute to define a interface as a field in the model class as well:

public interface ILocation
{
    string Phone { get; set; }

    [AllowNull, Length(2, 50)]
    string Address { get; set; }

    [DbColumn("MyNumber")]
    int Number { get; set; }

    int? Wow { get; set; }
}

public class CoUser : DbObjectModel<CoUser>
{
    public string Name { get; set; }

    [ComposedOf]
    public ILocation Location { get; private set; }
}

To use it just like:

var user = new CoUser { Name = "tom", Location = { Phone = "123456", Address = "test" } };
user.Save();

user = CoUser.FindById(user.Id);
Assert.IsNotNull(user);
Assert.AreEqual("tom", user.Name);
Assert.AreEqual("123456", user.Location.Phone);
Assert.AreEqual("test", user.Location.Address);

var list = CoUser.Find(p => p.Location.Phone == "123456");
Assert.AreEqual("test", list[0].Location.Address)

Last edited Apr 20, 2014 at 2:27 AM by lifeng, version 37

Comments

peng16870 May 12, 2013 at 7:27 AM 
Exclude : 排除列
如果只是作为查询使用, save的时候不使用怎么做?

mamboer Sep 18, 2009 at 2:56 AM 
Quite nice,but somewhat strange that you use abstract class for the partial mode,which is very different from other main ORM.
Is it the last resort?