Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve Key from Datastore (Update & Delete Entity)

I have been reading the Datastore documentation about keys.

In the documentation it says

"a key is stored as an object not as a value."

This is the result enter image description here

Below is a sample from my kind. ID is the key I am trying to retrieve to update and delete an entity

Sample Data

Display the results

@page
@using TestApp.Models
@model AllSportsStoreModel

<h2>Index</h2>

<p>
    <a asp-page="SportsStore">New Item</a>
</p>
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayName("ID")
                </th>
                <th>
                    @Html.DisplayName("Name")
                </th>
                <th>
                    @Html.DisplayName("Price")
                </th>
                <th>Edit | Delete</th>
            </tr>
        </thead>
        <tbody>
@for (var i = 0; i < Model.SportsStoreList.Count; i++) {
    <tr>
        <td>
            @Html.DisplayFor(model => model.SportsStoreList[i].Id)
        </td>
        <td>
            @Html.DisplayFor(model => model.SportsStoreList[i].PName)
        </td>
        <td>
            @Html.DisplayFor(model => model.SportsStoreList[i].Price)
        </td>
        <td>
            <a asp-page="EditStore" asp-route-Id="SportsStoreList[i].Id">Edit</a> |
            <a asp-page-handler="Delete" asp-route-Id="SportsStoreList[i].Id">Delete</a>
        </td>
    </tr>
}
        </tbody>
    </table>
    <br />

Code Behind:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Google.Cloud.Datastore.V1;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using TestApp.Models;

namespace TestApp.Pages
{
    public class AllSportsStoreModel : PageModel
    {
        private readonly ISportsStore stores;

        public AllSportsStoreModel(ISportsStore stores)
        {
            this.stores = stores;
        }

        [BindProperty]
        public List<Item> SportsStoreList { get; set; }

        public IActionResult OnGet()
        {
            SportsStoreList = stores.ReadAll();
            return Page();
        }

    }
}

DataStoreSportsStore.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Google.Cloud.Datastore.V1;


namespace TestApp.Models
{
    public static class DatastoreBookStoreExtensionMethods
    {
        public static Key ToKey(this long id) => new Key().WithElement("Sports_db", id);

        /// <summary>
        /// Make a id given a datastore key.
        /// </summary>
        /// <param name="key">A datastore key</param>
        /// <returns>A item id.</returns>
        public static long ToId(this Key key) => key.Path.First().Id;

        /// <summary>
        /// Create a datastore entity with the same values as item.
        /// </summary>
        /// <param name="item">The item to store in datastore.</param>
        /// <returns>A datastore entity.</returns>
        public static Entity ToEntity(this Item item) => new Entity()
        {
            Key = item.Id.ToKey(),
            ["PName"] = item.PName,
            ["Price"] = item.Price,
        };

        /// <summary>
        /// Unpack an itemfrom a datastore entity.
        /// </summary>
        /// <param name="entity">An entity retrieved from datastore.</param>
        /// <returns>An Item.</returns>
        public static Item ToItem(this Entity entity) => new Item()
        {
            Id = entity.Key.Path.First().Id,
            PName = (string)entity["PName"],
            Price = (string)entity["Price"]
        };
    }


    public class DatastoreSportsStore : ISportsStore
    {
        string kind = "Sports_db";
        private readonly DatastoreDb _db;

        public DatastoreSportsStore()
        {
            _db = DatastoreDb.Create("projectid");
        }

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

        public Item Read(long id)
        {
            return _db.Lookup(id.ToKey())?.ToItem();
        }

        public List<Item> ReadAll()
        {
            var query = new Query(kind);
            var results = _db.RunQuery(query);
            return results.Entities.Select(entity => entity.ToItem()).ToList();
        }

        public void Update(Item item)
        {
            _db.Update(item.ToEntity());
        }

