.netTiers
Help Wanted! If you are using .netTiers and find it as invaluable as we do, please consider giving back to the .netTiers team by helping with our effort to fully document .netTiers. To help, simply create an account and you will then be able to edit this wiki.

Database and SQL

Modified: 09/02/2010 01:18 PM by mikiurban - Categorized as: Documentation

Edit

Database Model and Sql:

How does .netTiers figure out what to generate? .netTiers uses the rich meta data CodeSmith provides that is found from database in order to create a complete object domain based on your data relationships. The SchemaExplorer provides information on Database, Table, View, Indexes, StoredProcedures, and more. There are several important aspects to keep in mind when creating or using an existing database. In a perfect world, you would try and utilize these best practices.

Edit

Best Practices:

  • Table names are singular and Pascal Case. ex. Order, Product, File
  • Fields are Pascal Case, ex. FirstName, LastName, MiddleInitial
  • Description is provided for Tables, Columns, and Keys as an Extended Attribute.
    • For MSSQL, it is "MS_Description" key for the extended attribute.
  • Using the ParseDbColDefaultVal option it is possible to get .netTiers to default your entity properties using your database defaults. Bear in mind that only simple constants and a few functions are supported (getdate(), getutcdate()). User defined schema defaults are not supported
  • .netTiers can not assume relationships, it's imperative that you actually create Foreign keys associated to the parent table's primary key.

Edit

Relationships

Using Composition in your Model. Given the following Tables, we will create a One to One relationship, meaning for every person there can be a single unique Contact record for that Person.

1-- Create the Base Relationship, has a unique primary key amongst both tables. 2CREATE TABLE [dbo].[Person]( 3 [PersonId] [int] NOT NULL, 4 [Name] [varchar](50) NOT NULL, 5CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED 6( 7 [PersonId] ASC 8) WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] 9) ON [PRIMARY] 10 11 12--Create the other side of the 1:1 relationship 13CREATE TABLE [dbo].[Contact]( 14 [ContactPersonId] [int] NOT NULL, 15 [ContactNumber] [varchar](30) , 16CONSTRAINT [PK_Contact] PRIMARY KEY CLUSTERED 17( 18 [ContactPersonId] ASC 19) WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] 20) ON [PRIMARY] 21 22 23-- Example of 1:1 relationship 24-- The contact primary key is the is also a foreign key relationship to the PersonId. 25-- Therefore creating a 1:1 relationship. 26 27ALTER TABLE [dbo].[Contact] WITH CHECK ADD CONSTRAINT [FK_Contact_Person] 28FOREIGN KEY([ContactPersonId]) 29REFERENCES [dbo].[Person] ([PersonId]) 30GO


Your generated entities would look like this. For every Person entity, you would have a composite property to the Contact entity.

1/// <summary> 2/// Holds a Contact object 3/// which is related to this object through the relation Contact 4/// </summary> 5[BindableAttribute()] 6public Contact Contact 7{ 8 get { return entityData.Contact; } 9 set { entityData.Contact = value; } 10}


For every Contact entity, since you are working with the actual foreign key, that is your primary key, you would get a PersonId and a PersonIdSource of type Parent.

1private Person _contactPersonIdSource = null; 2 3/// <summary> 4/// Gets or sets the source <see cref="Person"/>. 5/// </summary> 6/// <value>The source Person for ContactPersonId.</value> 7[Browsable(false), BindableAttribute()] 8public virtual Person ContactPersonIdSource 9{ 10 get { return this._contactPersonIdSource; } 11 set { this._contactPersonIdSource = value; } 12}


Edit

Using Collection Relationships.

Given a modified for brevity version of some of the familiar Northwind Database.

