DynamoDB .NET persistance model: retrieve only specific attributes - c#

I have a DynamoDB table with GSI and I need to use AWS .NET SDK to retrieve two specific attributes, preferably using the persistence model. I've created a separate class for this projection like this
[DynamoDBTable("MyTable")]
public class MyItem
{
public long Number{ get; set; }
public string Name{ get; set; }
}
but even when I specify SelectValues.SpecificAttributes to get only these attributes, it seems that DynamoDB client tries to retrieve primary key attributes (which I would like to avoid) and gives me an exception Unable to locate property for key attribute <PK hash key attribute name>. Here is the code I use to query
var keyExpression = new Expression
{
ExpressionStatement = $"GSI_PK = :pkValue",
ExpressionAttributeValues =
{
[":pkValue"] = 1
}
};
db.FromQueryAsync<MyItem>(new QueryOperationConfig
{
IndexName = "MyIndex",
KeyExpression = keyExpression,
Select = SelectValues.SpecificAttributes,
AttributesToGet = new List<string> { "Number", "Name" }
}
Is there a way to stop DynamoDB client from mapping non-requested items?

Related

How to add a List<> data to a List<> variable using EF Core C#

I am creating a CRUD Web API and I am just following this Microsoft tutorial. In my case, I have two models:
First_Model.cs:
// i removed other unnecessary data
public string Id { get; set; }
public IList<Second_Model> Second_Models {get; set;} = new List<Second_Model>();
Second_Model.cs:
// i removed other unnecessary data
public string Id { get; set; }
Just like in the tutorial, it will statically add a default value of the collection, so I want to add List<Second_Model> into Second_Models
Here's what I tried in my First_ModelController.cs
_context.First_Models.Add(
new First_Model {
Id = "default_id",
Second_Models = new List<Second_Model>() {
new List<Second_Model>().Find(p => p.Id == "default_id")
// given that Second_Model also has default_id
};
}
);
However, this set of codes will return this error:
ArgumentNullException: Value cannot be null. Parameter name: key
Statically creating a list of objects would look more like this:
....
Id = "default_id",
Second_Models = new List<Second_Model>() {
new Second_Model() { Id = "<your second model ID 1>", OtherProperty = <value> },
new Second_Model() { Id = "<your second model ID 2>", OtherProperty = <value> },
}
However, if you are want to add them directly to the context, you would do something similar to:
_context.Second_Models.Add(
new Second_Model() {
Id = "<your second model ID 1>",
OtherProperty = <value>,
}
);
... repeat ...
That does not associate the second_model with the first_model though.
You might need to read more about defining foreign keys on EF models so make this all join together, because I don't think the tutorial includes anything related to that.

MongoDB Indexes.CreateOneAsync Exception using Azure DocumentDB

I'm trying to create multiple unique indexes using c# MongoDB driver connecting to Azure DocumentDB instance, but I'm receiving the following exception when trying to create the second unique index:
MongoDB.Driver.MongoCommandException: 'Command createIndexes failed: Message: {"Errors":["The number of unique keys cannot be greater than 1."]}
I can't seem to find any documentation regarding the number of unique keys for Azure DocumentDB collection. Note that this exception does not occur when using actual MongoDB instance.
var keys = Builders<ProductEntity>.IndexKeys.Ascending(p => p.UPC);
var options = new CreateIndexOptions<ProductEntity>() { Name = "UX_UPC", Unique = true, Sparse = true };
var result = await _collection.Indexes.CreateOneAsync(keys, options);
keys = Builders<ProductEntity>.IndexKeys.Ascending(p => p.Manufacturer).Ascending(p => p.MPN);
options = new CreateIndexOptions<ProductEntity>() { Name = "UX_Manufacturer_MPN", Unique = true, Sparse = true };
result = await _collection.Indexes.CreateOneAsync(keys, options);
public class ProductEntity
{
public Guid Id { get; set; }
public string UPC { get; set; }
public string MPN { get; set; }
public string Manufacturer { get; set; }
}
From this blog, we could find it does not support for Unique indexes currently.
General availability is just the beginning for all the features and improvements we have in stored for DocumentDB: API for MongoDB. In the near future, we will be releasing support for Unique indexes and a couple major performance improvements.
This thread discussed Cannot create index in Azure DocumentDb with Mongodb protocol, you could refer to it.

Elastic search with GUID ID without attributes

We are looking to switch from a relational database to elastic search and I am trying to get some basic code up and running with Nest. We have existing objects which use guids for ids that I would like to save into an elastic search index.
I don't want to add any specific attributes as the class is used in different applications and I don't want to add unnecessary dependencies to Nest.
Right now my code looks like this:
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node)
settings.DefaultIndex = "test";
var client = new ElasticClient(settings);
var testItem = new TestType { Id = Guid.NewGuid(), Name = "Test", Value = "10" };
var response = client.Index(testItem);
With TestType as:
public class TestType
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Value { get; set; }
}
However I get an error like:
ServerError: 400Type: mapper_parsing_exception Reason: "failed to
parse [id]" CausedBy: "Type: number_format_exception Reason: "For
input string: "c9c0ed42-86cd-4a94-bc86-a6112f4c9188""
I think I need to specify a mapping that tells the server the Id is a string, but I can't find any examples or documentation on how I do this without using the attributes.
Assuming you're using Elasticsearch 2.x and NEST 2.x (e.g. latest of both at time of writing is Elasticsearch 2.3.5 and NEST 2.4.3), then NEST will automatically infer the id of a POCO by default from the Id property. In the case of a GUID id, this will be saved as a string in Elasticsearch.
Here's an example to get you going
void Main()
{
var node = new Uri("http://localhost:9200");
var settings = new ConnectionSettings(node)
// default index to use if one is not specified on the request
// or is not set up to be inferred from the POCO type
.DefaultIndex("tests");
var client = new ElasticClient(settings);
// create the index, and explicitly provide a mapping for TestType
client.CreateIndex("tests", c => c
.Mappings(m => m
.Map<TestType>(t => t
.AutoMap()
.Properties(p => p
// don't analyze ids when indexing,
// so they are indexed verbatim
.String(s => s
.Name(n => n.Id)
.NotAnalyzed()
)
)
)
)
);
var testItem = new TestType { Id = Guid.NewGuid(), Name = "Test", Value = "10" };
// now index our TestType instance
var response = client.Index(testItem);
}
public class TestType
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Value { get; set; }
}
Take a look at the Automapping documentation for more examples of how to explicitly map a POCO for controlling norms, analyzers, multi_fields, etc.
What I normally do is to have a separate class that is only specific to Elasticsearch. And use Automapper to map that into a DTO or ViewModel, or Model into the Elasticsearch Document.
That way, you won't have to expose an object that have a dependency in NEST and attributes that might be specific only to Elasticsearch.
Another good reason is that normally, documents in ES are flat, so you would normally flatten your objects before you index them to ES.

