Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
C# 7 and .NET Core 2.0 Blueprints
C# 7 and .NET Core 2.0 Blueprints

C# 7 and .NET Core 2.0 Blueprints: Build effective applications that meet modern software requirements

eBook
€8.99 €32.99
Paperback
€41.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

C# 7 and .NET Core 2.0 Blueprints

eBook Manager and Catalogue App

C# 7 is a fantastic release and is available in Visual Studio 2017. It introduces developers to a lot of powerful features, some of which were previously only available in other languages. The new features introduced in C# 7 allow developers to write less code and be more productive.

The features available are:

  • Tuples
  • Pattern matching
  • Out variables
  • Deconstruction
  • Local functions
  • Improvements to literals
  • Ref returns and locals
  • Generalized async and return types
  • Expression bodies for accessors, constructors, and finalizers
  • Throw expressions

This chapter will take you through some of these features, while the rest of the book will introduce you to some of the other features as we go along. In this chapter, we will create an eBookManager application. If you are like me, you have eBooks scattered all over your hard drives and some external drives as well. This application will provide a mechanism to bring all these various locations together into a Virtual Storage Space. The application is functional, but can be further enhanced to suit your needs. The scope for an application such as this is vast. You can download the source code from GitHub (https://github.com/PacktPublishing/CSharp7-and-.NET-Core-2.0-Blueprints) and follow it to see some of the new features of C# 7 in action.

Let's begin!

Setting up the project

Using Visual Studio 2017, we will create a simple Windows Forms App template project. You can call the application anything you like, but I called mine eBookManager:

The project will be created and will look as follows:

Our Solution needs a Class Library project to contain the classes that drive the eBookManager application. Add a new Class Library project to your Solution and call it eBookManager.Engine:

A Class Library project is added to the Solution with the default class name. Change this class to Document:

The Document class will represent a single eBook. Thinking of a book, we can have multiple properties that would represent a single book, but be representative of all books. An example of this would be the author. All books must have an author, otherwise it would not exist.

I know some of you might be thinking that machines could generate documents too, but the information it generates was probably originally written by a person. Take code comments for example. A developer writes the comments in code, and a tool generates a document from that. The developer is still the author.

The properties I have added to the class are merely my interpretation of what might represent a book. Feel free to add additional code to make this your own.

Open the Document.cs file and add the following code to the class:

namespace eBookManager.Engine 
{ 
    public class Document 
    { 
        public string Title { get; set; } 
        public string FileName { get; set; } 
        public string Extension { get; set; } 
        public DateTime LastAccessed { get; set; } 
        public DateTime Created { get; set; } 
        public string FilePath { get; set; } 
        public string FileSize { get; set; } 
        public string ISBN { get; set; } 
        public string Price { get; set; } 
        public string Publisher { get; set; } 
        public string Author { get; set; } 
        public DateTime PublishDate { get; set; } 
        public DeweyDecimal Classification { get; set; } 
        public string Category { get; set; } 
    } 
} 

You will notice that I have included a property called Classification of type DeweyDecimal. We have not added this class yet, and will do so next.

To the eBookManager.Engine project, add a class called DeweyDecimal. If you don't want to go to this level of classification for your eBooks, you can leave this class out. I have included it for the sake of completeness:

Your DeweyDecimal class must be in the same project as the Document class added earlier:

The DeweyDecimal system is quite big. For this reason, I have not catered for every book classification available. I have also only assumed that you would want to be working with programming eBooks. In reality, however, you may want to add in other classifications, such as literature, the sciences, the arts, and so on. It is up to you.
So let's create a class to represent the Dewey Decimal system::

  1. Open up the DeweyDecimal class and add the following code to the class:
namespace eBookManager.Engine 
{ 
    public class DeweyDecimal 
    { 
        public string ComputerScience { get; set; } = "000"; 
        public string DataProcessing { get; set; } = "004"; 
        public string ComputerProgramming { get; set; } = "005"; 
    } 
}

Word nerds may disagree with me here, but I would like to remind them that I'm a code nerd. The classifications represented here are just so that I can catalog programming and computer science-related eBooks. As mentioned earlier, you can change this to suit your needs.

  1. We now need to add in the heart of the eBookManager.Engine Solution. This is a class called DocumentEngine and it will be a class that will contain the methods you need to work with the documents:

Your eBookManager.Engine Solution will now contain the following classes:

    • DeweyDecimal
    • Document
    • DocumentEngine
  1. We now need to add a reference to eBookManager.Engine from the eBookManager project. I'm sure that you all know how to do this:

The eBookManager.Engine project will be available under the Projects section in the Reference Manager screen:

  1. Once we have added the reference, we need a Windows Form that will be responsible for importing new books. Add a new form called ImportBooks to the eBookManager Solution:
  1. Before we forget, add an ImageList control to the ImportBooks form and call it tvImages. This will contain the images for the different types of documents we want to catalog.
The ImageList is a control you add from the Toolbox on to the ImportBooks form. You can access the Images Collection Editor from the ImageList properties.

The icons can be found in the img folder in the source code downloadable from GitHub at the following URL—https://github.com/PacktPublishing/CSharp7-and-.NET-Core-2.0-Blueprints.

The icons here are catering for PDF, MS Word, and ePub file types. It also contains folder images:

  1. Now, to use tuples in C# 7, you need to add the System.ValueTuple NuGet package. Right-click on your Solution and select Manage NuGet Packages for Solution...
Please note that if you are running the .NET Framework 4.7, System.ValueTuple is included in this version of the framework. You will therefore not need to get it from NuGet.
  1. Search for System.ValueTuple and add it to your Solution projects. Then click Install and let the process complete (you will see the progress in the output window in Visual Studio):

I love making use of extension methods in my projects. I usually add a separate project and/or class for this purpose. In this application, I added an eBookManager.Helper class library project:

  1. This helper class must also be added to the eBookManager Solution as a reference:

Lastly, I will be using JSON as a simple file store for my eBook catalogue. JSON is really flexible and can be consumed by various programming languages. What makes JSON so nice is the fact that it is relatively light weight and the output it generates is human-readable:

  1. Go to Manage the NuGet packages for your Solution and search for Newtonsoft.Json. Add this then to the projects in your Solution and click the Install button.

You have now set up the basics needed for your eBookManager application. Next, we will venture further into the guts of the application by writing some code.

Virtual Storage Spaces and extension methods

Let's start by discussing the logic behind a Virtual Storage Space. This is a single virtual representation of several physical spaces on your hard drive (or hard drives). A storage space will be seen as a single area where a specific group of eBooks are stored. I use the term stored loosely because the storage space doesn't exist. It represents more of a grouping than a physical space on the hard drive:

  1. To start creating Virtual Storage Spaces, add a new class called StorageSpace to the eBookManager.Engine project. Open the StorageSpace.cs file and add the following code to it:
using System; 
using System.Collections.Generic; 
 
namespace eBookManager.Engine 
{ 
    [Serializable] 
    public class StorageSpace 
    { 
        public int ID { get; set; } 
        public string Name { get; set; } 
        public string Description { get; set; } 
        public List<Document> BookList { get; set; } 
    } 
} 
Note that you need to include the System.Collections.Generic namespace here, because the StorageSpace class contains a property called BookList of type List<Document> that will contain all the books in that particular storage space.

Now we need to focus our attention on the ExtensionMethods class in the eBookManager.Helper project. This will be a static class, because extension methods need to be static in nature in order to act on the various objects defined by the extension methods.

  1. Add a new class to the eBookManager.Helper project and modify the ExtensionMethods class as follows:

public static class ExtensionMethods 
{ 
 
} 

Let's add the first extension method to the class called ToInt(). What this extension method does is take a string value and try to parse it to an integer value. I am too lazy to type Convert.ToInt32(stringVariable) whenever I need to convert a string to an integer. It is for this reason that I use an extension method.

  1. Add the following static method to the ExtensionMethods class:
public static int ToInt(this string value, int defaultInteger = 0) 
{ 
    try 
    { 
        if (int.TryParse(value, out int validInteger)) 
// Out variables return validInteger; else return defaultInteger; } catch { return defaultInteger; } }

The ToInt() extension method acts only on string. This is defined by the code this string value in the method signature where value is the variable name that will contain the string you are trying to convert to an integer. It also has a default parameter called defaultInteger, which is set to 0. Unless the developer calling the extension method wants to return a default integer value of 0, they can pass a different integer to this extension method (-1, for example).

It is also here that we find our first feature of C# 7. The improvement to out variables. In previous iterations of C#, we had to do the following with out variables:

int validInteger; 
if (int.TryParse(value, out validInteger)) 
{ 
 
} 

There was this predeclared integer variable hanging around and it gets its value if the string value parsed to an integer. C# 7 simplifies the code a lot more:

if (int.TryParse(value, out int validInteger)) 

C# 7 allows developers to declare an out variable right there where it is passed as an out argument. Moving along to the other methods of the ExtensionMethods class, these methods are used to provide the following logic:

  • Read and write to the data source

  • Check whether a storage space exists

  • Convert bytes to megabytes

  • Convert a string to an integer (as discussed previously)

The ToMegabytes method is quite easy. Not having to write this calculation all over the place, defining it inside an extension method makes sense:

public static double ToMegabytes(this long bytes) 
{ 
    return (bytes > 0) ? (bytes / 1024f) / 1024f : bytes; 
} 

We also need a way to check if a particular storage space already exists.

Be sure to add a project reference to eBookManager.Engine from the eBookManager.Helper project.

What this extension method also does is to return the next storage space ID to the calling code. If the storage space does not exist, the returned ID will be the next ID that can be used when creating a new storage space:

public static bool StorageSpaceExists(this List<StorageSpace> space, string nameValueToCheck, out int storageSpaceId) 
{ 
    bool exists = false; 
    storageSpaceId = 0; 
 
    if (space.Count() != 0) 
    { 
       int count = (from r in space 
                 where r.Name.Equals(nameValueToCheck) 
                 select r).Count(); 
 
        if (count > 0) 
            exists = true; 
 
        storageSpaceId = (from r in space 
                          select r.ID).Max() + 1;                                 
    } 
    return exists; 
} 

We also need to create a method that will write the data we have to a file after converting it to JSON:

public static void WriteToDataStore(this List<StorageSpace> value, string storagePath, bool appendToExistingFile = false) 
{ 
    JsonSerializer json = new JsonSerializer(); 
    json.Formatting = Formatting.Indented; 
    using (StreamWriter sw = new StreamWriter(storagePath,  
appendToExistingFile)) { using (JsonWriter writer = new JsonTextWriter(sw)) { json.Serialize(writer, value); } } }

This method is rather self-explanatory. It acts on a List<StorageSpace> object and will create the JSON data, overwriting a file defined in the storagePath variable.

Lastly, we need to be able to read the data back again into a List<StorageSpace> object and return that to the calling code:

public static List<StorageSpace> ReadFromDataStore(this List<StorageSpace> value, string storagePath) 
{ 
    JsonSerializer json = new JsonSerializer(); 
    if (!File.Exists(storagePath)) 
    { 
        var newFile = File.Create(storagePath); 
        newFile.Close(); 
    } 
    using (StreamReader sr = new StreamReader(storagePath)) 
    { 
        using (JsonReader reader = new JsonTextReader(sr)) 
        { 
            var retVal = 
json.Deserialize<List<StorageSpace>>(reader); if (retVal is null) retVal = new List<StorageSpace>(); return retVal; } } }

The method will return an empty List<StorageSpace> object and nothing is contained in the file. The ExtensionMethods class can contain many more extension methods that you might use often. It is a great way to separate often-used code.

The DocumentEngine class

The purpose of this class is merely to provide supporting code to a document. In the eBookManager application, I am going to use a single method called GetFileProperties() that will (you guessed it) return the properties of a selected file. This class also only contains this single method. As the application is modified for your specific purposes, you can modify this class and add additional methods specific to documents.

The DocumentEngine class introduces us to the next feature of C# 7 called tuples. What do tuples do exactly? It is often a requirement for a developer to return more than a single value from a method. Among other Solutions, you can of course use out parameters, but these do not work in async methods. Tuples provide a better way to do this.

Inside the DocumentEngine class, add the following code:

public (DateTime dateCreated, DateTime dateLastAccessed, string fileName, string fileExtension, long fileLength, bool error) GetFileProperties(string filePath) 
{ 
    var returnTuple = (created: DateTime.MinValue,
lastDateAccessed: DateTime.MinValue, name: "", ext: "",
fileSize: 0L, error: false); try { FileInfo fi = new FileInfo(filePath); fi.Refresh(); returnTuple = (fi.CreationTime, fi.LastAccessTime, fi.Name,
fi.Extension, fi.Length, false); } catch { returnTuple.error = true; } return returnTuple; }

The GetFileProperties() method returns a tuple as (DateTime dateCreated, DateTime dateLastAccessed, string fileName, string fileExtension, long fileLength, bool error) and allows us to inspect the values returned from the calling code easily.

Before I try to get the properties of the specific file, I initialize the tuple by doing the following:

var returnTuple = (created: DateTime.MinValue, lastDateAccessed: DateTime.MinValue, name: "", ext: "", fileSize: 0L, error: false); 

If there is an exception, I can return default values. Reading the file properties is simple enough using the FileInfo class. I can then assign the file properties to the tuple by doing this:

returnTuple = (fi.CreationTime, fi.LastAccessTime, fi.Name, fi.Extension, fi.Length, false); 

The tuple is then returned to the calling code where it will be used as required. We will have a look at the calling code next.

The ImportBooks form

The ImportBooks form does exactly what the name suggests. It allows us to create Virtual Storage Spaces and to import books into those spaces. The form design is as follows:

The TreeView controls are prefixed with tv, buttons with btn, combo boxes with dl, textboxes with txt, and date time pickers with dt. When this form loads, if there are any storage spaces defined then these will be listed in the dlVirtualStorageSpaces combo box. Clicking on the Select source folder button will allow us to select a source folder to look for eBooks.

If a storage space does not exist, we can add a new Virtual Storage Space by clicking the btnAddNewStorageSpace button. This will allow us to add a name and description for the new storage space and click on the btnSaveNewStorageSpace button.

Selecting an eBook from the tvFoundBooks TreeView will populate the File details group of controls to the right of the form. You can then add additional Book details and click on the btnAddeBookToStorageSpace button to add the book to our space:

  1. You need to ensure that the following namespaces are added to your ImportBooks class:
using eBookManager.Engine; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Windows.Forms; 
using static eBookManager.Helper.ExtensionMethods; 
using static System.Math; 
  1. Next, let's start at the most logical place to begin with, which is the constructor ImportBooks() and the form variables. Add the following code above the constructor:
private string _jsonPath; 
private List<StorageSpace> spaces; 
private enum StorageSpaceSelection { New = -9999, NoSelection = -1 } 

The usefulness of the enumerator will become evident later on in code. The _jsonPath variable will contain the path to the file used to store our eBook information.

  1. Modify the constructor as follows:
public ImportBooks() 
{ 
    InitializeComponent(); 
    _jsonPath = Path.Combine(Application.StartupPath, 
"bookData.txt"); spaces = spaces.ReadFromDataStore(_jsonPath); }

The _jsonPath is initialized to the executing folder for the application and the file hard coded to bookData.txt. You could provide a settings screen if you wanted to configure these settings, but I just decided to make the application use a hard-coded setting.

  1. Next, we need to add another enumerator that defines the file extensions that we will be able to save in our application. It is here that we will see another feature of C# 7 called expression-bodied properties.

Expression-bodied accessors, constructors, and finalizers

If the following expression looks intimidating, it's because it is using a feature introduced in C# 6 and expanded in C# 7:

private HashSet<string> AllowedExtensions => new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { ".doc",".docx",".pdf", ".epub" }; 
private enum Extention { doc = 0, docx = 1, pdf = 2, epub = 3 } 

The preceding example returns a HashSet of allowed file extensions for our application. These have been around since C# 6, but have been extended in C# 7 to include accessors, constructors, and finalizers. Let's simplify the examples a bit.

Assume that we had to modify the Document class to set a field for _defaultDate inside the class; traditionally, we would need to do this:

private DateTime _defaultDate; 
 
public Document() 
{ 
    _defaultDate = DateTime.Now; 
} 

In C# 7, we can greatly simplify this code by simply doing the following:

private DateTime _defaultDate; 
public Document() => _defaultDate = DateTime.Now; 

This is perfectly legal and compiles correctly. Similarly, the same can be done with finalizers (or deconstructors). Another nice implementation of this is expression-bodied properties as seen with the AllowedExtensions property. The expression-bodied properties have actually been around since C# 6, but who's counting? 

Suppose that we wanted to just return the string value of the Extension enumeration for PDFs, we could do something such as the following:

public string PDFExtension 
{ 
    get 
    { 
        return nameof(Extention.pdf); 
    } 
} 

That property only has a get accessor and will never return anything other than the string value of Extension.pdf. Simplify that by changing the code to:

public string PDFExtension => nameof(Extention.pdf); 

That's it. A single line of code does exactly the same thing as the previous seven lines of code. Falling into the same category, expression-bodied property accessors are also simplified. Consider the following 11 lines of code:

public string DefaultSavePath 
{ 
    get 
    { 
        return _jsonPath; 
    } 
    set 
    { 
        _jsonPath = value; 
    } 
} 

With C# 7, we can simplify this to the following:

public string DefaultSavePath 
{ 
    get => _jsonPath; 
    set => _jsonPath = value; 
} 

This makes our code a lot more readable and quicker to write. Swing back to our AllowedExtensions property; traditionally, it would be written as follows:

private HashSet<string> AllowedExtensions 
{ 
    get 
    { 
        return new HashSet<string> 
(StringComparer.InvariantCultureIgnoreCase) { ".doc",
".docx", ".pdf", ".epub" }; } }

Since C# 6, we have been able to simplify this, as we saw previously. This gives developers a great way to reduce unnecessary code.

Populating the TreeView control

We can see the implementation of the AllowedExtensions property when we look at the PopulateBookList() method. All that this method does is populate the TreeView control with files and folders found at the selected source location. Consider the following code:

public void PopulateBookList(string paramDir, TreeNode paramNode) 
{ 
    DirectoryInfo dir = new DirectoryInfo(paramDir); 
    foreach (DirectoryInfo dirInfo in dir.GetDirectories()) 
    { 
        TreeNode node = new TreeNode(dirInfo.Name); 
        node.ImageIndex = 4; 
        node.SelectedImageIndex = 5; 
 
        if (paramNode != null) 
            paramNode.Nodes.Add(node); 
        else 
            tvFoundBooks.Nodes.Add(node); 
        PopulateBookList(dirInfo.FullName, node); 
    } 
    foreach (FileInfo fleInfo in dir.GetFiles().Where
(x => AllowedExtensions.Contains(x.Extension)).ToList()) { TreeNode node = new TreeNode(fleInfo.Name); node.Tag = fleInfo.FullName; int iconIndex = Enum.Parse(typeof(Extention),
fleInfo.Extension.TrimStart('.'), true).GetHashCode(); node.ImageIndex = iconIndex; node.SelectedImageIndex = iconIndex; if (paramNode != null) paramNode.Nodes.Add(node); else tvFoundBooks.Nodes.Add(node); } }

The first place we need to call this method is obviously from within itself, as this is a recursive method. The second place we need to call it is from the btnSelectSourceFolder button click event:

private void btnSelectSourceFolder_Click(object sender, EventArgs e) 
{ 
    try 
    { 
        FolderBrowserDialog fbd = new FolderBrowserDialog(); 
        fbd.Description = "Select the location of your eBooks and 
documents"; DialogResult dlgResult = fbd.ShowDialog(); if (dlgResult == DialogResult.OK) { tvFoundBooks.Nodes.Clear(); tvFoundBooks.ImageList = tvImages; string path = fbd.SelectedPath; DirectoryInfo di = new DirectoryInfo(path); TreeNode root = new TreeNode(di.Name); root.ImageIndex = 4; root.SelectedImageIndex = 5; tvFoundBooks.Nodes.Add(root); PopulateBookList(di.FullName, root); tvFoundBooks.Sort(); root.Expand(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } }

This is all quite straightforward code. Select the folder to recurse and populate the TreeView control with all the files found that match the file extension contained in our AllowedExtensions property.

We also need to look at the code when someone selects a book in the tvFoundBooks TreeView control. When a book is selected, we need to read the properties of the selected file and return those properties to the file details section:

private void tvFoundBooks_AfterSelect(object sender, TreeViewEventArgs e) 
{ 
    DocumentEngine engine = new DocumentEngine(); 
    string path = e.Node.Tag?.ToString() ?? ""; 
 
    if (File.Exists(path)) 
    { 
        var (dateCreated, dateLastAccessed, fileName, 
fileExtention, fileLength, hasError) =
engine.GetFileProperties(e.Node.Tag.ToString()); if (!hasError) { txtFileName.Text = fileName; txtExtension.Text = fileExtention; dtCreated.Value = dateCreated; dtLastAccessed.Value = dateLastAccessed; txtFilePath.Text = e.Node.Tag.ToString(); txtFileSize.Text = $"{Round(fileLength.ToMegabytes(),
2).ToString()} MB"; } } }

You will notice that it is here that we are calling the method GetFileProperties() on the DocumentEngine class that returns the tuple.

Local functions

This is one of those features in C# 7 that I truly wondered where I would ever find a use for. As it turns out, local functions are extremely useful indeed. Also called nested functions by some, these functions are nested within another parent function. It is obviously only within scope inside the parent function and adds a useful way to call code that otherwise wouldn't have any real purpose outside the parent function. Consider the PopulateStorageSpacesList() method:

private void PopulateStorageSpacesList() 
{ 
    List<KeyValuePair<int, string>> lstSpaces = 
new List<KeyValuePair<int, string>>(); BindStorageSpaceList((int)StorageSpaceSelection.NoSelection,
"Select Storage Space"); void BindStorageSpaceList(int key, string value)
// Local function { lstSpaces.Add(new KeyValuePair<int, string>(key, value)); } if (spaces is null || spaces.Count() == 0) // Pattern matching { BindStorageSpaceList((int)StorageSpaceSelection.New, "
<create new>"); } else { foreach (var space in spaces) { BindStorageSpaceList(space.ID, space.Name); } } dlVirtualStorageSpaces.DataSource = new
BindingSource(lstSpaces, null); dlVirtualStorageSpaces.DisplayMember = "Value"; dlVirtualStorageSpaces.ValueMember = "Key"; }

To see how PopulateStorageSpacesList() calls the local function BindStorageSpaceList(), have a look at the following screenshot:

You will notice that the local function can be called from anywhere within the parent function. In this case, the BindStorageSpaceList() local function does not return anything, but you can return whatever you like from a local function. You could just as well have done the following:

private void SomeMethod() 
{ 
    int currentYear = GetCurrentYear(); 
 
    int GetCurrentYear(int iAddYears = 0) 
    { 
        return DateTime.Now.Year + iAddYears; 
    } 
 
    int nextYear = GetCurrentYear(1); 
} 

The local function is accessible from anywhere within the parent function.

Pattern matching

Staying with the PopulateStorageSpacesList() method, we can see the use of another C# 7 feature called pattern matching. The spaces is null line of code is probably the simplest form of pattern matching. In reality, pattern matching supports several patterns.

Consider a switch statement:

switch (objObject) 
{ 
    case null: 
        WriteLine("null"); // Constant pattern 
        break; 
 
    case Document doc when doc.Author.Equals("Stephen King"): 
        WriteLine("Stephen King is the author"); 
        break; 
 
    case Document doc when doc.Author.StartsWith("Stephen"): 
        WriteLine("Stephen is the author"); 
        break; 
 
    default: 
        break; 
} 

Pattern matching allows developers to use the is expression to see whether something matches a specific pattern. Bear in mind that the pattern needs to check for the most specific to the most general pattern. If you simply started the case with case Document doc: then all the objects passed to the switch statement of type Document would match. You would never find specific documents where the author is Stephen King or starts with Stephen.

For a construct inherited by C# from the C language, it hasn't changed much since the '70s. C# 7 changes all that with pattern matching.

Finishing up the ImportBooks code

Let's have a look at the rest of the code in the ImportBooks form. The form load just populates the storage spaces list, if any existing storage spaces have been previously saved:

private void ImportBooks_Load(object sender, EventArgs e) 
{ 
    PopulateStorageSpacesList(); 
                         
    if (dlVirtualStorageSpaces.Items.Count == 0) 
    { 
        dlVirtualStorageSpaces.Items.Add("<create new storage 
space>"); } lblEbookCount.Text = ""; }

We now need to add the logic for changing the selected storage space. The SelectedIndexChanged() event of the dlVirtualStorageSpaces control is modified as follows:

private void dlVirtualStorageSpaces_SelectedIndexChanged(object sender, EventArgs e) 
{ 
    int selectedValue = 
dlVirtualStorageSpaces.SelectedValue.ToString().ToInt(); if (selectedValue == (int)StorageSpaceSelection.New) // -9999 { txtNewStorageSpaceName.Visible = true; lblStorageSpaceDescription.Visible = true; txtStorageSpaceDescription.ReadOnly = false; btnSaveNewStorageSpace.Visible = true; btnCancelNewStorageSpaceSave.Visible = true; dlVirtualStorageSpaces.Enabled = false; btnAddNewStorageSpace.Enabled = false; lblEbookCount.Text = ""; } else if (selectedValue !=
(int)StorageSpaceSelection.NoSelection) { // Find the contents of the selected storage space int contentCount = (from c in spaces where c.ID == selectedValue select c).Count(); if (contentCount > 0) { StorageSpace selectedSpace = (from c in spaces where c.ID ==
selectedValue select c).First(); txtStorageSpaceDescription.Text =
selectedSpace.Description; List<Document> eBooks = (selectedSpace.BookList ==
null)
? new List<Document> { } : selectedSpace.BookList; lblEbookCount.Text = $"Storage Space contains
{eBooks.Count()} {(eBooks.Count() == 1 ? "eBook" :
"eBooks")}"; } } else { lblEbookCount.Text = ""; } }

I will not go into any detailed explanation of the code here as it is relatively obvious what it is doing.

Throw expressions

We also need to add the code to save a new storage space. Add the following code to the Click event of the btnSaveNewStorageSpace button:

private void btnSaveNewStorageSpace_Click(object sender,
EventArgs e) { try { if (txtNewStorageSpaceName.Text.Length != 0) { string newName = txtNewStorageSpaceName.Text; // throw expressions: bool spaceExists =
(space exists = false) ? return false : throw exception // Out variables bool spaceExists = (!spaces.StorageSpaceExists
(newName, out int nextID)) ? false : throw new
Exception("The storage space you are
trying to add already exists."); if (!spaceExists) { StorageSpace newSpace = new StorageSpace(); newSpace.Name = newName; newSpace.ID = nextID; newSpace.Description =
txtStorageSpaceDescription.Text; spaces.Add(newSpace); PopulateStorageSpacesList(); // Save new Storage Space Name txtNewStorageSpaceName.Clear(); txtNewStorageSpaceName.Visible = false; lblStorageSpaceDescription.Visible = false; txtStorageSpaceDescription.ReadOnly = true; txtStorageSpaceDescription.Clear(); btnSaveNewStorageSpace.Visible = false; btnCancelNewStorageSpaceSave.Visible = false; dlVirtualStorageSpaces.Enabled = true; btnAddNewStorageSpace.Enabled = true; } } } catch (Exception ex) { txtNewStorageSpaceName.SelectAll(); MessageBox.Show(ex.Message); } }

Here, we can see another new feature in the C# 7 language called throw expressions. This gives developers the ability to throw exceptions from expressions. The code in question is this code:

bool spaceExists = (!spaces.StorageSpaceExists(newName, out int nextID)) ? false : throw new Exception("The storage space you are trying to add already exists."); 

I always like to remember the structure of the code as follows:

The last few methods deal with saving eBooks in the selected Virtual Storage Space. Modify the Click event of the btnAddBookToStorageSpace button. This code also contains a throw expression. If you haven't selected a storage space from the combo box, a new exception is thrown:

private void btnAddeBookToStorageSpace_Click(object sender, EventArgs e) 
{ 
    try 
    { 
        int selectedStorageSpaceID = 
dlVirtualStorageSpaces.SelectedValue.ToString().ToInt(); if ((selectedStorageSpaceID !=
(int)StorageSpaceSelection.NoSelection)
&& (selectedStorageSpaceID !=
(int)StorageSpaceSelection.New)) { UpdateStorageSpaceBooks(selectedStorageSpaceID); } else throw new Exception("Please select a Storage
Space to add your eBook to"); // throw expressions } catch (Exception ex) { MessageBox.Show(ex.Message); } }

Developers can now immediately throw exceptions in code right there where they occur. This is rather nice and makes code cleaner and its intent clearer.

Saving a selected book to a storage space

The following code basically updates the book list in the selected storage space if it already contains the specific book (after confirming with the user). Otherwise, it will add the book to the book list as a new book:

private void UpdateStorageSpaceBooks(int storageSpaceId) 
{ 
    try 
    { 
        int iCount = (from s in spaces 
                      where s.ID == storageSpaceId 
                      select s).Count(); 
        if (iCount > 0) // The space will always exist 
        { 
            // Update 
            StorageSpace existingSpace = (from s in spaces 
              where s.ID == storageSpaceId select s).First(); 
 
            List<Document> ebooks = existingSpace.BookList; 
 
            int iBooksExist = (ebooks != null) ? (from b in ebooks 
              where $"{b.FileName}".Equals($"
{txtFileName.Text.Trim()}") select b).Count() : 0; if (iBooksExist > 0) { // Update existing book DialogResult dlgResult = MessageBox.Show($"A book
with the same name has been found in Storage Space
{existingSpace.Name}.
Do you want to replace the existing book
entry with this one?",
"Duplicate Title", MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2); if (dlgResult == DialogResult.Yes) { Document existingBook = (from b in ebooks where $"
{b.FileName}".Equals($"
{txtFileName.Text.Trim()}") select b).First(); existingBook.FileName = txtFileName.Text; existingBook.Extension = txtExtension.Text; existingBook.LastAccessed =
dtLastAccessed.Value; existingBook.Created = dtCreated.Value; existingBook.FilePath = txtFilePath.Text; existingBook.FileSize = txtFileSize.Text; existingBook.Title = txtTitle.Text; existingBook.Author = txtAuthor.Text; existingBook.Publisher = txtPublisher.Text; existingBook.Price = txtPrice.Text; existingBook.ISBN = txtISBN.Text; existingBook.PublishDate =
dtDatePublished.Value; existingBook.Category = txtCategory.Text; } } else { // Insert new book Document newBook = new Document(); newBook.FileName = txtFileName.Text; newBook.Extension = txtExtension.Text; newBook.LastAccessed = dtLastAccessed.Value; newBook.Created = dtCreated.Value; newBook.FilePath = txtFilePath.Text; newBook.FileSize = txtFileSize.Text; newBook.Title = txtTitle.Text; newBook.Author = txtAuthor.Text; newBook.Publisher = txtPublisher.Text; newBook.Price = txtPrice.Text; newBook.ISBN = txtISBN.Text; newBook.PublishDate = dtDatePublished.Value; newBook.Category = txtCategory.Text; if (ebooks == null) ebooks = new List<Document>(); ebooks.Add(newBook); existingSpace.BookList = ebooks; } } spaces.WriteToDataStore(_jsonPath); PopulateStorageSpacesList(); MessageBox.Show("Book added"); } catch (Exception ex) { MessageBox.Show(ex.Message); } }

Lastly, as a matter of housekeeping, the ImportBooks form contains the following code for displaying and enabling controls based on the button click events of btnCancelNewStorageSpace and btnAddNewStorageSpace buttons:

private void btnCancelNewStorageSpaceSave_Click(object sender, EventArgs e) 
{ 
    txtNewStorageSpaceName.Clear(); 
    txtNewStorageSpaceName.Visible = false; 
    lblStorageSpaceDescription.Visible = false; 
    txtStorageSpaceDescription.ReadOnly = true; 
    txtStorageSpaceDescription.Clear(); 
    btnSaveNewStorageSpace.Visible = false; 
    btnCancelNewStorageSpaceSave.Visible = false; 
    dlVirtualStorageSpaces.Enabled = true; 
    btnAddNewStorageSpace.Enabled = true; 
} 
 
private void btnAddNewStorageSpace_Click(object sender, EventArgs e) 
{ 
    txtNewStorageSpaceName.Visible = true; 
    lblStorageSpaceDescription.Visible = true; 
    txtStorageSpaceDescription.ReadOnly = false; 
    btnSaveNewStorageSpace.Visible = true; 
    btnCancelNewStorageSpaceSave.Visible = true; 
    dlVirtualStorageSpaces.Enabled = false; 
    btnAddNewStorageSpace.Enabled = false; 
} 

All that remains now is for us to complete the code in the Form1.cs form, which is the start-up form.

Main eBookManager form

Start off by renaming Form1.cs to eBookManager.cs. This is the start-up form for the application and it will list all the existing storage spaces previously saved:

Design your eBookManager form as follows:

  • ListView control for existing storage spaces
  • ListView for eBooks contained in selected storage space
  • Button that opens the file location of the eBook
  • A menu control to navigate to the ImportBooks.cs form
  • Various read-only fields to display the selected eBook information

When you have added the controls, your eBook Manager form will look as follows:

Looking at the code we used earlier, you need to ensure that the following using statements are imported:

using eBookManager.Engine; 
using eBookManager.Helper; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Windows.Forms; 
using System.Linq; 
using System.Diagnostics; 

The constructor is quite similar to the ImportBooks.cs form's constructor. It reads any available storage spaces and populates the storage spaces list view control with the previously saved storage spaces:

private string _jsonPath; 
private List<StorageSpace> spaces; 
 
public eBookManager() 
{ 
    InitializeComponent(); 
    _jsonPath = Path.Combine(Application.StartupPath, 
"bookData.txt"); spaces = spaces.ReadFromDataStore(_jsonPath); } private void Form1_Load(object sender, EventArgs e) { PopulateStorageSpaceList(); } private void PopulateStorageSpaceList() { lstStorageSpaces.Clear(); if (!(spaces == null)) { foreach (StorageSpace space in spaces) { ListViewItem lvItem = new ListViewItem(space.Name, 0); lvItem.Tag = space.BookList; lvItem.Name = space.ID.ToString(); lstStorageSpaces.Items.Add(lvItem); } } }

If the user clicks on a storage space, we need to be able to read the books contained in that selected space:

private void lstStorageSpaces_MouseClick(object sender, MouseEventArgs e) 
{ 
    ListViewItem selectedStorageSpace = 
lstStorageSpaces.SelectedItems[0]; int spaceID = selectedStorageSpace.Name.ToInt(); txtStorageSpaceDescription.Text = (from d in spaces where d.ID == spaceID select
d.Description).First(); List<Document> ebookList =
(List<Document>)selectedStorageSpace.Tag; PopulateContainedEbooks(ebookList);
}

We now need to create the method that will populate the lstBooks list view with the books contained in the selected storage space:

private void PopulateContainedEbooks(List<Document> ebookList) 
{ 
    lstBooks.Clear(); 
    ClearSelectedBook(); 
 
    if (ebookList != null) 
    { 
        foreach (Document eBook in ebookList) 
        { 
            ListViewItem book = new ListViewItem(eBook.Title, 1); 
            book.Tag = eBook; 
            lstBooks.Items.Add(book); 
        } 
    } 
    else 
    { 
        ListViewItem book = new ListViewItem("This storage space 
contains no eBooks", 2); book.Tag = ""; lstBooks.Items.Add(book); } }

You will notice that each ListViewItem is populated with the title of the eBook and the index of an image in an ImageList control that I added to the form. To find the images in the GitHub repository, browse to the following path:

https://github.com/PacktPublishing/CSharp7-and-.NET-Core-2.0-Blueprints/tree/master/eBookManager/eBookManager/img

Looking at the Images Collection Editor, you will see that I have added them as follows:

We also need to clear the selected book's details when the selected storage space is changed. I have created two group controls around the file and book details. This code just loops through all the child controls, and if the child control is a textbox, it clears it:

private void ClearSelectedBook() 
{ 
    foreach (Control ctrl in gbBookDetails.Controls) 
    { 
        if (ctrl is TextBox) 
            ctrl.Text = ""; 
    } 
 
    foreach (Control ctrl in gbFileDetails.Controls) 
    { 
        if (ctrl is TextBox) 
            ctrl.Text = ""; 
    } 
 
    dtLastAccessed.Value = DateTime.Now; 
    dtCreated.Value = DateTime.Now; 
    dtDatePublished.Value = DateTime.Now; 
} 

The MenuStrip that was added to the form has a click event on the ImportEBooks menu item. It simply opens up the ImportBooks form:

private void mnuImportEbooks_Click(object sender, EventArgs e) 
{ 
    ImportBooks import = new ImportBooks(); 
    import.ShowDialog(); 
    spaces = spaces.ReadFromDataStore(_jsonPath); 
    PopulateStorageSpaceList(); 
} 

The following method wraps up the logic to select a specific eBook and populate the file and eBook details on the eBookManager form:

private void lstBooks_MouseClick(object sender, MouseEventArgs e) 
{ 
    ListViewItem selectedBook = lstBooks.SelectedItems[0]; 
    if (!String.IsNullOrEmpty(selectedBook.Tag.ToString())) 
    { 
        Document ebook = (Document)selectedBook.Tag; 
        txtFileName.Text = ebook.FileName; 
        txtExtension.Text = ebook.Extension; 
        dtLastAccessed.Value = ebook.LastAccessed; 
        dtCreated.Value = ebook.Created; 
        txtFilePath.Text = ebook.FilePath; 
        txtFileSize.Text = ebook.FileSize; 
        txtTitle.Text = ebook.Title; 
        txtAuthor.Text = ebook.Author; 
        txtPublisher.Text = ebook.Publisher; 
        txtPrice.Text = ebook.Price; 
        txtISBN.Text = ebook.ISBN; 
        dtDatePublished.Value = ebook.PublishDate; 
        txtCategory.Text = ebook.Category; 
    } 
} 

Lastly, when the book selected is the one you wish to read, click on the Read eBook button to open the file location of the selected eBook:

private void btnReadEbook_Click(object sender, EventArgs e) 
{ 
    string filePath = txtFilePath.Text; 
    FileInfo fi = new FileInfo(filePath); 
    if (fi.Exists) 
    { 
        Process.Start(Path.GetDirectoryName(filePath)); 
    } 
} 

This completes the code logic contained in the eBookManager application.

You can further modify the code to open the required application for the selected eBook instead of just the file location. In other words, if you click on a PDF document, the application can launch a PDF reader with the document loaded. Lastly, note that the classification has not been implemented in this version of the application.

It is time to fire up the application and test it out.

Running the eBookManager application

When the application starts for the first time, there will be no Virtual Storage Spaces available. To create one, we will need to import some books. Click on the Import eBooks menu item:

The Import eBooks screen opens where you can add a new storage space and Select source folder for eBooks:

Once you have selected an eBook, add the applicable information regarding the book and save it to the storage space:

After you have added all the storage spaces and eBooks, you will see the Virtual Storage Spaces listed. As you click on a storage space, the books it contains will be listed:

Selecting an eBook and clicking on the Read eBook button will open up the file location containing the selected eBook:

Lastly, let's have a look at the JSON file generated for the eBook Manager application:

As you can see, the JSON file is quite nicely laid out and it is easily readable.

Summary

C# 7 is a fantastic version of the language. In this chapter, we had a look at out variables. You will remember that with C# 7 we now have the ability to declare the variable right at the point it is passed as an out argument. We then looked at tuples, which provide an elegant way to return multiple values from a method.

Moving, on we looked at expression-bodied properties, which is a more succinct way to write your code. Then, we discussed local functions (one of my favorite features) and its ability to create a helper function inside another function. This makes sense if the function that uses the local function is the only code that uses it.

Pattern matching was up next and are syntactic elements that look to see if a specific value has a certain shape. This makes switch statements (for example) nicer to use. Lastly, we looked at throw expressions. This makes it possible to add exception throwing to our expression-bodied members, conditional and null-coalescing expressions.

As you continue to use C# 7, you will discover more opportunities to use these new features. At first (for me anyway) I had to purposefully condition myself to write code using a new feature (out variables being a perfect example).

After a while, the convenience of doing that becomes second nature. You will soon start to automatically write code using the new features available to you.

Left arrow icon Right arrow icon

Key benefits

  • Easy-to-follow real-world projects that get you up and running with the new features of C# 7 and .NET Core 2.0
  • The practical applications will assist you with concepts such as Entity Framework Core, serverless computing, and more in .NET Core 2.0
  • Explore OAuth concepts and build ASP.NET Core applications using MongoDB

Description

.NET Core is a general purpose, modular, cross-platform, and open source implementation of .NET. With the latest release of .NET Core, many more APIs are expected to show up, which will make APIs consistent across .Net Framework, .NET Core, and Xamarin. This step-by-step guide will teach you the essential .NET Core and C# concepts with the help of real-world projects. The book starts with a brief introduction to the latest features of C# 7 and .NET Core 2.0 before moving on to explain how C# 7 can be implemented using the object-oriented paradigm. You'll learn to work with relational data using Entity Framework and see how to use ASP.NET Core practically. This book will show you how .NET Core allows the creations of cross-platform applications. You'll also learn about SignalR to add real-time functionality to your application. Then you will see how to use MongoDB and how to implement MongoDB into your applications. You'll learn about serverless computing and OAuth concepts, along with running ASP.NET Core applications with Docker Compose. This project-based guide uses practical applications to demonstrate these concepts. By the end of the book, you'll be proficient in developing applications using .NET Core 2.0.

Who is this book for?

This book is for .NET developers who would like to master and implement C# 7 and .NET Core 2.0 with practical projects. Basic knowledge of .NET Core and C# is assumed.

What you will learn

  • • How to incorporate Entity Framework Core to build ASP .NET Core MVC applications
  • • Get hands-on experience with SignalR, and NuGet packages
  • • Working with MongoDB in your ASP.NET Core MVC application
  • • Get hands-on experience with .NET Core MVC, Middleware, Controllers, Views, Layouts, Routing, and OAuth
  • • Implementing Azure Functions and learn what Serverless computing means
  • • See how .NET Core enables cross-platform applications that run on Windows, macOS and Linux
  • • Running a .NET Core MVC application with Docker Compose
Estimated delivery fee Deliver to Portugal

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Mar 28, 2018
Length: 428 pages
Edition : 1st
Language : English
ISBN-13 : 9781788396196
Vendor :
Microsoft
Category :
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Portugal

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Publication date : Mar 28, 2018
Length: 428 pages
Edition : 1st
Language : English
ISBN-13 : 9781788396196
Vendor :
Microsoft
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 120.97
Building Microservices with .NET Core 2.0
€36.99
C# 7 and .NET Core 2.0 Blueprints
€41.99
.NET Core 2.0 By Example
€41.99
Total 120.97 Stars icon
Banner background image

Table of Contents

10 Chapters
eBook Manager and Catalogue App Chevron down icon Chevron up icon
Cricket Score Calculator and Tracker Chevron down icon Chevron up icon
Cross Platform .NET Core System Info Manager Chevron down icon Chevron up icon
Task Bug Logging ASP .NET Core MVC App Chevron down icon Chevron up icon
ASP.NET SignalR Chat Application Chevron down icon Chevron up icon
Web Research Tool with Entity Framework Core Chevron down icon Chevron up icon
A Serverless Email Validation Azure Function Chevron down icon Chevron up icon
Twitter Clone Using OAuth Chevron down icon Chevron up icon
Using Docker and ASP.NET Core Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Full star icon 5
(1 Ratings)
5 star 100%
4 star 0%
3 star 0%
2 star 0%
1 star 0%
Prakash Mar 28, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Hi Guys. I was the technical reviewer of this book. The sequence of topics are arranged nicely that makes easy for readers to follow. Practical tips are added wherever required to make it more useful for day-to-day work. The best part of this book is project driven approach that let you apply the concept in the working demo to concrete the understanding and make yourself comfortable enough to apply the same in your day-to-day programming. Moreover, this book not only talks about using C# 7 and .NET Core but also the related topics to get best out of it, In summary, It has great content and code examples and worth buying.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela