Data Access Layer Generator
Genie is a cross platform .Net library that can generate a data access layer for any .Net or .Net Core application using a relational database.
Genie has written in C# for .Net core framework
Genie can create a .Net standard class library that can be used as an interface to access any relational databse.
Getting Started
- Download latest release or build the project
- run GenieCLI executable from the genieSetting.json file's location. you can place the download somewhere and add an environment variable to that folder then you can run genie just by typing 'geniecli'
How It Works
- Takes the given configuration file (JSON) and validates the file.
- Reads the database schema and creates internal modal from it.
- Process the model and generates file content using templates
- Writes the file contents to the disk
Genie CLI
Genie CLI is a simple CLI Program that uses Genie core library to trigger a generation. This is bundled with the release.
Command Line Arguments for CLI
- -s = Silent Generation (Nothing will be printed on the console).
- -ni = Information will not be printed on the console.
- -y = Automatically close the program after finished.
- -f = Specify a file name of the configuration. (default is genieSettings.json)
How to install
After downloading the latest release for your OS , extract the archive and put in some place , add the path to the PATH variable.
After that you can call Genie by executing GenieCLI command .
Configuration
It should be something like this
connectionString
A connection string that points to the target database. this connection string is only used to read database meta-data.
projectPath
Path to the project (Data access layer of the target system). This path should point to a existing directory.
baseNamespace
The base namespace of the data access layer (ex :Example.DataAccess)
projectFile (optional)
The relative path to the project file from project path. this is an optional property. if this is provided the generator will add necessary packages to the project file. and if the project file is not there , Genie will create it
abstractModelsLocation (optional)
Genie can generate set of interfaces for every relation and view in thee specified location.
abstractModelsNamespace (optional)
Namespace of the generated interfaces. all interfaces will be in the same interface
enums (optional)
List of table details that can be used as enums. if a table has predefined set of rows which need to be accessed often,this table can be used as an enum table.
example :
the OrderType table (ID, Name) has 3 rows and these rows do not change. we can use this table as an enum table and can be defined in the configuration file as
- type can be string / int / bool or double
this will result a class which has static members for each row and and those members can be implicitly converted to the specified type.
the resulting class will be something like this.
Generated DAL
The generated DAL uses Dapper (micro ORM) to map objects and to access the database.
Implements unit of work pattern and repository pattern. each table and view has a model object and a repository.
All repositories can be accessed through a unit of work.
the DapperContext,should be a singleton. UnitOfWork, DapperContext can be implemented using a DI Container in an upper layer.
Repositories
UnitOfWork can be used to access the repositories. Unit() function in the DapperContext can be used to create new Unit A repository contains methods to get, add, update, remove database entities all unit of work objects must be used in a using block.
a unit has the Commit method that can be used to commit changes to the database
Procedures
All Stored procedures of the the database can be accessed through the Procedures object of any Unit Of Work object, all the parameters of the methods are nullable and null by default. A generic parameter should be provided in order to execute a procedure and the result will be mapped to the given class type.
There are three functions for each procedure (List, Single and Void) List is for getting list of objects as the result Single is for getting single object as the result Void is for no result
Querying
DAL provides an easy way to filter objects. this is a Builder like pattern to filter objects. there is a QueryContext
implemented for all repositories. user can access it through the
Get()
method of any repository. the
Get
method returns a QueryContext that is specific for the object type of the repository.
QueryContext
A QueryContext contains some methods to filter, order, page the result
Where
There is a FilterContext that can be used to filter the query there is a property for each column and these properties
can be used to chain the complete filter expression. each two expressions should be connected using And || Or
, however if you do not specify a boolean operator between two expressions it will be
and
by default.
there is a filter for every data type and every filter contains methods to filter the property. the
Filter()
method returns the parent QueryContext.
OrderBy
There is a OrderContext that can be used to order the query there is a property for each column and these properties can be used to chain the complete order expression. an order expression can be Descending or Ascending.
the
Order()
method returns the parent QueryContext.
Page
The
Page
function can be used to add paging to the query.
Top
Add a limit to the query.
Skip
Skip can be used to skip certain rows from the query result.
Take
Skip can be used to Take only certain rows from the query result. Skip and Take can be used together to page the result.
Query Function
The
Query
function ends the QueryContext and returns the query result.
Count Function
The
Count
function returns the count of the result.
Filter
This function needs an
IEnumerable<IPropertyFilter>
which is a collection of property names, operations and values(if necessary) . this method can be used to filter
the result in a customizable way How the operations are filtered and what are the operations :
Transactions
You can start a transaction by calling
BeginTransaction
method in the UnitOfWork then you should pass the transaction to all methods you use in repositories. you can
use the
Commit
method in the UnitOfWork to commit the transaction.