Skip to content

Designing Model Classes

Adam O'Neil edited this page Feb 27, 2019 · 13 revisions

Postulate is POCO-friendly. The only requirement for model classes is either that they have an Id property or an Identity attribute indicating the name of the key property.

The Id property type must correspond to the namespace containing the extension methods you're using (for example public int Id { get; set; } would be used for namespace Postulate.SqlServer.IntKey or public long Id { get; set; } would be used with Postulate.SqlServer.LongKey, etc)

To get more functionality for your model class types such as permission checks and trigger-like behavior, base your model class on Postulate.Base.Record.

Properties

Methods

If you're using SQL Model Merge to implement your model classes, you can shape the generated database objects using various attributes described next.

Primary and Unique Keys

Postulate offers [PrimaryKey] and [UniqueKey] attributes for use on properties. If you don't use either, then the Id property will be the primary key on the table. If you mark any columns with the [PrimaryKey] attribute, they will comprise the primary key on the table instead, and Id will be a unique constraint.

The [UniqueKey] attribute can be used at the property or class level. Use it at the property level to define a single-column unique constraint. Use it at the class level to define a composite unique key, for example:

[UniqueKey(nameof(OrganizationID), nameof(UpcCode))]
public class Item
{
    public int OrganizationID { get; set; }
    [MaxLength(100)]
    public string UpcCode { get; set; }
}

Foreign Keys

To mark a property as a foreign key, use the References attribute. For example:

public class OrderHeader { }

A model with a foreign key property to OrderHeader (on the "many" side of the relationship) would look like this:

public class OrderItem
{
    [References(typeof(OrderHeader))]
    public int OrderHeaderID { get; set; }
}

Note that this creates a foreign key constraint in the database, but does not behave like a navigation property. For this functionality, see navigation properties.

Inheritance

Base class columns are imported into your model class. If you need common columns or other model-wide conventions, you can create a custom base model class, for example:

public abstract class ModelConventions
{
    public DateTime DateCreated { get; set; }
    public string CreatedBy { get; set; }
    public DateTime? DateModified { get; set; }
    public string ModifiedBy { get; set; }
}

Once this is created, you would use it as the basis for your other model classes.

public class SampleEntity : ModelConventions { }
public class AnotherSample : ModelConventions { }

SampleEntity and AnotherSample will inherit the DateCreated, CreatedBy, DateModified, and ModifiedBy columns.

Insert- and Update-specific properties

There may be situations where you need certain rules or behaviors in effect when a model object is being updated vs inserted. Building on the previous example, let's say you want the CreatedBy property never to be updated, and you want the ModifiedBy property never to be inserted. Use the [ColumnAccess] attribute for this:

[ColumnAccess(SaveAction.Insert)]
public string CreatedBy { get; set; }

[ColumnAccess(SaveAction.Update)]
public string ModifiedBy { get; set; }

Another use case along these lines is a property that distinguishes tenants in a multi-tenant system. Normally, you always insert such a column, and never update it. Again, use the [ColumnAccess] attribute on those columns set to SaveAction.Insert.

Seed Data

To generate seed data with SQL Model Merge, add a static method to your model class called GetSeedData that returns DataTable. A good way to do this is to create an array of records of your model class type, then use the ToDataTable extension method on the array. Your model class must have one or more [PrimaryKey] properties so that SQL Model Merge can determine what seed records already exist when it performs a merge. The GetSeedData can go anywhere, but it must be static. Example:

public class WorkRole : AppTable
{
	[MaxLength(50)]
	[PrimaryKey]
	public string Name { get; set; }

	public static DataTable GetSeedData()
	{
		return new WorkRole[]
		{
			new WorkRole() { Name = "Development" },
			new WorkRole() { Name = "Business" },
			new WorkRole() { Name = "Design" }								
		}.ToDataTable(new SqlServerIntegrator(), excludeIdentity: true);
	}
}
You can’t perform that action at this time.