1 -- ORDER 2 -- A familiar pattern in database design, which is the Header/Details pattern in which 3 -- one table contains the the overall summary of all the 4 -- detail items contained within a particular detail Table. 5 -- Think of a single order always *could* potentially have many order items. 6 -- Just like on your grocery receipt. 7 8 -- the Header Table 9 CREATE TABLE [dbo].[Orders]( 10 [OrderID] [int] IDENTITY(1,1) NOT NULL, 11 [CustomerID] int NOT NULL, 12 [EmployeeID] [int] NULL, 13 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 14 ( 15 [OrderID] ASC 16 ) ON [PRIMARY] 17 ) ON [PRIMARY] 18 19 20 -- the Details Table 21 CREATE TABLE [dbo].[Order Details]( 22 [OrderID] [int] NOT NULL, 23 [ProductID] [int] NOT NULL, 24 CONSTRAINT [PK_Order_Details] PRIMARY KEY CLUSTERED 25 ( 26 [OrderID] ASC, 27 [ProductID] ASC 28 ) ON [PRIMARY] 29 ) ON [PRIMARY] 30 31 -- the Customer Table 32 CREATE TABLE [dbo].[Customer]( 33 [CustomerID] [int] NOT NULL, 34 [ContactName] [nvarchar](30) NULL, 35 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 36 ( 37 [CustomerID] ASC 38 ) ON [PRIMARY] 39 ) ON [PRIMARY] 40 41 42 43 Example of Many To One relationship: 44 -- This creates a simple relationship between the Orders Table and the Customer, so that in your 45 -- for every OrderEntity entity, you will have a CustomerId foreign key integer, and a 46 -- CustomerIdSource entity of type Customer nested composite entity in your Order class. 47 48ALTER TABLE [dbo].[Order] WITH NOCHECK ADD CONSTRAINT [FK_Order_Customer] 49 FOREIGN KEY([CustomerID]) 50 REFERENCES [dbo].[Customer] ([CustomerID]) 51 GO 52 53 Example of One to Many Relationship: 54 -- This creates a one to many relationship between the Order and the Order Detail tables. 55 -- Meaning, in your generated code, 56 -- for every Order entity, you will have an OrderDetailCollection of type TList<OrderDetail>. 57 -- This means you can have an entire 58 -- list of OrderDetail entities within your Order entity. 59 60ALTER TABLE [dbo].[Order Details] WITH NOCHECK ADD CONSTRAINT [FK_Order_Details_Orders] FOREIGN KEY([OrderID]) 61 REFERENCES [dbo].[Orders] ([OrderID]) 62 GO




Edit

Many To Many Relationship

The last of the intrinsic relationships are the Many to Many relationships. The m:m relationship consists of 3 tables, the left table, the junction table and the right table and is essentially a flexible 1:m relationship but with two tables using a junction to facilitate the relationship. An example of this, is the Orders, Order Details, and the Products table. Where the order can have many Order Details that all have Products, while at the same time any one Product can be in any Order Detail which is associated with many orders. This junction table holds both references to the left and right tables. The nice thing about this relationship is that we can infer from the Order entity that the Order Entity will have a collection of type TList and similarly will see an Orders collection for every Product because of the junction table of Order Datails.

1 ALTER TABLE [dbo].[Order Details] WITH NOCHECK ADD CONSTRAINT [FK_Order_Details_Orders] FOREIGN KEY([OrderID]) 2 REFERENCES [dbo].[Orders] ([OrderID]) 3 GO 4 5 Alter TABLE [dbo].[Order Details] WITH NOCHECK ADD CONSTRAINT [FK_Order_Details_Products] FOREIGN KEY([ProductID]) 6 REFERENCES [dbo].[Products] ([ProductID]) 7 GO




1 // Order Entity: 2 3 /// <summary> 4 /// Holds a collection of ProductsFromOrderDetails objects 5 /// which are related to this object with junction table OrderDetails 6 /// </summary> 7 [BindableAttribute()] 8 public TList<Products> ProductsCollection_From_OrderDetails 9 { 10 get { return entityData.ProductsCollection_From_OrderDetails; } 11 set { entityData.ProductsCollection_From_OrderDetails = value; } 12 } 13 14 15 //Product Entity: 16 17 /// <summary> 18 /// Holds a collection of OrdersFromOrderDetails objects 19 /// which are related to this object through junction table OrderDetails 20 /// </summary> 21 [BindableAttribute()] 22 public TList<Orders> OrdersCollection_From_OrderDetails 23 { 24 get { return entityData.OrdersCollection_From_OrderDetails; } 25 set { entityData.OrdersCollection_From_OrderDetails = value; } 26 }


Edit

Indexes

Indexes are not only a powerful feature to help improve your query execution become more performant, it's also used to create handy data access API methods based on those indexes. The important features of an index is that it can contain 1-n columns and has has the ability to be set as a UNIQUE index.

For example, in the Person Table we created above, if we added an index to the Name column, and said that column was Unique. Then in my data access API I would get a method called Person GetByName(string name). If i had not added that the index was unique, then the method would return a collection of type TList.

SQL:
1 CREATE UNIQUE NONCLUSTERED INDEX [IX_Person] ON [dbo].[Person] 2 ( 3 [Name] ASC 4 )WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]


OUTPUT:

1 /// <summary> 2 /// Gets rows from the datasource based on IX_Person index. 3 /// </summary> 4 /// <param name="name"></param> 5 /// <returns>Returns an instance of the <see cref="Person"/></returns> 6 public Person GetByName(System.String name) 7 { 8 int count = -1; 9 //Calls Provider specific Implementation 10 11 return GetByName(null,name, 0, int.MaxValue, out count); 12 }


Edit

Custom Stored Procedures

There are several times when you want to extend the data access API, but you still want to leverage much of the generated approach to the data layer. .netTiers offeres the ability to write your own procedures and be able to do things that .netTiers can not do out of the box or are specialized to your application.

For example, if you wanted to create a custom stored procedure to handle getting all products below a certain inventory. When you begin the generation process, the CodeSmith SchemaExplorer will attempt to discover all of the rich meta data provided to determine of this procedure returns a resultset, which parameters it takes (input & output).

1 -- Get the products that have less units in stock than the @UnitsInStock parameter. 2 CREATE PROCEDURE dbo._Product_GetWithStockBelow 3 @UnitsInStock smallint 4 AS 5 6 SELECT 7 [ProductID], 8 [ProductName], 9 [SupplierID], 10 [CategoryID], 11 [QuantityPerUnit], 12 [UnitPrice], 13 [UnitsInStock], 14 [UnitsOnOrder], 15 [ReorderLevel], 16 [Discontinued] 17 FROM 18 [dbo].[Product] 19 WHERE 20 [UnitsInStock] < @UnitsInStock 21 GO


Output:

1 public TList<Product> GetWithStockBelow(System.Int16 unitsInStock) 2 { 3 4 int count = -1; 5 //Calls Provider specific Implementation 6 7 return GetWithStockBelow( 8 null, unitsInStock, 0, int.MaxValue, out count); 9 10 }


NOTE: There are some situations where the procedure will not return results as expected. The Custom Stored procedures don't work when using temp tables within the custom stored procedure. This is because when CodeSmith's SchemaExplorer is discovering this information, it doesn't have necessary priveldges to create a temp table. One workaround is to use a table variable in the stored procedure instead of a temp table. Warning: Table variables are held in memory on the server so do not load too many rows in the table variable or the servers performance will be impacted.

IMPORTANT: In order for .netTiers to return an entity that maps to the table you've created the Custom Stored Procedure for; all of the columns being returned must match type and be in the correct order. Therefore it is recommended to SELECT * from the source table where possible instead of specifying each column in the select.

If you notice that the generated code for your csp starts returning "void" instead of what you expect check that you csp is still valid

Edit

Enum Tables

.netTiers will create enums based on table data for the tables that you designate. This is useful for fairly static data or type tables.

Rules: The tables you would like to generate as enums must meet the following rules. 1. The first column must be a primary key (typically this would be an int Identity column), 2. The second column must have a unique column constraint index, the optional third column will be the description of the generated enum. 3. You must also select this table in the SourceTable as well in order to generate the enum.

Example:

1CREATE TABLE [dbo].[BankAccountType]( 2 [BankAccountTypeId] [int] IDENTITY(1,1) NOT NULL, 3 [BankAccountTypeName] [varchar](50) NOT NULL, 4 [BankAccountTypeDescription] [varchar](250)NULL, 5 CONSTRAINT [BankAccountType_PK] PRIMARY KEY CLUSTERED 6( 7 [BankAccountTypeId] ASC 8)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY], 9 CONSTRAINT [BankAccountType_UC1] UNIQUE NONCLUSTERED 10( 11 [BankAccountTypeName] ASC 12)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] 13) ON [PRIMARY] 14 15-- Add Table Description 16EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'All allowable Checking Account types for my ABC System' ,@level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'BankAccountType' 17 18 19Some Enum Items: 20INSERT INTO [dbo].[BankAccountType] VALUES ('Checking', 'A Valid Checking Account') 21INSERT INTO [dbo].[BankAccountType] VALUES ('Savings', 'A Valid Savings Account')


Generated Enumeration:

1 /// <summary> 2 /// All allowable Checking Account types for my ABC System 3 /// </summary> 4 /// <remark>Enum that contains the items in BankAccountType</remark> 5 [Serializable] 6 public enum BankAccountTypeList 7 { 8 9 /// <summary> 10 /// A Valid Checking Account 11 /// </summary> 12 [EnumTextValue("A Valid Checking Account")] 13 Checking = 1, 14 15 16 17 /// <summary> 18 /// A Valid Savings Account 19 /// </summary> 20 [EnumTextValue("A Valid Savings Account")] 21 Savings = 2 22 }


ScrewTurn Wiki version 2.0.31.