0% found this document useful (0 votes)
17 views

PersonDB - SQLite Example Using Entity Framework 5

The document describes a .NET application that uses Entity Framework Code First to interact with a SQLite database. It includes classes for business objects like Person and Persons, an abstract ProviderBase data access class, a PersonContext class that derives from DbContext, and a SQLiteProvider class that implements the ProviderBase methods to perform CRUD operations. It also includes a PersonBL business logic class and a Program class for the presentation layer that demonstrates using the classes to select, insert, update and delete person records from the SQLite database.

Uploaded by

Jales Jaque
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

PersonDB - SQLite Example Using Entity Framework 5

The document describes a .NET application that uses Entity Framework Code First to interact with a SQLite database. It includes classes for business objects like Person and Persons, an abstract ProviderBase data access class, a PersonContext class that derives from DbContext, and a SQLiteProvider class that implements the ProviderBase methods to perform CRUD operations. It also includes a PersonBL business logic class and a Program class for the presentation layer that demonstrates using the classes to select, insert, update and delete person records from the SQLite database.

Uploaded by

Jales Jaque
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 26

CUT, FS

PersonDB - SQLite Example


using Entity Framework Code First
Revision 1

P Kruger
Revised on: April 5, 2022
Software Development II PersonDB - SQLite Example using Entity Framework Code First

Contents
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Business Layer - Business Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Person . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1.1 UML Diagram - Person . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1.2 Person.cs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 Persons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2.1 UML Diagram - Persons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2.2 Persons.cs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 ProviderBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1.1 UML Diagram - ProviderBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1.2 ProviderBase.cs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 PersonContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2.1 UML Diagram - PersonContext . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2.2 PersonContext.cs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2.2.1 Connection string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.3 SQLiteProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.3.1 UML Diagram - SQLiteProvider . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.3.2 SQLiteProvider.cs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.3.2.1 SelectAll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.3.2.2 SelectPerson() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.3.2.3 Insert() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.3.2.4 Update() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.3.2.5 Delete() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3 Business Layer - Business Logic Class . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3.1 PersonBL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3.1.1 UML Diagram - PersonBL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3.1.2 ProviderBase.cs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3.1.2.1 private ProviderBase providerBase; . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1.2.2 public PersonBL(string Provider) . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1.2.3 public Persons SelectAll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1.2.4 public int SelectPerson(string ID, ref Person person) . . . . . . . . . . . . . . . 16
1.3.1.2.5 public int Insert(Person newPerson) . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1.2.6 public int Update(Person existingPerson) . . . . . . . . . . . . . . . . . . . . . 16
1.3.1.2.7 public int Delete(string ID) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1.2.8 private void setupProviderBase(string Provider) . . . . . . . . . . . . . . . . . . 16
1.4 Presentation Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.4.1 class Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.4.1.1 Global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.4.1.2 Initialise() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.4.1.3 Main(string[] args) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

© 2022 - P Kruger, Department of Information Technology, CUT, FS i of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

1 Introduction
The purpose of this document is to show how to apply the three layer pattern on a SQLite database
to develop a simple .NET application using Visual Studio and Entity Framework.

Please watch the accompanying video series on how to create the Visual Studio solution, add the
required NuGet packages, and run the migration and update to created the actual SQLite database.

1.1 Business Layer - Business Classes


1.1.1 Person
A simple class called Person was created to store the ID number, the age, first name and last
name of a person. To keep the example simple, no other information is stored. ID will be the unique
primary key of a person, and will be treated as a string. The age of the person will be treated as an
integer number. ID, age, first name and last name are implemented as automatic properties. A
default constructor and an overloaded constructor are provided. The inherited ToString() method is
overridden to return a simple, meaningful string representation of the data in the automatic properties.

1.1.1.1 UML Diagram - Person


The UML diagram for the Person class will be as follows:

Figure 1.1: UML design diagram - Business Class

1.1.1.2 Person.cs
The UML diagram can now be converted to C# code:

public class Person


{
[Key] public string ID { get; set; } // end property
public int Age { get; set; } // end property
public string FirstName { get; set; } // end property
public string LastName { get; set; } // end property

public Person(string ID, int Age, string FirstName, string LastName)


{
this.ID = ID; this.Age = Age;
this.FirstName = FirstName;
this.LastName = LastName;
} // end constructor

© 2022 - P Kruger, Department of Information Technology, CUT, FS 1 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

public Person()
{
} // end constructor

public override string ToString()


{
return $"{ID} : {FirstName} {LastName} ({Age})";
} // end method
} // end class

1.1.2 Persons
A strongly typed custom CollectionBase class called Persons was created to store a collection of
Person objects.

1.1.2.1 UML Diagram - Persons


The UML diagram for the Persons class will be as follows:

Figure 1.2: UML design diagram - Business Class

1.1.2.2 Persons.cs
The UML diagram can now be converted to C# code:

public class Persons : CollectionBase


{
public void Add(Person newPerson)
{
List.Add(newPerson);
} // end method

public void Remove(Person existingPerson)


{
List.Remove(existingPerson);
} // end method

public Person this[int personIndex]


{
get
{
return (Person)List[personIndex];

© 2022 - P Kruger, Department of Information Technology, CUT, FS 2 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

} // end get
set
{
List[personIndex] = value;
} // end set
} // end indexer
} // end class

1.2 Data Access Layer


1.2.1 ProviderBase
The class ProviderBase will contain the abstract methods that each provider must implement. These
methods represent the CRUD functionality.

1.2.1.1 UML Diagram - ProviderBase


The UML diagram for the ProviderBase class will be as follows:

Figure 1.3: UML design diagram - ProviderBase

1.2.1.2 ProviderBase.cs
The UML diagram can now be converted to C# code:

public abstract class ProviderBase


{
/// <summary>
/// This method gets the list of all the business objects from the Person datastore.
/// It returns the list of business objects
/// </summary>
public abstract Persons SelectAll();

/// <summary>
/// This method gets a single Person object from the Person datastore.
/// It returns 0 to indicate the Person was loaded from datastore, or
/// -1 to indicate that no Person was loaded from the datastore (not found).
/// </summary>
/// <param name="ID">The ID of the Person to load from the datastore.</param>
/// <param name="person">The Person object loaded from the datastore.</param>
public abstract int SelectPerson(string ID, ref Person person);

/// <summary>

© 2022 - P Kruger, Department of Information Technology, CUT, FS 3 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

/// This method inserts a record in the Person datastore.


/// It returns 0 to indicate the Person was inserted into datastore, or
/// -1 to indicate the Person was not inserted because a duplicate was found
/// </summary>
/// <param name="newPerson">The Person object to add to the Person datastore.</param>
public abstract int Insert(Person newPerson);

/// <summary>
/// This method updates a record in the Person datastore.
/// It returns 0 to indicate the Person was found and updated successfully, or
/// -1 to indicate the Person was not updated because the record was not found
/// </summary>
/// <param name="existingPerson">The new Person data for the record in the Person
/// datastore.</param>
public abstract int Update(Person existingPerson);

/// <summary>
/// This method deletes a record in the Person datastore.
/// It returns 0 to indicate the Person was found and deleted successfully, or
/// -1 to indicate the Person was not deleted because the record was not found
/// </summary>
/// <param name="ID">The Person ID of the Person to delete in the Person datastore.</param>
public abstract int Delete(string ID);

} // end class

1.2.2 PersonContext
The class PersonContext inherits from the Entity Framework’s DbContext (Database Context) class
for creating, updating, and deleting the Person objects as needed in the database.

1.2.2.1 UML Diagram - PersonContext

Figure 1.4: UML design diagram - PersonContext

The public automatic property Persons must not be confused with the Persons custom collection on
page 2. This automatic property is the collection of all the Person entries in the SQLite database.
By manipulating this collection, and then saving it, the contents of the database will also be changed.
All the CRUD methods will make use of an instance of the PersonContext class to manipulate the
database.

1.2.2.2 PersonContext.cs
The UML diagram can now be converted to C# code.

//

© 2022 - P Kruger, Department of Information Technology, CUT, FS 4 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

// The PersonContext class inherits from the Entity Framework’s DbContext


// (Database Context) class for creating, updating, and deleting the
// Person objects as needed in the database
//
public class PersonContext : DbContext
{
// Persons: a collection of all the Person entities in the database
public DbSet<Person> Persons { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
//
// Called by the Entity Framework to connect to the database
//
// pass a DbContextOptionsBuilder parameter to the OnConfiguring override
// method and call the UseSqlite options method to specify that it
// will connect to a SQLite database.A connection string is passed
// with the details used to connect to the database
optionsBuilder.UseSqlite($"Data Source=Persons.db");
} // end method
} // end class

1.2.2.2.1 Connection string


The connection string to the SQLite database file is given as the method parameter to the UseSqlite
method. In its simplest form, the connection string will consist out of a Data Source entry and
(optionally) a Version entry. Data Source is set equal to the name of the SQLite database file,
which in this example is the file Persons.db. If the database file is in a different directory than the
application directory, the full path to the database file must also be included. As an example, assume
that the database file Persons.db is located under the directory/folder C:\Data, the parameter must
change to:

@"Data Source=c:\Data\Persons.db;Version=3;"

Version indicates which version of SQLite is being used, which in this example is 3.

1.2.3 SQLiteProvider
The class SQLiteProvider will implement the ProviderBase class and provide the CRUD functionality
using SQLite.

© 2022 - P Kruger, Department of Information Technology, CUT, FS 5 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