        public void Delete(long id)
        {
            _db.Delete(id.ToKey());
        }

    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ISportsStore, DatastoreSportsStore>();
    services.AddMvc();
}

Item.cs

namespace TestApp.Models
{
    public class Item
    {
        public long Id { get; set; }
        public string PName { get; set; }
        public string Price { get; set; }
    }
}

How does one retrieve a key from the Datastore Entity to update and delete records using C#?

like image 292
Dan Nick Avatar asked May 14 '18 12:05

Dan Nick


2 Answers

How does one retrieve a key from the Datastore Entity using C#?

By calling the Entity.Key property as mention in the documentation

Key key = entity.Key;

Working with entities

Applications can use the Cloud Datastore API to create, retrieve, update, and delete entities. If the application knows the complete key for an entity (or can derive it from its parent key, kind, and identifier), it can use the key to operate directly on the entity.

Updating an entity

To update an existing entity, modify the properties of the entity previously retrieved and store it using the key:

_item["Name"] = "John";
_item["Price"] = 12.95;
_db.Update(_item);

Deleting an entity

Given an entity's key, you can delete the entity:

_db.Delete(_item.Key);

The assumption above is that _item is a variable of type Entity.

Reference Cloud Datastore Documentation: Entities, Properties, and Keys

Following a very helpful tutorial here

Using Cloud Datastore with .NET

you can try the following.

Assuming you have a model like this (Taken from one of your previous questions)

public class Item {
    public string Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

Note the Id

There was a link to some helper methods on GitHub. That can be adapted to your scenario.

Like

/// <summary>
/// Make a datastore key given a item's id.
/// </summary>
/// <param name="id">An Item's id.</param>
/// <returns>A datastore key.</returns>
public static Key ToKey(this string id) =>
    new Key().WithElement("Sports_db", id);

/// <summary>
/// Make a id given a datastore key.
/// </summary>
/// <param name="key">A datastore key</param>
/// <returns>A item id.</returns>
public static string ToId(this Key key) => key.Path.First().Name;

/// <summary>
/// Create a datastore entity with the same values as item.
/// </summary>
/// <param name="item">The item to store in datastore.</param>
/// <returns>A datastore entity.</returns>
public static Entity ToEntity(this Item item) => new Entity() {
    Key = item.Id.ToKey(),
    ["Name"] = item.Name,
    ["Price"] = item.Price,
};

/// <summary>
/// Unpack an itemfrom a datastore entity.
/// </summary>
/// <param name="entity">An entity retrieved from datastore.</param>
/// <returns>An Item.</returns>
public static Item ToItem(this Entity entity) => new Item() {
    Id = entity.Key.ToId(),
    Name = (string)entity["Name"],
    Price = (decimal)entity["Price"]
};

This would allow you to use the extension method to do the following

SportsStoreList = stores.Select(entity => entity.ToItem()).ToList();

Again this was taken from a previous question.

So now that you have an Id/Key to work with you should now be able to carry out updates based on the Id/Key

When editing an item you can

var entity = item.ToEntity()
_db.Update(entity);

For the delete you can just convert the Id back to a Key

var key = item.Id.ToKey();
_db.Delete(key);

Taking it a step further, you can encapsulate the Datastore behind an abstraction that provides CRUD functionality and keeps all the Datastore related features in one central area

public class DatastoreSportsStore : ISportsStore {
    string kind = "Sports_db";
    private readonly DatastoreDb _db;

    public DatastoreSportsStore() {
        _db = DatastoreDb.Create("projectid");
    }

    public void Create(Item item) {
        var entity = item.ToEntity();
        entity.Key = _db.CreateKeyFactory(kind).CreateIncompleteKey();
        var keys = _db.Insert(new[] { entity });
        item.Id = keys.First().ToId();
    }

    public Item Read(string id) {
        return _db.Lookup(id.ToKey())?.ToItem();
    }

    public List<Item> ReadAll() {
        var query = new Query(kind);
        var results = _db.RunQuery(query);
        return results.Entities.Select(entity => entity.ToItem()).ToList();
    }

    public void Update(Item item) {
        _db.Update(item.ToEntity());
    }

    public void Delete(string id) {
        _db.Delete(id.ToKey());
    }
}

That way there is no need to interact with the Datastore in your page models.

You would register the abstraction with the service collection during start up

Reference Introduction to Razor Pages in ASP.NET Core

Startup.cs:

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
        //...

        services.AddScoped<ISportsStore, DatastoreSportsStore>();

        //...

        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    //...
}

And then inject that as the dependency where needed.

For example,

AllSportsStore.cshtml.cs

public class AllSportsStoreModel : PageModel {

    private readonly ISportsStore stores;

    public AllSportsStoreModel(ISportsStore stores) {
        this.stores = stores;
    }

    [BindProperty]
    public List<Item> SportsStoreList { get; set; }

    public IActionResult OnGet() {
        SportsStoreList = stores.ReadAll();
        return Page();
    }
}

In the above PageMode the abstracted store is injected and used to get the items to be displayed in the view

You should then be able to access the items in the list in the view/page.

AllSportsStore.cshtml

@page
@using TestApp.Models
@model AllSportsStoreModel

<h2>Index</h2>

<p>
    <a asp-page="SportsStore">New Item</a>
</p>
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayName("ID")
                </th>
                <th>
                    @Html.DisplayName("Name")
                </th>
                <th>
                    @Html.DisplayName("Price")
                </th>
                <th>Edit | Delete</th>
            </tr>
        </thead>
        <tbody>
@for (var i = 0; i < Model.SportsStoreList.Count; i++) {
    <tr>
        <td>
            @Html.DisplayFor(model => model.SportsStoreList[i].Id)
        </td>
        <td>
            @Html.DisplayFor(model => model.SportsStoreList[i].Name)
        </td>
        <td>
            @Html.DisplayFor(model => model.SportsStoreList[i].Price)
        </td>
        <td>
            <a asp-page="EditStore" asp-route-Id="@(Model.SportsStoreList[i].Id)">Edit</a> |
            <a asp-page-handler="Delete" asp-route-Id="@(Model.SportsStoreList[i].Id)">Delete</a>
        </td>
    </tr>
}
    </tbody>
</table>
<br />
like image 89
Nkosi Avatar answered Oct 21 '22 10:10

Nkosi


If '_' is your variable name of the entity, then to get the key for the entity you'll call the Entity.Key property on the entity.

i.e:

Id = _.Key,

There is no need for casting it like the other members in the provided example..

like image 38
Jim Morrison Avatar answered Oct 21 '22 08:10

Jim Morrison