AWS sdk for .net queryAsync method using global secondary index fails - c#-4.0

given below is the method I used to retrieve details from a Dynamodb table. But when I call this method it ended up throwing an exception "Unable to locate property for key attribute appointmentId". primary key of this particular table is appointmentId, but I've already created a global secondary index on patientId column. I'm using that index in below query to get the appointment details by a given patientID.
public async Task GetAppointmentByPatientID(int patientID)
{
var context = CommonUtils.Instance.DynamoDBContext;
PatientAppointmentObjectList.Clear();
DynamoDBOperationConfig config = new DynamoDBOperationConfig();
config.IndexName = DBConstants.APPOINTMENT_PATIENTID_GSI;
AsyncSearch<ScheduledAppointment> appQuery = context.QueryAsync<ScheduledAppointment>(patientID.ToString(), config);
IEnumerable<ScheduledAppointment> appList = await appQuery.GetRemainingAsync();
appList.Distinct().ToList().ForEach(i => PatientAppointmentObjectList.Add(i));
if (PropertyChanged != null)
this.OnPropertyChanged("PatientAppointmentObjectList");
}
}

It was a silly mistake. I've had the hash key column of the table as "appointmentID" and the model object property named as AppointmentID. mismatch in the case of the property name had confused the dynamodb mapping.

Related

ServiceStack: Update<T>(...) produces 'duplicate entry'

I have tried reading the docs, but I don't get why the Update method produces a "Duplicate entry" MySQL error.
The docs says
In its most simple form, updating any model without any filters will update every field, except the Id which is used to filter the update to this specific record:
So I try it, and pass in an object, like below. A row with id 2 already exists.
using (var _db = _dbFactory.Open())
{
Customer coreObject = new Customer(...);
coreObject.Id = 2;
coreObject.ObjectName = "a changed value";
_db.Update<Customer>(coreObject); // <-- error "duplicate entry"
}
Yes, there are options using .Save and such, but what am I missing with the .Update? As I read it, it should use its Id property to update the row in the db, not insert a new row?
The issue with this method is that you're updating a generic object T but your Update API says to update the Concrete Customer type:
public void MyTestMethod<T>(T coreObject) where T : CoreObject
{
long id = 0;
using (var _db = _dbFactory.Open())
{
id = _db.Insert<T>(coreObject, selectIdentity: true);
if (DateTime.Now.Ticks == 0)
{
coreObject.Id = (uint)id;
_db.Delete(coreObject);
}
if (DateTime.Now.Ticks == 0)
{
_db.DeleteById<Customer>(id);
}
if (DateTime.Now.Ticks == 0)
{
coreObject.Id = (uint)id;
coreObject.ObjectName = "a changed value";
_db.Update<Customer>(coreObject);
}
}
}
Which OrmLite assumes that you're using a different/anonymous object to update the customer table, similar to:
db.Update<Customer>(new { Id = id, ObjectName = "a changed value", ... });
Which as it doesn't have a WHERE filter will attempt to update all rows with the same primary key.
What you instead want is to update the same entity, either passing in the Generic Type T or have it inferred by not passing in any type, e.g:
_db.Update<T>(coreObject);
_db.Update(coreObject);
Which will use OrmLite's behavior of updating entity by updating each field except for Primary Keys which it instead used in the WHERE expression to limit the update to only update that entity.
New Behavior in v5.1.1
To prevent accidental misuse like this I've added an Update API overload in this commit which will use the Primary Key as a filter when using an anonymous object to update an entity, so your previous usage:
_db.Update<Customer>(coreObject);
Will add the Primary Key to the WHERE filter instead of including it in the SET list. This change is available from v5.1.1 that's now available on MyGet.

java.lang.NullPointerException when using returning().fetchOne()