1.2.3.1 UML Diagram - SQLiteProvider

Figure 1.5: UML design diagram - SQLiteProvider

1.2.3.2 SQLiteProvider.cs
The UML diagram can now be converted to C# code. The class definition is shown below:

public class SQLiteProvider : ProviderBase


{

} // end class

The abstract methods that must be implemented will be discussed next.

1.2.3.2.1 SelectAll()
The SelectAll() method is now implemented:

public override Persons SelectAll()


{
//
//Method Name : Persons SelectAll()
//Purpose : Try to get all the Person objects from the datastore
//Re-use : none
//Input Parameter : None
//Output Type : - Persons
// - the Persons list that will contain the Person objects loaded
// from datastore
//
Persons list;

try
{
list = new Persons();
using (PersonContext db = new PersonContext())
{
//
// The next block of code can be used
// to select all the items in db.Persons
// and to sort the result on p.ID
//
// Then a foreach can be used to add the

© 2022 - P Kruger, Department of Information Technology, CUT, FS 6 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

// sorted result to list


//
// Sorting is optional
//
//var query = from p in db.Persons
// orderby p.ID
// select p;
//foreach (Person item in query)
//{
// list.Add(item);
//} //end foreach

foreach (var item in db.Persons)


{
list.Add(item);
} // end foreach
} // end using
} //end try
catch (Exception ex)
{
throw ex;
} // end catch
return list;
} // end method

The purpose of this method is to select all the records in the Persons table, and return them as a
Persons list.

The basic pattern is as follows:

• start a single try/catch


• instantiate list
• instantiate a PersonContext with a using() clause:
– The using (PersonContext db = new PersonContext()) clause lets you create a new
PersonContext instance for use in all the following code between the curly braces. Besides
being a convenient shorthand, the using() clause ensures that the database connection and
other underlying plumbing objects associated with the connection are closed and disposed
of properly when the operations inside the using clause are finished, even if there is an
exception or other unexpected event. Note the commented out code. Here a LINQ query
is used to sort the objects in db.Persons and then a foreach is used to add the sorted
objects to list. As stated, sorting is optional.
– The foreach after the comment block loops through all the objects in db.Persons and
adds each object to list.
• the catch is simply a general catch that will throw any caught exception back to the calling
method of SelectAll()
• the last thing to do is to return list. If there were one or more rows in the table, list will contain
one or more instantiated Person objects. If, however, there are no rows in the table, list will
be empty, but NOT null, since it was instantiated before the table was accessed

© 2022 - P Kruger, Department of Information Technology, CUT, FS 7 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

1.2.3.2.2 SelectPerson()
The SelectPerson() method is now implemented:

public override int SelectPerson(string ID, ref Person person)


{
//
//Method Name : int SelectPerson(string ID, ref Person person)
//Purpose : Try to get a single Person object from the datastore
//Re-use : none
//Input Parameter : - string ID
// - The ID of the Person to load from the datastore
// - ref Person person
// - The Person object loaded from the datastore
//Output Type : - int
// 0 : Person loaded from datastore
// -1 : no Person was loaded from the datastore
// (not found)
//
int rc;

try
{
using (PersonContext db = new PersonContext())
{
person = db.Persons.FirstOrDefault(p => p.ID.Equals(ID));
if (person == null) // not found
{
rc = -1;
} //end if
else
{
rc = 0;
} // end else
} // end using
} // end try
catch (Exception ex)
{
throw ex;
} // end catch
return rc;
} // end method

The purpose of this method is to select a single record in the Persons table. The method parameter
string ID contains the ID of the Person record to search for in the table. If the record is found, it
will be made available via the second method parameter ref Person person as a fully instantiated
Person object.

The integer rc can contain one of the following two valid values:
• 0 : the Person record was found, and is available as a Person object via the method parameter
ref Person person
• -1: the Person record was not found
From here, the basic pattern is as follows (notice what is the same as with SelectAll() on page 6):
• start a single try/catch

© 2022 - P Kruger, Department of Information Technology, CUT, FS 8 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

• instantiate a PersonContext object with a using() clause:


– As with SelectAll() on page 6 the PersonContext is used to gain access to the records
in the database
– A LINQ query is used to determine if the Person record identified with the method
parameter ID is in the database. FirstOrDefault is used since it will not throw an exception
if the object was not found, and instead will return a null.
– person is evaluated and if null, rc is set to -1 otherwise rc is set to 0
• the catch is simply a general catch that will throw any caught exception back to the calling
method of SelectPerson()
• the last thing to do is to return rc

1.2.3.2.3 Insert()
The Insert() method is now implemented:

public override int Insert(Person newPerson)


