I come from Relational Database background and we have a way to populate timestamp for row creation and update.
I am having difficulty finding similar feature for DynamoDB.
I checked DynamoDB to check if they support autopopulate the date timestamp for every entry in dynamoDB. I see it is possible to create random ID but that is not what I need.
My usecase is to add a timestamp entry automatically when I add any entry to DynamoDB Table. Appreciate any pointers. Thanks
Java Solution using Annotation (Data modelling package)
You can use the DynamoDBAutoGeneratedTimestamp annotation.
#DynamoDBAutoGeneratedTimestamp(strategy=DynamoDBAutoGenerateStrategy.CREATE)
public Date getCreatedDate() { return createdDate; }
public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; }
#DynamoDBAutoGeneratedTimestamp(strategy=DynamoDBAutoGenerateStrategy.ALWAYS)
public Date getLastUpdatedDate() { return lastUpdatedDate; }
public void setLastUpdatedDate(Date lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; }
DynamoDBAutoGeneratedTimestamp
There is no such functionality like
DEFAULT CURRENT TIMESTAMP or UPDATE CURRENT_TIMESTAMP.
You will have to set the date by yourself.
However if you want to keep track of changes on updates then you can use something like atomic counters.
Thus on every update your will increment the counter value.
What worked for me, using aws-sdk-version 2.17.169:
First, you need to define the extension to support
AutoGeneratedTimestampRecordExtension.
#Bean
public DynamoDbEnhancedClient dynamoDbEnhancedClient(){
return DynamoDbEnhancedClient.builder()
.dynamoDbClient(dynamoDbClient())
.extensions(AutoGeneratedTimestampRecordExtension.create())
.build();
}
Then, create a converter for java.time.Instant. As this is the only one supported at the time.
import java.time.Instant;
public class InstantToStringTypeConverter implements DynamoDBTypeConverter<String, Instant> {
#Override
public String convert(Instant instant) {
return instant.toString();
}
#Override
public Instant unconvert(String s) {
return Instant.parse(s);
}
Finally, on your model, add the annotations:
#DynamoDbAutoGeneratedTimestampAttribute
#DynamoDBTypeConverted(converter = InstantToStringTypeConverter.class)
public Instant getCreated() {
return created;
}
public void setCreated(Instant created) {
this.created = created;
}
#DynamoDbAutoGeneratedTimestampAttribute
#DynamoDBTypeConverted(converter = InstantToStringTypeConverter.class)
public Instant getUpdated() {
return updated;
}
public void setUpdated(Instant updated) {
this.updated = updated;
}
See some reference here
Related
I'm working on a new project that uses CosmosDB and Entity Framework Core (via the Microsoft.EntityFrameworkCore.Cosmos NuGet package, version 5.0.7; the project itself is .NET Core 5). I'm new to both, and running into an issue I can't sort out.
In short, I need to save a complex object to the database. It's a big model that will have multiple collections of classes underneath it, each with their own properties and some with collections underneath them as well. I'm trying to configure EF with OwnsOne and OwnsMany to store these child objects underneath the top-level one. The code compiles, and will save to the database so long as all the owned objects are left empty. But whenever I put anything into an owned object, either with OwnsOne or OwnsMany, I get a pair of NullReferenceExceptions.
I've tried to strip my code down to the very basics. Here's how it currently looks.
Owner and owned classes:
public class Questionnaire
{
// Constructors
private Questionnaire() { }
public Questionnaire(Guid id)
{
Test = "Test property.";
TV = new TestQ();
Id = id;
}
public Guid Id { get; set; }
public string Test { get; set; }
public TestQ TV { get; set; }
// Public Methods
public void AddForm(Form f)
{
// not currently using this method
//Forms.Add(f);
}
}
public class TestQ
{
public TestQ()
{
TestValue = "test ownsone value";
}
public string TestValue { get; set; }
}
DbContext:
public class QuestionnaireDbContext : DbContext
{
public DbSet<Questionnaire> Questionnaires { get; set; }
public QuestionnaireDbContext(DbContextOptions<QuestionnaireDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultContainer(nameof(Questionnaires));
modelBuilder.Entity<Questionnaire>().HasKey(q => q.Id);
modelBuilder.Entity<Questionnaire>().OwnsOne(q => q.TV);
}
}
And the code from the service that calls the dbContext (note that this is based on a generic service that I didn't set up originally). The actual exceptions are thrown here.
public virtual TEntity Add(TEntity entity)
{
_context.Entry(entity).State = EntityState.Added;
_context.SaveChanges();
return entity;
}
Ultimately I need this to work with OwnsMany and a collection, but I figured it might be simpler to get it working with OwnsOne first. The key thing to note here is that if I comment out the line
TV = new TestQ();
in the Questionnaire class, the model persists correctly into CosmosDB. It's only when I actually instantiate an owned entity that I get the NullReferenceExceptions.
Any advice would be much appreciated! Thank you!
Well, I'm not sure why this is the case, but the issue turned out to be with how we were adding the document. Using this generic code:
public virtual async Task<TEntity> Add(TEntity entity)
{
_context.Entry(entity).State = EntityState.Added;
await _context.SaveChanges();
return entity;
}
was the issue. It works just fine if I use the actual QuestionnaireDbContext class like so:
context.Add(questionnaire);
await context.SaveChangesAsync();
Where and when should data be inserted into a database in the arrange phase in xunit framework?
By the arrange phase I mean the Arrange from the Arrange, Act, Assert pattern.
To prepare data for each test method I am using a separate class. E.g.:
class CasesRelationTypeTest
{
[Theory]
[MemberData(nameof(CasesRelationTypeTestData.DeleteAsyncTest), MemberType = typeof(CasesRelationTypeTestData))]
public void DeleteAsyncTest(CasesRelationTypeDto data)
{
//...
}
//...
}
class CasesRelationTypeTestData
{
public TheoryData<CasesRelationTypeDto> DeleteAsyncTest { get; private set; }
public CasesRelationTypeTestData()
{
InitDeleteAsync();
}
private void InitDeleteAsync()
{
//should I insert the data here? but then it will be inserted for all the tests in the CasesRelationTypeTest, which is not what I want
DeleteAsyncTest.Add(new CasesRelationTypeDto
{
//...
});
}
}
I am okay with changing the way in which data is provided to a theory if you propose something different (which would allow for a well devised arrange phase).
I am using DynamoDB SDK 1.11.185 version. I have a Client entity mapped to Clients DynamoDB table through DynamoDBMapper annotations. One of the attributes contains nested values (see code examples below). I want to add a local secondary index to zone attribute from Options class. Once I want to save an object, I got a null pointer exception from this class
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel
line 415. Looks like a bug from the libraries.
P.D. If I generate the index over clientId attribute from Client entity, everything work fine.
#DynamoDBTable(tableName="Clients")
public class Client {
private String id;
private String clientId;
private Date created;
private Options options;
public Client() {
}
public Client(String id, String clientId, Options options) {
this.id = id;
this.clientId = clientId;
this.options = options;
this.created = new Date();
}
#DynamoDBHashKey
public String getId() { return id; }
public void setId(String id) { this.id = id; }
#DynamoDBAttribute
public String getClientId() { return clientId; }
public void setClientId(String clientId) { this.clientId = clientId; }
#DynamoDBAttribute
public Options getOptions() { return options; }
public void setOptions(Options options) { this.options = options; }
#DynamoDBRangeKey
public Date getCreated() { return created; }
public void setCreated(Date created) { this.created = created; }
}
#DynamoDBDocument
public class Options {
private String zone;
public Options() {
}
public Options(String zone) {
this.zone = zone;
}
#DynamoDBIndexRangeKey(localSecondaryIndexName = "zone-index")
public String getZone() { return zone; }
public void setZone(String zone) { this.zone = zone; }
}
**************************** EDITED *************************
Correct answer by #Raniz and
Indexing on nested field
It can be done using JSON attributes though:
DynamoDB create index on map or list type
You can't use nested attributes in the key schema for an index.
I assume you've created the index with options.zone in the schema which means that DynamoDB is expecting a top-level attribute with that exact name - i.e. an attribute named options.zone and not an attribute named zone nested under the options attribute.
Excerpt from here:
The key schema for the index. Every attribute in the index key schema
must be a top-level attribute of type String, Number, or Binary. Other
data types, including documents and sets, are not allowed. Other requirements for the key schema depend on the type of index:
For a global secondary index, the partition key can be any scalar attribute of the base table. A sort key is optional, and it too can be
any scalar attribute of the base table.
For a local secondary index, the partition key must be the same as the base table's partition key, and the sort key must be a non-key
base table attribute.
To use zone in your index schema you'll need to either move or duplicate it so it's available on the top level. The easiest way of accomplishing this would probably be to add a getter to Client that returns options.zone:
#DynamoDBIndexRangeKey(localSecondaryIndexName = "zone-index")
public String getZone() {
if (options != null) {
return options.getZone();
}
return null;
}
I build a simple custom native activity that return a string value.
public sealed class MyActivity : NativeActivity<string>
{
public InArgument<string> Id { get; set; }
protected override void Execute(NativeActivityContext context)
{
var returnString = QuerySomthing();
context.SetValue<string>(base.Result, returnString);
}
}
How can I get this value in the workflow's variables?
You can access the 'Result' property of your activity. All you need to do is create a variable on the workflow (of type String) and bind this to the 'Result' property. Then you can access the variable later on in the workflow to analyse its value. HTH
I am going though the Apress ASP.NET MVC 3 book and trying to ensure I create Unit Tests for everything possible but after spending a good part of a day trying to work out why edit's wouldn't save (see this SO question) I wanted to create a unit test for this.
I have worked out that I need to create a unit test for the following class:
public class EFProductRepository : IProductRepository {
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products {
get { return context.Products; }
}
public void SaveProduct(Product product) {
if (product.ProductID == 0) {
context.Products.Add(product);
}
context.SaveChanges();
}
public void DeleteProduct(Product product) {
context.Products.Remove(product);
context.SaveChanges();
}
}
public class EFDbContext : DbContext {
public DbSet<Product> Products { get; set; }
}
I am using Ninject.MVC3 and Moq and have created several unit tests before (while working though the previously mentioned book) so am slowly getting my head around it. I have already (hopefully correctly) created a constructor method to enable me to pass in _context:
public class EFProductRepository : IProductRepository {
private EFDbContext _context;
// constructor
public EFProductRepository(EFDbContext context) {
_context = context;
}
public IQueryable<Product> Products {
get { return _context.Products; }
}
public void SaveProduct(Product product) {
if (product.ProductID == 0) {
_context.Products.Add(product);
} else {
_context.Entry(product).State = EntityState.Modified;
}
_context.SaveChanges();
}
public void DeleteProduct(Product product) {
_context.Products.Remove(product);
_context.SaveChanges();
}
}
BUT this is where I start to have trouble... I believe I need to create an Interface for EFDbContext (see below) so I can replace it with a mock repo for the tests BUT it is built on the class DbContext:
public class EFDbContext : DbContext {
public DbSet<Product> Products { get; set; }
}
from System.Data.Entity and I can't for the life of me work out how to create an interface for it... If I create the following interface I get errors due to lack of the method .SaveChanges() which is from the DbContext class and I can't build the interface using "DbContext" like the `EFDbContext is as it's a class not an interface...
using System;
using System.Data.Entity;
using SportsStore.Domain.Entities;
namespace SportsStore.Domain.Concrete {
interface IEFDbContext {
DbSet<Product> Products { get; set; }
}
}
The original Source can be got from the "Source Code/Downloads" on this page encase I have missed something in the above code fragments (or just ask and I will add it).
I have hit the limit of what I understand and no mater what I search for or read I can't seem to work out how I get past this. Please help!
The problem here is that you have not abstracted enough. The point of abstractions/interfaces is to define a contract that exposes behavior in a technology-agnostic way.
In other words, it is a good first step that you created an interface for the EFDbContext, but that interface is still tied to the concrete implementation - DbSet (DbSet).
The quick fix for this is to expose this property as IDbSet instead of DbSet. Ideally you expose something even more abstract like IQueryable (though this doesn't give you the Add() methods, etc.). The more abstract, the easier it is to mock.
Then, you're left with fulfilling the rest of the "contract" that you rely on - namely the SaveChanges() method.
Your updated code would look like this:
public class EFProductRepository : IProductRepository {
private IEFDbContext context;
public EFProductRepository(IEFDbContext context) {
this.context = context;
}
...
}
public interface IEFDbContext {
IDbSet<Product> Products { get; set; }
void SaveChanges();
}
BUT... the main question you have to ask is: what are you trying to test (conversely, what are you trying to mock out/avoid testing)? In other words: are you trying to validate how your application works when something is saved, or are you testing the actual saving.
If you're just testing how your application works and don't care about actually saving to the database, I'd consider mocking at a higher level - the IProductRepository. Then you're not hitting the database at all.
If you want to make sure that your objects actually get persisted to the database, then you should be hitting the DbContext and don't want to mock that part after all.
Personally, I consider both of those scenarios to be different - and equally important - and I write separate tests for each of them: one to test that my application does what it's supposed to do, and another to test that the database interaction works.
I guess your current code looks something like this (I put in the interface):
public class EFProductRepository : IProductRepository {
private IEFDbContext _context;
// constructor
public EFProductRepository(IEFDbContext context) {
_context = context;
}
public IQueryable<Product> Products {
get { return _context.Products; }
}
public void SaveProduct(Product product) {
if (product.ProductID == 0) {
_context.Products.Add(product);
} else {
_context.Entry(product).State = EntityState.Modified;
}
**_context.SaveChanges();**
}
public void DeleteProduct(Product product) {
_context.Products.Remove(product);
**_context.SaveChanges();**
}
}
public class EFDbContext : DbContext, IEFDbContext {
public DbSet<Product> Products { get; set; }
}
public interface IEFDbContext {
DbSet<Product> Products { get; set; }
}
The problem is EFProductRepository now expects an object implementing the IEFDbContext interface, but this interface does not define the SaveChanges method used at the lines I put between the asteriskes so the compiler starts complaining.
Defining the SaveChanges method on the IEFDbContext interface solves your problem:
public interface IEFDbContext {
DbSet<Product> Products { get; set; }
void SaveChanges();
}