Note: This code used to work as I used PostgreSQL. After switching to MySQL these functions stopped working for some reason.
I double checked: languageId and shopId are valid IDs and there is a valid entry of supported_language resembling the inserted record after fetchOne() but it returns null.
private Long addSupportedLanguage(Long languageId, Long shopId) {
Long value = this.ctx.insertInto(SHOP_LANGUAGE)
.set(SHOP_LANGUAGE.LANGUAGE_ID, languageId)
.set(SHOP_LANGUAGE.SHOP_ID, shopId)
.returning()
.fetchOne() // After this line I see the record in my db but it returns null
.getValue(SHOP_LANGUAGE.LANGUAGE_ID);
return value;
}
I also tried using
.returning(RESTAURANT_LANGUAGE.LANGUAGE_ID)
but the result is the same.
I am observing something similar when calling returnin() after a delete() statement:
public void deleteEmailVerificationToken(String token) {
ShopAdminVerificationTokenRecord shopAdminVerificationTokenRecord = this
.ctx.delete(SHOP_ADMIN_VERIFICATION_TOKEN)
.where(SHOP_ADMIN_VERIFICATION_TOKEN.VERIFICATION_TOKEN.eq(token))
.returning()
.fetchOne()
.into(SHOP_ADMIN_VERIFICATION_TOKEN);
if(shopAdminVerificationTokenRecord != null) {
LOGGER.debug("Deleted verification token for " + shopAdminVerificationTokenRecord.getUserEmail());
} else {
LOGGER.debug("The given token was not found.");
}
}
I don't understand why this is happening. The new record gets correctly inserted but still I cannot fetch that record immediately after insertion.
I created SHOP_LANGUAGE and all other objects using the jOOQ code generator. This is the SQL CREATE TABLE statement that I used to create the table for a MySQL database:
CREATE TABLE shop_language (
shop_id BIGINT NOT NULL,
CONSTRAINT fk__shop_language__shop
FOREIGN KEY (shop_id)
REFERENCES shop(id),
language_id BIGINT NOT NULL,
CONSTRAINT fk__shop_language__language
FOREIGN KEY (language_id)
REFERENCES language(id),
PRIMARY KEY (shop_id, language_id)
);
As mentioned in the comments:
DELETE .. RETURNING is not supported for MySQL as documented by the #Support annotation on the relevant returning() methods.

Linq queries and optionSet labels, missing formatted value

I'm doin a simple query linq to retrieve a label from an optionSet. Looks like the formatted value for the option set is missing. Someone knows why is not getting generated?
Best Regards
Sorry for the unclear post. I discovered the problem, and the reason of the missing key as formattedvalue.
The issue is with the way you retrieve the property. With this query:
var invoiceDetails = from d in xrmService.InvoiceSet
where d.InvoiceId.Value.Equals(invId)
select new
{
name = d.Name,
paymenttermscode = d.PaymentTermsCode
}
I was retrieving the correct int value for the option set, but what i needed was only the text. I changed the query this way:
var invoiceDetails = from d in xrmService.InvoiceSet
where d.InvoiceId.Value.Equals(invId)
select new
{
name = d.Name,
paymenttermscode = d.FormattedValues["paymenttermscode"]
}
In this case I had an error stating that the key was not present. After many attempts, i tried to pass both the key value and the option set text, and that attempt worked just fine.
var invoiceDetails = from d in xrmService.InvoiceSet
where d.InvoiceId.Value.Equals(invId)
select new
{
name = d.Name,
paymenttermscode = d.PaymentTermsCode,
paymenttermscodeValue = d.FormattedValues["paymenttermscode"]
}
My guess is that to retrieve the correct text associated to that option set, in that specific entity, you need to retrieve the int value too.
I hope this will be helpful.
Best Regards
You're question is rather confusing for a couple reasons. I'm going to assume that what you mean when you say you're trying to "retrieve a label from an OptionSet" is that you're attempting to get the Text Value of a particular OptionSetValue and you're not querying the OptionSetMetadata directly to retrieve the actual LocalizedLabels text value. I'm also assuming "formatted value for the option set is missing" is referring to the FormattedValues collection. If these assumptions are correct, I refer you to this: CRM 2011 - Retrieving FormattedValues from joined entity
The option set metadata has to be queried.
Here is an extension method that I wrote:
public static class OrganizationServiceHelper
{
public static string GetOptionSetLabel(this IOrganizationService service, string optionSetName, int optionSetValue)
{
RetrieveOptionSetRequest retrieve = new RetrieveOptionSetRequest
{
Name = optionSetName
};
try
{
RetrieveOptionSetResponse response = (RetrieveOptionSetResponse)service.Execute(retrieve);
OptionSetMetadata metaData = (OptionSetMetadata)response.OptionSetMetadata;
return metaData.Options
.Where(o => o.Value == optionSetValue)
.Select(o => o.Label.UserLocalizedLabel.Label)
.FirstOrDefault();
}
catch { }
return null;
}
}
RetrieveOptionSetRequest and RetrieveOptionSetResponse are on Microsoft.Xrm.Sdk.Messages.
Call it like this:
string label = service.GetOptionSetLabel("wim_continent", 102730000);
If you are going to be querying the same option set multiple times, I recommend that you write a method that returns the OptionSetMetadata instead of the label; then query the OptionSetMetadata locally. Calling the above extension method multiple times will result in the same query being executed over and over.