{
//
//Method Name : int Insert(Person newPerson)
//Purpose : Try to insert a row in the Person datastore
//Re-use : none
//Input Parameter : Person newPerson
// - The Person object to add to the Person datastore
//Output Type : - int
// 0 : newPerson inserted into datastore
// -1 : newPerson not inserted because a duplicate
// was found
//
Person person;
int rc;

try
{
using (PersonContext db = new PersonContext())
{
person = db.Persons.FirstOrDefault(b => b.ID.Equals(newPerson.ID));
if (person == null) // not found
{
db.Persons.Add(newPerson);
db.SaveChanges();
rc = 0;
} // end if
else
{
rc = -1;
} // end else
} // end using
} // end try
catch (Exception ex)
{
throw ex;
} // end catch
return rc;
} // end method

© 2022 - P Kruger, Department of Information Technology, CUT, FS 9 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

The purpose of this method is to insert a single record in the Persons table. The method parameter
Person newPerson contains the fully instantiated Person object that must be inserted into the table.

The integer rc can contain one of the following two valid values:
• 0 : the Person object in the method parameter newPerson was successfully inserted into the
table
• -1: the Person object was not inserted since an existing record with the same primary key
already exists in the table
From here, the basic pattern is as follows (notice what is the same as with SelectAll() on page 6
and SelectPerson() on page 8):
• start a single try/catch
• instantiate a PersonContext object with a using() clause:
– As with SelectAll() on page 6 and SelectPerson() on page 8 the PersonContext is used
to gain access to the records in the database
– As with SelectPerson() on page 8, a LINQ query is used to determine if the Person
record identified with the ID property of the Person method parameter is in the database.
FirstOrDefault is used since it will not throw an exception if the object was not found,
and instead will return a null.
– person is evaluated:
∗ if null, newPerson is added to db.Persons and the SaveChanges() method is invoked
to save all the changes to the database. Variable rc is set to 0 to indicate success
∗ if not null, rc is set to -1
• the catch is simply a general catch that will throw any caught exception back to the calling
method of Insert()
• the last thing to do is to return rc

1.2.3.2.4 Update()
The Update() method is now implemented:

public override int Update(Person existingPerson)


{
//
//Method Name : int Update(Person existingPerson)
//Purpose : Try to update a row in the datastore
//Re-use : none
//Input Parameter : Person existingPerson
// - The new Person data for the row in the datastore
//Output Type : - int
// 0 : person found and updated successfully
// -1 : person not updated because the record was
// not found
//
Person person;
int rc;

try
{
using (PersonContext db = new PersonContext())
{
person = db.Persons.FirstOrDefault(p => p.ID.Equals(existingPerson.ID));
if (person == null) // not found
{

© 2022 - P Kruger, Department of Information Technology, CUT, FS 10 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

rc = -1;
} // end if
else
{
person.Age= existingPerson.Age;
person.FirstName = existingPerson.FirstName;
person.LastName = existingPerson.LastName;
db.SaveChanges();
rc = 0;
} // end else
} // end using
} // end try
catch (Exception ex)
{
throw ex;
} // end catch
return rc;
} // end method

The purpose of this method is to update a single record in the Persons table. The method parameter
Person existingPerson contains the fully instantiated Person object of which ID will be used to
search for the record to update, and the properties of existingPerson will be used to update the
corresponding fields/columns for that record in the table. This method does NOT support updating
the primary key as well.

The integer rc can contain one of the following two valid values:
• 0 : the Person object in the method parameter person was successfully updated in the table
• -1: the Person object was not updated since an existing record with the same primary key was
not found in the table
From here, the basic pattern is as follows (notice what is the same as with SelectAll() on page 6,
SelectPerson() on page 8, and Insert() on page 9):
• start a single try/catch
• instantiate a PersonContext object with a using() clause:
– As with SelectAll() on page 6, SelectPerson() on page 8 and Insert() on page 9, the
PersonContext is used to gain access to the records in the database
– As with SelectPerson() on page 8 and Insert() on page 9, a LINQ query is used to
determine if the Person record identified with the ID property of the Person method
parameter is in the database. FirstOrDefault is used since it will not throw an exception
if the object was not found, and instead will return a null.
– person is evaluated:
∗ if null, the record was not found and rc is set to -1
∗ if not null, the record was found and the properties of existingPerson is used to update
the corresponding properties of person. Note: person.ID is NOT updated, since
that would change the primary key which is not supported by this method. Next the
SaveChanges() method is invoked to save all the changes to the database. Variable
rc Variable rc is set to 0 to indicate success
• the catch is simply a general catch that will throw any caught exception back to the calling
method of Insert()
• the last thing to do is to return rc

1.2.3.2.5 Delete()
The Delete() method is now implemented:

© 2022 - P Kruger, Department of Information Technology, CUT, FS 11 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

public override int Delete(string ID)


