Use IAmazonDynamoDB or IDynamoDBContext (both?) - c#

I started my Visual Studio project from AWS SDK template. It uses IDynamoDBContext in the function and IAmazonDynamoDB in the tests.
Everything worked to save and received documents when I received them with an id (hash). But it stopped to work when I added a range to my table. All my test were against AWS dynamoDb. But I got it to work in 2 ways. The first way were when I downloaded the local instance of dynamoDb. The second were when I replaced IDynamoDBContext to IAmazonDynamoDB in my function (so it used the same interface in both the function and in my test class). I don't know what the correct solution is, but why use 2 interfaces in the first place? Should I keep digging in why it didn't work with different interfaces or should I only use one of them?
// IDynamoDBContext (default) - Didn't save my item (did save it in local DynamoDB)
var test = new Test
{
UserId = "Test",
Id = 1
};
await DDBContext.SaveAsync<Test>(test);
// IAmazonDynamoDB - Did save my item
var putItemRequest = new PutItemRequest
{
TableName = "TestTable",
Item = new Dictionary<string, AttributeValue>()
{
{ "UserId", new AttributeValue { S = "Test" }},
{ "Id", new AttributeValue { N = "1" }}
}
};
await DDBContext.PutItemAsync(putItemRequest);
My test:
var item = new GetItemRequest
{
TableName = "TestTable",
Key = new Dictionary<string, AttributeValue>
{
{ "UserId", new AttributeValue { S = "Test" } },
{ "Id", new AttributeValue { N = "1" } },
},
};
Assert.True((await this.DDBClient.GetItemAsync(item)).Item.Count > 0);

We probably need someone on the AWS .Net SDK team to speak to this but here is my best insight.
Amazon documentation is always fun.
The documentation does not make it overly clear but IDynamoDBContext is found in the Amazon.DynamoDbv2.DataModel namespace which is used for object persistence data access.
So I think the IAmazonDynamoDB interface is used for general API calls against the DynamoDB service. The two modes have overlapping functionality in that both can work with given items in a dynamoDb table.
The docs, of course, are really clear in that for IDynamoDbContext it says
Context interface for using the DataModel mode of DynamoDB. Used to
interact with the service, save/load objects, etc.
https://docs.aws.amazon.com/sdkfornet/v3/apidocs/Index.html
For IAmazonDynamoDB it says
Interface for accessing DynamoDB Amazon DynamoDB
IAmazonDynamoDB is from the Amazon.DynamoDbv2 namespace and IDynamoDBContext is found in the Amazon.DynamoDbv2.DataModel.
If you look at the documentation for them both though you will see by looking at the methods the actions each can performance are very different.
IAmazonDynamoDb allows you to interact and work more with DynamoDb via:
Creating tables
Deleting tables
Creating global indexes and backups
etc.
You can still work directly with items but the number of API calls
available via this interface is larger and allows working with
the overall DynamoDB service.
While IDynamoDBContext allows you work directly with items in a given DynamoDb table with methods like:
Save
Query
Scan
Load
Consistency is always key in programming so always use the same interface for areas that are meant to do the same level of work. So your code and tests should be using the same interface as they are focused on the same work scope. Hopefully based on that additional clarification you know what interface you are after. If all your code is trying to do is work with items in a DynamoDb table then IDynamoDBContext is probably what you are after.

Related

Get Azure DevOps comment details including comment id in .NET C#

I am trying to get list of comments of a specific Azure DevOps workitem through C# Azure DevOps SDK.
Nuget packages I have used:
Microsoft.TeamFoundationServer.Client 16.170.0
Microsoft.VisualStudio.Services.Client 16.170.0
Here is the C# code to get WorkItem comments:
// create instance of work item tracking http client
var credentials = new VssBasicCredential(string.Empty, PAT);
using (var httpClient = new WorkItemTrackingHttpClient(new Uri(COLLECTION_URI), credentials))
{
// get all comments for this work item
var comments = await httpClient.GetCommentsAsync(workItemId);
}
The "httpClient.GetCommentsAsync" method works fine and returns object of "WorkItemComments" class. The "WorkItemComments" object contains a property named "Comments" of data type "IEnumerable of WorkItemComment". If I further go inside "WorkItemComment" class then I can see only four properties inside it.
Revision
Text
RevisedBy
RevisedDate
So, in short, I am not able to get other details of comment like comment id, created by, created date, modifed by, modified date, etc.
How can I get other details of comments ? Do I need to call any other method ?

Would calling a mediator be correct in my Handler when crossing services/modules in a DDD monolith

