Using Cloud Datastore with .NET

This part of the .NET Bookshelf tutorial shows how to create, read, update, and delete structured data in Cloud Datastore.

This page is part of a multi-page tutorial. To start from the beginning and read the setup instructions, go to .NET Bookshelf App.

Configuring settings

  1. To open the sample app in Visual Studio, in the getting-started-dotnet\aspnet\2-structured-data directory, double-click 2-structured-data.sln.

  2. In the Solution Explorer pane, click Web.config.

  3. In Web.config, complete the following steps:

    1. Set GoogleCloudSamples:ProjectId to your project ID.

    2. Set GoogleCloudSamples:BookStore to datastore.

  4. Save and close Web.config.

Running the app on your local machine

In Visual Studio, press F5 to run the project. Now you can browse the app's web pages to add, edit, and delete books.

Deploying the app to Compute Engine

  1. In Visual Studio, in the Solution Explorer pane, right-click 2-structured-data, and click Publish.

    Publish app

  2. In the Publish Web dialog, select Custom as your publish target.

  3. In the New Custom Profile dialog, for Profile name, enter bookshelf-profile. Click OK.

  4. Fill out your profile:

    1. For Server, enter the external IP address of your Compute Engine instance.

    2. For Site name, enter Default Web Site.

    3. For User name, enter the username of the Windows user account you created on your Compute Engine instance.

    4. For Password, enter the password of the Windows user account you created on your Compute Engine instance.

    5. For Destination URL, enter http://[EXTERNAL_IP_ADDRESS_OF_YOUR_COMPUTE_ENGINE_INSTANCE].

      Where:

      • [EXTERNAL_IP_ADDRESS_OF_YOUR_COMPUTE_ENGINE_INSTANCE] represents the external IP address of your Compute Engine instance.
  5. To check your profile credentials and configuration, click Validate Connection.

  6. Because the Microsoft IIS installation in your deployment uses a self-signed certificate by default, a Certificate Error displays during the validation process. Click the box to Save this certificate for future sessions of Visual Studio, and then click Accept to accept the certificate.

  7. Click Settings.

  8. Click File Publish Options, and select Remove additional files at destination. This is important for later steps when you publish new web sites to the same Compute Engine instance.

  9. To deploy the sample web app, click Publish. After publishing completes, Visual Studio opens the app in your default web browser.

App structure

This diagram shows the app's components and how they fit together. It follows the classic ASP.NET MVC pattern. An IBookStore interface lives between the BooksController and DatastoreBookStore so that you can switch to storing your book data in Cloud SQL without changing any code.

Bookshelf app structure

Understanding the code

This section walks you through the app's code and explains how it works.

The data model

The Book class keeps information about one book as well as additional fields that are used in later tutorials.

    [Bind(Include = "Title, Author, PublishedDate, Description")]
    public class Book
    {
        [Key]
        public long Id { get; set; }

        [Required]
        public string Title { get; set; }

        public string Author { get; set; }

        [Display(Name = "Date Published")]
        [DataType(DataType.Date)]
        public DateTime? PublishedDate { get; set; }

        public string ImageUrl { get; set; }

        [DataType(DataType.MultilineText)]
        public string Description { get; set; }

        public string CreatedById { get; set; }
    }

Handling user submissions with forms

The add/edit HTML form allows users to add and edit book submissions within the app.

Image of add/edit form

The HTML form is created using Razor, templates. This Razor template specifies that the form include text input fields for Title, Author, Date Published, and Description.

<form action="/Books/@Model.FormAction/@Model.Book.Id" method="post" id="book-form" enctype="multipart/form-data">
    @Html.AntiForgeryToken()
    <div class="form-group">
        @Html.LabelFor(model => model.Book.Title)
        @Html.EditorFor(model => model.Book.Title, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Book.Title, "", new { @class = "text-danger" })
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Book.Author)
        @Html.EditorFor(model => model.Book.Author, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Book.Author, "", new { @class = "text-danger" })
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Book.PublishedDate)
        @Html.EditorFor(model => model.Book.PublishedDate, new { htmlAttributes = new { @class = "form-control", @type = "text" } })
        @Html.ValidationMessageFor(model => model.Book.PublishedDate, "", new { @class = "text-danger" })
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Book.Description)
        @Html.EditorFor(model => model.Book.Description, new { htmlAttributes = new { @class = "form-control", @type = "text" } })
        @Html.ValidationMessageFor(model => model.Book.Description, "", new { @class = "text-danger" })
    </div>

    <button type="submit" class="btn btn-success">Save</button>
</form>

Handling form submissions

When you click Add Book, the BooksController.Create() method displays the form. After you fill in the form and click Save, the BooksController.Create() method receives the form's contents and sends the contents to the Cloud SQL database via the IBookStore::Create() method. Note that the Create method is annotated with HttpPost.

        // GET: Books/Create
        public ActionResult Create()
        {
            return ViewForm("Create", "Create");
        }

        // POST: Books/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Book book)
        {
            if (ModelState.IsValid)
            {
                _store.Create(book);
                return RedirectToAction("Details", new { id = book.Id });
            }
            return ViewForm("Create", "Create", book);
        }

The DatastoreBookStore class calls the Cloud Datastore API to perform queries and to perform Create, Read, Update, and Delete (CRUD) operations.

Here is the Create method:

public void Create(Book book)
{
    var entity = book.ToEntity();
    entity.Key = _db.CreateKeyFactory("Book").CreateIncompleteKey();
    var keys = _db.Insert(new[] { entity });
    book.Id = keys.First().Path.First().Id;
}

First, Create calls CreateIncompleteKey which tells Cloud Datastore to create a key for the new entity. Then, it calls Insert to insert the new book into Cloud Datastore. Finally, it updates the book's ID with the new key created by Cloud Datastore. The IDs aren't created in sequential order.

The DatastoreBookStoreExtensionMethods class has helper methods to make packing a Book into Cloud Datastore easier. The ToEntity helper method converts a Book to a Cloud Datastore entity.

public static Entity ToEntity(this Book book) => new Entity()
{
    Key = book.Id.ToKey(),
    ["Title"] = book.Title,
    ["Author"] = book.Author,
    ["PublishedDate"] = book.PublishedDate?.ToUniversalTime(),
    ["ImageUrl"] = book.ImageUrl,
    ["Description"] = book.Description,
    ["CreateById"] = book.CreatedById
};

See all the helper methods on GitHub.

Listing books

After books are added, click the Books link to go to the /Books page, which lists all the books currently stored in Cloud Datastore. The List method lists all the books by using data retrieved from Cloud Datastore.

public BookList List(int pageSize, string nextPageToken)
{
    var query = new Query("Book") { Limit = pageSize };
    if (!string.IsNullOrWhiteSpace(nextPageToken))
        query.StartCursor = ByteString.FromBase64(nextPageToken);
    var results = _db.RunQuery(query);
    return new BookList()
    {
        Books = results.Entities.Select(entity => entity.ToBook()),
        NextPageToken = results.Entities.Count == query.Limit ?
            results.EndCursor.ToBase64() : null
    };
}

The List method creates and runs a Query to find all entities of kind Book. It uses the query cursors of Cloud Datastore to implement paging.

Was this page helpful? Let us know how we did:

Send feedback about...