{
//
//Method Name : int Delete(string ID)
//Purpose : Try to delete a row from the Person datastore
//Re-use : none
//Input Parameter : string ID
// - the ID of the Person to delete in the Person datastore
//Output Type : - int
// 0 : Person found and deleted successfully
// -1 : Person not deleted because the record was
// not found
//
Person person;
int rc;

try
{
using (PersonContext db = new PersonContext())
{
person = db.Persons.FirstOrDefault(b => b.ID.Equals(ID));
if (person == null) // not found
{
rc = -1;
} // end if
else
{
db.Persons.Remove(person);
db.SaveChanges();
rc = 0;
} // end else
} // end using
} // end try
catch (Exception ex)
{
throw ex;
} // end catch
return rc;
} // end method

The purpose of this method is to delete a single record from the Persons table. The method parameter
string ID contains the value of the primary key of the record to remove from the table.

The integer rc can contain one of the following two valid values:

• 0 : the Person record represented by the method parameter ID was successfully removed from
the table
• -1: the Person record was not removed since an existing record with the same primary key was
not found in the table

From here, the basic pattern is as follows (notice what is the same as with SelectAll() on page 6,
SelectPerson() on page 8, Insert() on page 9, and Update() on page 10):

• start a single try/catch


• instantiate a PersonContext object with a using() clause:
– As with SelectAll() on page 6, SelectPerson() on page 8, Insert() on page 9, and

© 2022 - P Kruger, Department of Information Technology, CUT, FS 12 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

Update() on page 10, the PersonContext is used to gain access to the records in the
database
– As with SelectPerson() on page 8, Insert() on page 9, and Update() on page 10, a LINQ
query is used to determine if the Person record identified with the ID method parameter
is in the database. FirstOrDefault is used since it will not throw an exception if the object
was not found, and instead will return a null.
– person is evaluated:
∗ if null, the record was not found and rc is set to -1
∗ if not null, the record was found and the object removed using the Remove() method.
Next the SaveChanges() method is invoked to save all the changes to the database.
Variable rc Variable rc is set to 0 to indicate success
• the catch is simply a general catch that will throw any caught exception back to the calling
method of Insert()
• the last thing to do is to return rc

1.3 Business Layer - Business Logic Class


1.3.1 PersonBL
The class PersonBL will provide the link between the Presentation Layer and the Data Access Layer.
PersonBL is a simple class that contains the same method signatures/headers of the methods found
in ProviderBase.

1.3.1.1 UML Diagram - PersonBL


The UML diagram for the PersonBL class will be as follows:

Figure 1.6: UML design diagram - PersonBL

1.3.1.2 ProviderBase.cs
The UML diagram can now be converted to C# code:

© 2022 - P Kruger, Department of Information Technology, CUT, FS 13 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

public class PersonBL


{
private ProviderBase providerBase;

public PersonBL(string Provider)


{
//
//Method Name : PersonBL(string Provider)
//Purpose : Overloaded constructor; invoke setupProviderBase to setup

// data provider
//Re-use : setupProviderBase()
//Input Parameter : string Provider
// - The name of the data provider to use
//Output Type : None
//
setupProviderBase(Provider);
} // end method

/// <summary>
/// This method gets the list of all the business objects from the Person datastore.
/// It returns the list of business objects
/// </summary>
public Persons SelectAll()
{
return providerBase.SelectAll();
} // end method

/// <summary>
/// This method gets a single Person object from the Person datastore.
/// It returns 0 to indicate the Person was loaded from the datastore, or
/// -1 to indicate that no Person was loaded from the datastore.
/// </summary>
/// <param name="ID">The Person ID of the Person to load from the datastore.</param>
/// <param name="person">The Person object loaded from the datastore.</param>
public int SelectPerson(string ID, ref Person person)
{
return providerBase.SelectPerson(ID, ref person);
} // end method

/// <summary>
/// This method inserts a record in the Person datastore.
/// It returns 0 to indicate the Person was inserted into datastore, or
/// -1 to indicate the Person was not inserted because a duplicate was found
/// </summary>
/// <param name="newPerson">The Person object to add to the Person datastore.</param>
public int Insert(Person newPerson)
{
return providerBase.Insert(newPerson);
} // end method

/// <summary>
/// This method updates a record in the Person datastore.
/// It returns 0 to indicate the Person was found and updated successfully, or
/// -1 to indicate the Person was not updated because the record was not found

© 2022 - P Kruger, Department of Information Technology, CUT, FS 14 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

/// </summary>
/// <param name="existingPerson">The new Person data for the record in the Person

/// datastore.</param>
public int Update(Person existingPerson)
{
return providerBase.Update(existingPerson);
} // end method

/// <summary>
/// This method deletes a record in the Person datastore.
/// It returns 0 to indicate the Person was found and deleted successfully, or
/// -1 to indicate the Person was not deleted because the record was not found
/// </summary>
/// <param name="ID">The Person ID of the Person to delete in the Person datastore.</param>
public int Delete(string ID)
{
return providerBase.Delete(ID);
} // end method

private void setupProviderBase(string Provider)


{
//
//Method Name : void setupProviderBase()
//Purpose : Helper method to select the correct data provider
//Re-use : None
//Input Parameter : string Provider
// - The name of the data provider to use
//Output Type : None
//
if (Provider == "XMLProvider")
{
//providerBase = new XMLProvider();
} // end if
else
{
if (Provider == "SQLiteProvider")
{
providerBase = new SQLiteProvider();
} // end if
else
{
if (Provider == "MySQLProvider")
{
//providerBase = new MySQLProvider();
} // end if
} // end else
} // end else
} // end method
} // end class

© 2022 - P Kruger, Department of Information Technology, CUT, FS 15 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

1.3.1.2.1 private ProviderBase providerBase;


The private field providerBase is of type ProviderBase (Data Access Layer on page 3), which is
the abstract base class for all the provider classes. providerBase will be instantiated in the helper
method setupProviderBase() (on page 16).

1.3.1.2.2 public PersonBL(string Provider)


The overloaded constructor is used to instantiate an object of PersonBL in the Presentation Layer.
This object will then be used to invoke the appropriate CRUD methods. Via the PersonBL object, the
selected Provider object will be instantiated and the corresponding CRUD method in the Provider
class will be executed.

The constructor accepts one method parameter of type string. This string represents the name
of the Provider class that must be used. The constructor will then send this string to the helper
method setupProviderBase() (on page 16).

1.3.1.2.3 public Persons SelectAll()


This method simply invokes the SelectAll() method via providerBase. Thus, the SelectAll() method
of the provider class to which providerBase was instantiated, will be invoked.

1.3.1.2.4 public int SelectPerson(string ID, ref Person person)


This method simply invokes the SelectPerson() method via providerBase. Thus, the SelectPerson()
method of the provider class to which providerBase was instantiated, will be invoked.

1.3.1.2.5 public int Insert(Person newPerson)


This method simply invokes the Insert() method via providerBase. Thus, the Insert() method of
the provider class to which providerBase was instantiated, will be invoked.

1.3.1.2.6 public int Update(Person existingPerson)


This method simply invokes the Update() method via providerBase. Thus, the Update() method
of the provider class to which providerBase was instantiated, will be invoked.

1.3.1.2.7 public int Delete(string ID)


This method simply invokes the Delete() method via providerBase. Thus, the Delete() method of
the provider class to which providerBase was instantiated, will be invoked.

1.3.1.2.8 private void setupProviderBase(string Provider)


This method has one string method parameter called Provider. The value of Provider is evaluated
against a set of values for existing Provider classes. In this example, there are three:

• XMLProvider - Provide CRUD functionality for manipulating Person info in a XML file
• SQLiteProvider - Provide CRUD functionality for manipulating Person info in a SQLite
database file
• MySQLProvider - Provide CRUD functionality for manipulating Person info in a MySQL
database on a local MySQL server

As more providers are implemented, this evaluation must be expanded. For now, a form of selection
will be used.

In this method, if, as an example, Provider contains the value "SQLiteProvider", the instance
field providerBase will be instantiated as a new SQLiteProvider (on page 5) object. This means

© 2022 - P Kruger, Department of Information Technology, CUT, FS 16 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

that any reference to a CRUD method via the instantiated PersonBL object, will invoke the
corresponding CRUD method in SQLiteProvider. As another example, if Provider contains the
value "XMLProvider", the instance field providerBase will be instantiated as a new XMLProvider
object. This means that any reference to a CRUD method via the instantiated PersonBL object,
will invoke the corresponding CRUD method in XMLProvider.

1.4 Presentation Layer


The Presentation Layer can now be designed and implemented. Regardless of what form this will
take, the following must be remembered:

• in this layer, a PersonBL object must be instantiated with the correct provider. This object
must then be used to invoke the required CRUD methods
• except for the PersonBL object, NO objects or references to ProviderBase and the derived
classes may be used

The following example will be used to tie everything together. This example will be a Console
application with a simple interactive menu driven approach. Exception handling and other error
handling are kept to a minimum but must naturally be implemented in full. All the different parts of
this program will not be discussed in detail, but certain parts will be discussed after the code listing.
The following parts will be discussed:

• Global variables on page 24


• Initialise() on page 24
• Main(string[] args) on page 24

1.4.1 class Program