Preface about the architecture
In a microservice, this could be a service according to clean-architecture:
BasketService
Api
Application [CQRS]
Core
Infrastructure
CatalogService
Api
Application [CQRS]
Core
Infrastructure
But since i'm applying DDD to a monolith, i can currently remove my Api layer and have less "projects"/module. So every module ( eg. basket and catalog) contains 3 projects currently:
Application
Core
Infrastructure
Both have a dependency on .Core
Problem description
My application has a BasketModule and a CatalogModule.
Would it be correct to get information from the CatalogModule ( which contains the products) with Mediator like this:
public Task<BasketDTO> Handle(GetBasketByBuyerIdCommand request, CancellationToken cancellationToken)
{
BasketDTO result;
var basket = m_basketRepo.GetById(request.BuyerId);
if (basket == null)
{
result = new BasketDTO()
{
BuyerId = request.BuyerId,
Items = new List<BasketItem>()
};
return Task.FromResult(result);
}
//Could be automapper, but not now currently //ignore
var products = new List<DTO.Product>();
foreach (var item in basket.Items)
{
var product = m_mediator.Send(new GetProductByIdQuery(item.ProductId)).Result; //ignore the non-async. It's example code
products.Add(new DTO.Product()
{
Id = product.Id,
Price = product.Price,
Title = product.Title
});
}
result = new BasketDTO()
{
BuyerId = basket.BuyerId,
Items = basket.Items
.Select(dl => new DTO.BasketItem()
{
ProductId = dl.ProductId,
Quantity = dl.Quantity,
Product = products
.Where(cl => cl.Id == dl.ProductId)
.FirstOrDefault()
}).ToList()
};
return Task.FromResult(result);
}
}
It is about the following line:
var product = await m_mediator.Send(new GetProductByIdQuery(item.ProductId));
Is this correct to do? I'm not talking about the rest of the example code, but specifically about calling the mediator to get the product and having a dependency of the project "Basket.Application" on "Catalog.Application"
To be honest, only you know the answer to your question. By the looks of it and despite the naming "GetBasketByBuyerIdCommand" it seems you're only running queries in your code. So that means you're only working on the "Q" of CQRS and on that perspective what you are doing is fine.
In my opinion, the answer to your question lies in the rest of your architecture and especially in what you're trying to achieve with it at this point. If you want your BasketModule and CatalogModule to be totally independent somewhere down the road, this type of choice taken now might have a great deal of impact in the future.
For example, if you, later on, want the two modules to be separated into two different microservices, you will have to come up with a different way of doing the query you described. How to handle this is a whole new conversation in its own respect.
The mean role of a mediator is to decouple the presentation layer from the application services and do some default treatments using middlewares, rather than communicating between bounded contexts.
Thus I would prefer to have a mediator per bounded context :)
To integrate the two BCs you can add an interface of GetProductByIdQuery to the Basket domain (or application) layer and have its implementation in Basket/infra.
The implementation of GetProductByIdQuery (considered as a secondary adapter) may call the mediator of Catalog's BC, and transform the result to preserve the ubiquitous language.
IMHO, This will keep the monolith clean and reduce the migration cost if you decide to split some parts of it i.e only the secondary adapter implementation will change to do some inter-process communication instead.
PS: I'm not really familiar with clean architecture, but I consider it a more featured version of the Hexagonal one..