How to implement typesafe enum pattern in entity framework core rc2

How can I implement the typesafe enum pattern in entity framework core rc2?
public class TestStatus
{
[Column("Id")]
public int Id { get; private set; }
[Column("Description")]
public string Description { get; private set; }
[Column("LongDescription")]
public string LongDescription { get; private set; }
private TestStatus(int id
, string description
, string longDescription)
{
Id = id;
Description = description;
LongDescription = longDescription;
}
public TestStatus() { }
public static readonly TestStatus Active = new TestStatus(1, "Active", "Active Long Description");
public static readonly TestStatus Pending = new TestStatus(2, "Pending", "Pending Long Description");
public static readonly TestStatus Cancelled = new TestStatus(3, "Cancelled", "Cancelled Long Description");
}
The id generation strategy is set in OnModelCreating:
builder.Entity<TestStatus>()
.Property(s => s.Id)
.ValueGeneratedNever();
This is a simplified example, but the real code was working in rc1. When upgrading to rc2, I had to add the Column attributes so that the properties would be mapped (I assume this is because of the private setter). When attempting to assign the typesafe enum value:
var i = new TestItem
{
Name = "Test Item 2",
Status = TestStatus.Active
};
_context.Items.Add(i);
_context.SaveChanges();
I get one of the following errors depending on the use case:
InvalidOperationException: The instance of entity type 'TestStatus' cannot be tracked because another instance of this type with the same key is already being tracked. For new entities consider using an IIdentityGenerator to generate unique key values.
Or
SqlException: Violation of PRIMARY KEY constraint 'PK_Statuses'. Cannot insert duplicate key in object 'dbo.Statuses'. The duplicate key value is (1). The statement has been terminated.
I understand the error. EF thinks that I am trying to create a new instance with the same Id. How can I tell EF that these instances should be considered the same? I can workaround this by moving away from the typesafe enum pattern. I would just like to make it work with the pattern if possible. It was working in rc1.
Since you are using the Type-Safe Pattern there is no need to persist the whole object. Simply store the id and create a wrapper like so:
[Required]
protected string ObjectTypeValue { get; set; }
[NotMapped]
public ObjectType Type
{
get { return ObjectType.Parse(ObjectTypeValue); }
set { ObjectTypeValue = value.Print(); }
}
For some reasons I use a string as an Id, but you can use any type you like.

Entity Framework & Multitables

I have a linq to entity expression:
entities = new zdmEntities();
var reltables = (from r in entities.relations
orderby r.id
select new Relation
{
Id = r.id,
Devices = r.devices.device_name,
Systems = r.systems.system_name,
Models = r.models.name,
Functions = r.functions.function_name
}).ToList();
ultraGrid1.DataSource = reltables.ToList();
class Relation
{
public int Id { get; set; }
public string Devices { get; set; }
public string Systems { get; set; }
public string Models { get; set; }
public string Functions { get; set; }
}
As you can see the relation table contains a link to other tables.
The class Relation contains my columns for the datagrid.
But there is one problem... can't be posssible two way databinding between grid and database. I wrote all the updates manually but it's very difficult.
I understand that this is because in linq expression there is 'new'. But how do you make it without 'new'?
How I can display columns that I need with a two-way databinding and without own class like 'Relation'.
Windows Form. Not wpf)
Thanx, Alex.
When you write entities.Relations.Select(r => new ...) you are making a projection of each Relation EF object into a new non-EF object. By EF object I mean a class which is known by and tracked by EntityFramework.
Making changes to a EF-known class instance would propagate the changes back to DB when you save changes in your db/entity context. In contrast, making changes to a EF-unknown projection (or any projection) has no effect on the original object.
There are two ways you can achive what you want: If your DataGrid (NetAdvantage UltraGrid?) supports binding to subobjects (such as relation.device) you can then use ultraGrid.DataSource = entities.relations and define grid columns to bind to field devices.device_name. The other way would be something like this:
class Relation
{
private readonly EfRelation _originalRelation;
public Relation(EfRelation originalRelation)
{
this._originalRelation = originalRelation;
}
public string Devices
{
get { return this._originalRelation.devices.device_name; }
set { this._originalRelation.devices.device_name = value; }
}
// Repeat for other properties
}
...
var reltables = entities.relations.ToList().Select(r => new Relation(r)).ToList();
Then you just save changes to your db/object context. The EfRelation is the name of your EF Relation class, change it to the name of your EF class which represents a relation.

Categories

Resources