class Program
{
public static Persons personList;
public static PersonBL pBL;

static void Initialise()


{
//
//Method Name : void Initialise()
//Purpose : Initialise and instantiate global variables
//Re-use : none
//Input Parameter : none
//Output Type : none
//
personList = new Persons();
pBL = new PersonBL("SQLiteProvider");
//pBL = new PersonBL("MySQLProvider");
//pBL = new PersonBL("XMLProvider");
} // end method

//--------------------------------------------------------------------------------
//
// Display menus
//
//--------------------------------------------------------------------------------

public static void ShowMainMenu()


{

© 2022 - P Kruger, Department of Information Technology, CUT, FS 17 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

//
//Method Name : void ShowMainMenu()
//Purpose : Display the main menu
//Re-use : none
//Input Parameter : none
//Output Type : none
//
WriteLine();
WriteLine("Please select an option:");
WriteLine("========================");
WriteLine("1. Person Maintenance");
WriteLine("X. Exit");
WriteLine();
} // end method ShowMainMenu()

public static void ShowPersonMaint()


{
//
//Method Name : void ShowPersonMaint()
//Purpose : Display the Person Maintenance menu
//Re-use : none
//Input Parameter : none
//Output Type : none
//
WriteLine();
WriteLine("Person Maintenance: Please select an option:");
WriteLine("=============================================");
WriteLine("1. List Person");
WriteLine("2. Add Person");
WriteLine("3. Remove Person");
WriteLine("4. Update Person");
WriteLine("R. Return");
WriteLine();
} // end method ShowPersonMaint()

//--------------------------------------------------------------------------------
//
// Process menus
//
//--------------------------------------------------------------------------------
public static void ProcessPersonMenu()
{
//
//Method Name : void ProcessPersonMenu()
//Purpose : Invoke appropriate method to handle user menu selection
//Re-use : ShowPersonMaint();PersonList();PersonAdd();PersonRemove();
// PersonUpdate()
//Input Parameter : none
//Output Type : none
//
char choice = ’0’;
ConsoleKeyInfo cki;

WriteLine();
ShowPersonMaint();

© 2022 - P Kruger, Department of Information Technology, CUT, FS 18 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

cki = ReadKey();
WriteLine();
choice = cki.KeyChar;

while (choice != ’r’ && choice != ’R’)


{
switch (choice)
{
case ’1’:
PersonList();
break;
case ’2’:
PersonAdd();
break;
case ’3’:
PersonRemove();
break;
case ’4’:
PersonUpdate();
break;
case ’R’:
case ’r’:
break;
default:
WriteLine("Invalid input");
break;
} // end switch
WriteLine();
ShowPersonMaint();

cki = ReadKey();
WriteLine();
choice = cki.KeyChar;
} // end while
} // end method

//--------------------------------------------------------------------------------
//
// Person related methods
//
//--------------------------------------------------------------------------------

public static void PersonUpdate()


{
//
//Method Name : void PersonUpdate()
//Purpose : Update existing person info
//Re-use : none
//Input Parameter : none
//Output Type : none
//
string ID = "";
string age = "";

© 2022 - P Kruger, Department of Information Technology, CUT, FS 19 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

string firstName = "";


string lastName = "";
int rc = 0;
bool change = false;
Person person = new Person();

Write("Please enter the person ID: ");


ID = ReadLine().ToUpper();
rc = pBL.SelectPerson(ID, ref person);
if (rc == 0)
{
person.ID = ID;
WriteLine(person);
Write("New age or press enter not to change: ");
age = ReadLine();
if (age.Length != 0)
{
person.Age = Convert.ToInt32(age);
change = true;
} // end if

Write("New first name or press enter not to change: ");


firstName = ReadLine();
if (firstName.Length != 0)
{
person.FirstName = firstName;
change = true;
} // end if

Write("New last name or press enter not to change: ");


lastName = ReadLine();
if (lastName.Length != 0)
{
person.LastName = lastName;
change = true;
} // end if

if (change)
{
rc = pBL.Update(person);
if (rc == -1)
{
WriteLine(ID + " NOT updated since it is not in the DB");
} // end if
else
{
WriteLine(ID + " updated");
} // end else
} // end if
else
{
WriteLine("Nothing selected to update");
} // end else

} // end if

© 2022 - P Kruger, Department of Information Technology, CUT, FS 20 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

else
{
WriteLine(ID + " NOT found");
} // end else
} // end method

public static void PersonAdd()


{
//
//Method Name : void PersonAdd()
//Purpose : Get new Person info and try to add it to DB
//Re-use : none
//Input Parameter : none
//Output Type : none
//
string ID = "";
string firstName = "";
string lastName = "";
int age = 0;
int rc = 0;
Person person = null;

WriteLine("Please supply the following person info:");


Write("ID: ");
ID = ReadLine().ToUpper();
rc = pBL.SelectPerson(ID, ref person);
if (rc == 0)
{
WriteLine(ID + " NOT added since it is already in the DB");
} // end if
else
{
Write("Age: ");
age = Convert.ToInt32(ReadLine());
Write("First Name: ");
firstName = ReadLine();
Write("Last Name: ");
lastName = ReadLine();

rc = pBL.Insert(new Person(ID, age, firstName, lastName));


if (rc == -1)
{
WriteLine(ID + " NOT added since it is already in the DB");
} // end if
else
{
WriteLine(ID + " added");
} // end else
} // end else

} // end method

public static void PersonRemove()


{

© 2022 - P Kruger, Department of Information Technology, CUT, FS 21 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

//
//Method Name : void PersonRemove()
//Purpose : Try to remove a Person record from the DB
//Re-use : none
//Input Parameter : none
//Output Type : none
//
string code = "";
int rc = 0;

if (personList.Count > 0)
{
Write("Please enter the person ID: ");
code = ReadLine().ToUpper();
rc = pBL.Delete(code);
if (rc == 0)
{
WriteLine(code + " removed from DB");
} // end if
else
{
WriteLine(code + " NOT removed since it is not in the DB");
} // end else
} // end if
else
{
WriteLine("No person record to remove from DB");
} // end else
} // end method

public static void PersonList()


{
//
//Method Name : void PersonList()
//Purpose : Display the Person records in the DB
//Re-use : none
//Input Parameter : none
//Output Type : none
//
personList = pBL.SelectAll();
if (personList.Count == 0)
{
WriteLine("No Person records found");
} // end if
else
{
foreach (Person pRef in personList)
{
WriteLine(pRef);
} // end foreach
} // end if
} // end method

//--------------------------------------------------------------------------------
//

© 2022 - P Kruger, Department of Information Technology, CUT, FS 22 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

// Main
//
//--------------------------------------------------------------------------------
public static void Main(string[] args)
{
//
//Method Name : void Main(string[] args)
//Purpose : Main entry into program
//Re-use : ShowMainMenu(); ProcessPersonMenu()
//Input Parameter : string[] args
// - command line args - not used
//Output Type : none
//

char choice = ’0’;


ConsoleKeyInfo cki;

try
{
Initialise();

WriteLine();
ShowMainMenu();

cki = ReadKey();
WriteLine();
choice = cki.KeyChar;

while (choice != ’x’ && choice != ’X’)


{
switch (choice)
{
case ’1’:
ProcessPersonMenu();
break;
case ’x’:
case ’X’:
break;
default:
WriteLine("Invalid input");
break;
} // end switch

WriteLine();
ShowMainMenu();

cki = ReadKey();
WriteLine();
choice = cki.KeyChar;
} // end while
} // end try
catch (Exception ex)
{
WriteLine(ex.Message);
} // end catch

© 2022 - P Kruger, Department of Information Technology, CUT, FS 23 of 24


Software Development II PersonDB - SQLite Example using Entity Framework Code First

} // end method Main()


} // end class