How to query to an existing database online with .net Graphql NuGet? (c#)

My goal: Create a Windows form app that queries via GraphQl to an existing online database.
What I've done:
To simulate a graphQl server I've followed the official graphql-node Tutorial till the end. So now I'm able to query from the GraphQl playground and get a response. This server runs on localhost:4000.
I've created a windows form app in Visual Studio that queries in graphQl to an internal database containing mocked data. E.g.
My Form.cs class contains this code, where I execute the query onButtonClick
private void button1_Click(object sender, EventArgs e)
{
var schema = Schema.For(#"
type Jedi {
name: String,
side: String,
id: ID
}
type Query {
hello: String,
jedis: [Jedi],
jedi(id: ID): Jedi
}", _ => { _.Types.Include<Query>(); }
);
var json = schema.Execute(_ =>
{
_.Query = "{jedis { name, side } }";
});
System.Diagnostics.Debug.WriteLine(json);
label1.Text = json;
}
After I execute the query my Query.cs class will recognize it and resolve the query
class Query
{
[GraphQLMetadata("jedis")]
public IEnumerable<Jedi> GetJedis()
{
return StarWarsDB.GetJedis();
}
}
This is my Current internal DB
class StarWarsDB
{
public static IEnumerable<Jedi> GetJedis()
{
return new List<Jedi>() {
new Jedi(){ Id = 1, Name ="Luke", Side="Light"},
new Jedi(){ Id = 2, Name ="Yoda", Side="Light"},
new Jedi(){ Id = 3, Name ="Darth Vader", Side="Dark"}
};
}
}
The next step i want to do:
Now, I've found a lot of examples online on how to query a db but they were always made internally in visual Studio. What I wanted to do is connecting to my personal DB running on my localHost for now (the one i made following the graphql-node Tutorial) and query it with graphQl. Then take the JSON response and print it somewhere.
You are mixing it up a little bit. For starters you don't need to have a schema declaration on your client.
If I understood you correctly you are using webforms, and want to acess a api service on the backend which will return you data.
So in that case you need a graphql "Client" in your webforms project, with which you "Connect" to a endpoint like your prisma api, and afterwards you only send querys or mutations to the api as a request.
Here is the link for the client, you install it as a nuget, and follow the documentation.
https://github.com/graphql-dotnet/graphql-client
So in most simple terms explained, graphqlClient sends a query to => endpoint(in this case your prisma api) which gets data => from your database.
Client(query) => Api(select * datasource) => DB(SQL) => Api(returns data) => Client(deserialise) => bind to UI => see the output.
GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn't tied to any specific database or storage engine and is instead backed by your existing code and data. - graphql.org
Hi Zayed
Maybe I misunderstand your explanation but you cannot query directly to the database using GraphQL. You'll need an API project between your connection and the GraphQL logic. You'll post your GraphQL scheme to the API endpoint and it will return, based on your endpoint, the correct data structure.
I found a good website where it will be explained step by step on how to implement it in ASP.NET CORE 2.
Hope this helps.Good luck!

Add to odata collection property in .NET

Using the odata-v4 service connection in visual studio C# .NET, I get an entity of type testDefinition. testDefinition has property called features which is a collection of entities of type feature.
In the DB, testDefinition to feature is many to many with a junction table.
In my code, I add a service reference to a web service serving the EDMX of the DB.
Code gets generated correctly and I run:
var dsc = new Container(new Uri("http://webserver/webapi/odata/"));
var someFeature = new Feature
{
name = $"Sample feature created with C# {DateTime.UtcNow}",
};
var someOtherFeature = new Feature
{
name = $"Sample other feature created with C# {DateTime.UtcNow}",
};
dsc.AddToFeature(someFeature);
dsc.AddToFeature(someOtherFeature);
dsc.SaveChanges();
var someTestDefinition = new TestDefinition
{
name = $"Sample test created with C# {DateTime.UtcNow}",
description = $"A nice succinct description",
};
dsc.AddToTestDefinition(someTestDefinition);
dsc.SaveChanges();
someTestDefinition.features.Add(someFeature);
someTestDefinition.features.Add(someOtherFeature);
dsc.SaveChanges();
The problem is the mapping from the test definition to features is not recorded in the database.
Has anyone encountered this issue, or better yet, resolved it?
For Reference:
I can't say when this was implemented but it works in later versions, the earliest verison I can confirm this against is OData.Client v 7.6.2
If this is an issue for any new users on current versions of the OData Client and Server libraries it generally indicates that the schema is not correctly configured or published.
Also check that the API uses a similarly current OData runtime, the above test was performed against API with Microsoft.AspNet.OData v7.3.0

Create if not exists methods not available

I have a .Net core app that I have installed the cosmos nuget package into:
Install-Package Microsoft.Azure.DocumentDB.Client
I am attempting to follow the instructions given here from MS that details how to create a database and the collections.
Using dependency injection, I have registered IDocumentClient. However, when I attempt to use this in a setup class to create the database and collections, the following methods are not listed as avaialable:
public void Setup(IDocumentClient client)
{
await client.CreateDatabaseIfNotExistsAsync(new Database() { Id = databaseId });
await client.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri(databaseId), new DocumentCollection { Id = "Identity" });
}
In both cases, the methods flag up as not known, and the IDE prompts me to create them. This happens even with the Microsoft.Azure.Documents.Client namespace defined in the file.
Whilst these methods exist on the DocumentClient class, they are not actually part of the IDocumentClient interface.
In this manner, you will need to pass the concrete DocumentClient class into the setup method, rather than the IDocumentClient interface.
public void Setup(DocumentClient client)
{
await client.CreateDatabaseIfNotExistsAsync(new Database() { Id = databaseId });
await client.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri(databaseId), new DocumentCollection { Id = "Identity" });
}
Once you do this, the methods are available as normal.
Whilst Dependency Injection/IoC generally prefers the use of Interfaces over concrete classes, it is probably ok in this instance, since the class purpose is specifically to set up and configure the database: a certain knowledge of the underlying technology here is to be expected.

Categories

Resources