Get entity record values from Audit Details Response

Auditing of an entity is enabled,I want the entity record after deletion.So
I was trying to get that from audit entity records,like this:
RetrieveAuditDetailsRequest request = new RetrieveAuditDetailsRequest();
request.AuditId = _selectedId;
RetrieveAuditDetailsResponse response = (RetrieveAuditDetailsponse)_orgService.Execute(request);
EntityReference ObjectId = (EntityReference)response.AuditDetail.AuditRecord.Attributes["objectid"];
string ObjectName = ObjectId.LogicalName;
Guid Id = ObjectId.Id;
ColumnSet col = new ColumnSet(true);
Entity ent = _orgService.Retrieve(ObjectName,Id,col);
Its throwing an error "Expected non empty Guid".
FYI, I want this record values because I want to restore/recover record by creating it again.
Please help whats wrong with it??
You are attempting to retrieve the deleted record with this code:
string ObjectName = ObjectId.LogicalName;
Guid Id = ObjectId.Id;
ColumnSet col = new ColumnSet(true);
Entity ent = _orgService.Retrieve(ObjectName,Id,col);
This will fail with the error you are getting because no such record exists (it is deleted.) Unlike CRM 4 and earlier there are no soft deletes in 2011, once deleted it is gone from the database.
Replace it with the following code:
RetrieveRecordChangeHistoryRequest retrieveRequest = new RetrieveRecordChangeHistoryRequest();
changeRequest.Target = new EntityReference(ObjectName, Id);
RetrieveRecordChangeHistoryResponse response =
(RetrieveRecordChangeHistoryResponse)_orgService.Execute(retrieveRequest);
if (response.AuditDetailCollection != null)
{
var auditDetails = response.AuditDetailCollection;
// Do work
}
You then enumerate through the auditDetails to get the correct attributes.
You can find more at http://blogs.msdn.com/b/crm/archive/2011/05/23/recover-your-deleted-crm-data-and-recreate-them-using-crm-api.aspx.
The "Expected non empty Guid" error is thrown whenever you try to retrieve something with an empty GUID (Guid.Empty, 00000000-0000-0000-0000-000000000000). I'm guessing your _selectedId is not set to an actual GUID. Maybe you're setting it from a Nullable GUID and you are calling ValueOrDefault(), which is resulting in it getting set to the empty Guid, and failing in your Request?

Windows Azure: "An item with the same key has already been added." exception thrown on Select

I'm getting a strange error while trying to select a row from a table under Windows Azure Table Storage. The exception "An item with the same key has already been added." is being thrown even though I'm not inserting anything. The query that is causing the problem is as follows:
var ids = new HashSet<string>() { id };
var fields = new HashSet<string> {"#all"};
using (var db = new AzureDbFetcher())
{
var result = db.GetPeople(ids, fields, null);
}
public Dictionary<string, Person> GetPeople(HashSet<String> ids, HashSet<String> fields, CollectionOptions options)
{
var result = new Dictionary<string, Person>();
foreach (var id in ids)
{
var p = db.persons.Where(x => x.RowKey == id).SingleOrDefault();
if (p == null)
{
continue;
}
// do something with result
}
}
As you can see, there's only 1 id and the error is thrown right at the top of the loop and nothing is being modified.
However, I'm using "" as the Partition Key for this particular row. What gives?
You probably added an object with the same row key (and no partition key) to your DataServiceContext before performing this query. Then you're retrieving the conflicting object from the data store, and it can't be added to the context because of the collision.
The context tracks all object retrieved from the Tables. Since entities are uniquely identified by their partitionKey/rowKey combination, a context, like the tables, cannot contain duplicate partitionkey/rowkey combinations.
Possible causes of such a collison are:
Retrieving an entity, modifying it, and then retrieving it again using the same context.
Adding an entity to the context, and then retrieving one with the same keys.
In both cases, the context the encounters it's already tracking a different object which does however have the same keys. This is not something the context can sort out by itself, hence the exception.
Hope this helps. If you could give a little more information, that would be helpful.

Resources