1.4.1.1 Global variables


In this program, two global variables are used. personList is used throughout the program when
a Persons list is required. pBL represents the PersonBL. pBL is used everywhere access to the
database/datastore is required. It will be instantiated in method Initialise() (on page 24)

1.4.1.2 Initialise()
This method instantiates the two global variables. Importantly, pBL is instantiated to the desired
Provider class. Currently, SQLiteProvider is used. You will recall that one of the advantages of
using the three layer pattern was the swapping of the data providers. Here, in this example, is where
the swap can be made.

Consider the following scenario: the three layer pattern was used and implemented for a simple Person
Management System. The initial datastore is a SQLite database file. Later on, a requirement arose
that instead of using the SQLite database file, an XML file must now be used. What should happen
now? Luckily, the process is straight forward. An XML Provider class must be implemented that
inherits from the abstract base class ProviderBase (on page 3). Since ProviderBase also contains
abstract methods, that represents the CRUD methods, the XML Provider class must implement these
methods. This means the the method headers in the existing SQLite Provider class and the new XML
Provider class will be exactly the same. What is critically important is that the CRUD methods in
the XML provider class must access and modify the XML file, but the behavior must be the same as
for the SQLite provider. As soon as the XML Provider is finished, method setupProviderBase (on
page 16) must be modified to include a test for XMLProvider (which is currently the case in this
example). Next, method Initialise() must be modified to instantiate pBL to XMLProvider. Save
and build the program and it will now use the XML provider, instead of the SQLite provider. Nothing
else in the whole program needs to change.

1.4.1.3 Main(string[] args)


One of the first things that this method must do is to invoke method Initialise() to ensure that pBL
is instantiated to the correct data provider before any of the CRUD methods are invoked.

2 Summary
In this document a complete SQLite database aware application was developed using the simple three
layer pattern. As with many things programming related, such a database application can be designed
and implemented in several different ways, but for the purpose of the Software Development II
subject, this will be the standard pattern to use. The only thing that could change is obviously the
Presentation Layer, but the ProviderBase and SQLiteProvider classes will follow the pattern as
implemented in this document.

© 2022 - P Kruger, Department of Information Technology, CUT, FS 24 of 24

You might also like