//Data Model
[DynamoDBTable("ContactsTable")]
public class Contact
{
[DynamoDBHashKey(AttributeName = "Id")]
public string Id { get; set; }
[DynamoDBProperty(AttributeName = "FirstName")]
public string FirstName { get; set; }
[DynamoDBProperty(AttributeName = "LastName")]
public string LastName { get; set; }
[DynamoDBProperty(AttributeName = "Address1")]
public string Address1 { get; set; }
[DynamoDBProperty(AttributeName = "Address2")]
public string Address2 { get; set; }
}
public void SaveContactsToDynamoDb()
{
var config = new AmazonDynamoDBConfig();
config.ServiceURL = ConfigurationManager.AppSettings["ServiceURL"];
var context = new DynamoDBContext(new AmazonDynamoDBClient(config));
var contactBatchWrite = context.CreateBatchWrite<Contact>();
var contacts = GetContacts();
contactBatchWrite.AddPutItems(contacts.Select(contact => new Contact
{
Id = contact.Id.ToString(),
FirstName = contact.FirstName,
LastName = contact.LastName,
Address1 = contact.Address1,
Address2 = contact.Address2
}));
contactBatchWrite.Execute();
}
When I execute it it returns an exception saying that "AttributeValue may not contain an empty string". I understand that some of the fields for a particular contact that are being mapped might contain empty fields. As DynamoDB does not accept null or empty fields, is there any way that I can modify the data model to add some attribute to property which ignores if its null or empty? Or can anyone give me a better way to map/ handle in this situation. I do not want to add constant like "N/A" if null or empty.
Thank you.
DynamoDB does accept null as attribute value when mapping Objects. It just drops the attribute internally.
If you need 2 empty values (null and empty string) you will have to use an __EMPTY_STRING constant, otherwise - you can just use null.
Related
I'm making a small application with CRUD functions with ArangoDatabase and its driver:
http://www.arangoclient.net/
Here is my code:
var insert = new Account
{
Email = "email01#gmail.com",
FirstName = "Adam",
LastName = "Smith"
};
var update = new Account
{
Email = "email01#gmail.com",
FirstName = "John",
LastName = "Peterson"
};
using (var arangoDatabase = new ArangoDatabase(new DatabaseSharedSetting()
{
Url = "http://127.0.0.1:8529/",
Database = "_system",
Credential = new NetworkCredential()
{
UserName = "root",
Password = "xvxvc"
}
}))
{
arangoDatabase.Query()
.Upsert(_ => new Account() {Email = insert.Email},
_ => insert, ((aql, x) => update))
.In<Account>()
.Execute();
}
For the first time running, [insert] object is added to database.
Therefore, my database now is :
But at the second time of running code, it throws me an error :
unique constraint violated (while executing). ErrorNumber: 1210 HttpStatusCode: 409
The question is: What is my problem and how to solve it?
Thank you,
Problem could be upsert search expression serialization:
Assume Account class definition is:
public class Account
{
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
upsert search expression: new Account() {Email = insert.Email} will serializes to:
{ Email: "email01#gmail.com", FirstName: null, LastName: null }
but what is expected is:
{ Email: "email01#gmail.com" }
Since search expression will never found a document, then insert will occur and you get unique constraint violated.
There are two solution to avoid serializing FirstName and LastName members:
One is we could use Json.net JsonProperty attribute to ignore nulls in serialization:
public class Account
{
public string Email { get; set; }
[Newtonsoft.Json.JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string FirstName { get; set; }
[Newtonsoft.Json.JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string LastName { get; set; }
}
And the other way is to use an anonymous object for search expression:
arangoDatabase.Query()
.Upsert(_ => new Account() {Email = insert.Email}
// should be
arangoDatabase.Query()
.Upsert(_ => new {Email = insert.Email}
One note about using anonymous object is that Email member could resolve to something else base on what you specify for its naming convention, for example:
public class Account
{
[DocumentProperty(Identifier = IdentifierType.Key)]
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
If you specify Email as a Key identifier, then you should use _key in the anonymous object:
arangoDatabase.Query()
.Upsert(_ => new { _key = insert.Email }
code taken from http://www.dotnetcurry.com/aspnet-mvc/1083/aspnet-mvc-self-validation-model-objects
public class Person : IValidatableObject
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
[Required]
public DateTime BirthDate { get; set; }
[Required]
public int Income { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var pIncome = new[] { "Income" };
if (Income < 0)
{
yield return new ValidationResult("Income cannot be negative", pIncome);
}
var pName = new[] { "Name" };
if (Name.Length > 40)
{
yield return new ValidationResult("Name cannot be such huge in length", pName);
}
var pBDate = new[] { "BirthDate" };
if (BirthDate > DateTime.Now)
{
yield return new ValidationResult("Sorry Future Date cannot be accepted.", pBDate);
}
}
}
see this line
yield return new ValidationResult("Income cannot be negative", pIncome);
if i write it like
yield return new ValidationResult("Income cannot be negative", Income.ToString());
then getting error but the moment we pass a string variable then no problem. so if i need to validate many int type property then do i need to put those property name in
string array like var pIncome = new[] { "Income" }; ?
if some many element name is there in array then how to refer it for 2nd argument for ValidationResult ctor ?
please help. thanks
if i am not wrong it is supposed to be collection (IEnumerable) , so it has to be of [] type. changing to .tostring() will not yield array.
public ValidationResult(string errorMessage, IEnumerable<string> memberNames);
Moreover the overloaded ctor is assign the validation error for one or more fields in the model and if you want to omit passing member name, i guess the error wont be associated with any field rather for the form. please refer another nice answer here Assign ValidationResult to specific field?
We're switching over from Lokad to CloudFx to handle putting things in/out of table storage.
With Lokad, we had 3 entities:
1) Object 1, which added partition key and row key to
2) Object 2 which had a bunch of properties as well as an instance of
3) Object 3, which had its own set of properties
As far as I can tell, the only way for CloudFx to input that info into table storage is to flatten that whole thing out with one massive object that has all the properties of the previous three objects. With Lokad, we could just useSkinny=true.
Thoughts?
This is the method we ended up implementing. Short version: serialize object 2 and stuff it into the Value property of Object 1, then put in table storage.
Object 1:
public class CloudEntity<T>
{
public CloudEntity()
{
Timestamp = DateTime.UtcNow;
}
public string RowKey { get; set; }
public string PartitionKey { get; set; }
public DateTime Timestamp { get; set; }
public T Value { get; set; }
}
Object 2:
public class Store
{
public string StoreId { get; set; }
public string StoreName { get; set; }
public string StoreType { get; set; }
public Address MailingAddress { get; set; }
public PhoneNumber TelephoneNumber { get; set; }
public StoreHours StoreHours { get; set; }
}
Object 3 can be whatever...the address in this case perhaps...it all gets serialized.
So in code you can get the table as follows (more than 1 way to do this):
var tableStorage = new ReliableCloudTableStorage(connection string you're using);
Then let's say you have an instance of store() you want to put in table storage:
var myStore = new Store(
{
storeId = "9832",
storeName = "Headquarters"
...
});
You can do so in the following way:
var cloudEntity = new CloudEntity<string>
{
PartitionKey = whatever you want your partition key to be,
RowKey = whatever you want your row key to be,
Value = JsonConvert.SerializeObject(myStore) // THIS IS THE MAGIC
};
tableStorage.Add<CloudEntity<string>>(name of table in table storage, cloudEntity);
The entity put in table storage will have all the properties of the CloudEntity class (row key, partition key, etc) and in the "Value" column there will be the json of the object you wanted to store. It's easily readable through Azure Storage Explorer which is nice too.
To get the object back out, use something like this:
var cloudEntity = tableStorage.Get<CloudEntity<string>>(name of your table, partitionKey: whatever the partition key is);
Then you can deserialize the "Value" field of those into the object you're expecting:
var myStore = JsonConvert.DeserializeObject<Store>(cloudEntity.Value);
If you're getting a bunch back, just make myStore a list and loop through the cloudEntities, deserializing and adding each to your list.
Hope this helps!
Normally when I use the DropDownListFor helper I'm selecting which item is selected based on an ID (int), but now I have a situation where I need to display which item is selected based on the text (string) and not an ID. In the controller the model is being set correctly to the value that I want with this property:
model.Title
An example title would be "Front Office". I have the following code in my Controller:
ViewBag.Titles = new SelectList(jobTitles, "Name", "Name");
and on the view I have this:
#Html.DropDownListFor(model => model.Title, ViewBag.Titles as SelectList)
The DropDownList is populating correctly with all the expected job titles, it just isn't selecting the correct job title based on model.Title. What am I doing wrong?
UPDATE:
There seems to be something else potentially going wrong in my code, so I'm putting all of it here to see if I'm doing something wrong.
Controller:
public ActionResult Edit(int id = 0)
{
StaffMember staffmember = StaffMember.SelectByID(id); // gets staff member from db
ViewBag.Titles = new SelectList(JobTitle.SelectAll(), "Name", "Name", staffmember.Title); // JobTitle.SelectAll() returns List<JobTitle>
StaffEditModel model = new StaffEditModel();
model.ID = staffmember.ID;
model.ClientID = staffmember.ClientID;
model.FirstName = staffmember.FirstName;
model.MiddleInitial = staffmember.MiddleInitial;
model.LastName = staffmember.LastName;
model.Title = staffmember.Title;
model.Phone = staffmember.Phone;
model.Email = staffmember.Email;
model.Birthday = staffmember.Birthday;
model.HireDate = staffmember.HireDate;
model.Biography = staffmember.Biography;
if (staffmember == null)
{
return HttpNotFound();
}
return View(model);
}
Model:
public class StaffEditModel
{
public int ID { get; set; }
public int ClientID { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Middle Initial")]
public string MiddleInitial { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
public string Title { get; set; } // this is the property I'm trying to show in DropDown
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public string Birthday { get; set; }
[Display(Name = "Hire Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public string HireDate { get; set; }
public string Email { get; set; }
[MaxLength(14)]
public string Phone { get; set; }
public string Biography { get; set; }
}
View:
#Html.DropDownListFor(model => model.Title, new SelectList(ViewBag.Titles,"Value","Text", Model.Title))
This should work.
You can pass ViewBag.Titles as List<SelectListItem> from controller
var jobList = new List<SelectListItem>();
foreach (var job in jobTitles)
{
var item = new SelectListItem();
item.Value = job.PropertyName; //the property you want to display i.e. Title
item.Text = job.PropertyName;
jobList.Add(item);
}
ViewBag.Title = jobList;
and then in the view,
#Html.DropDownListFor(model => model.Title, new SelectList(ViewBag.Titles,"Value","Text", Model.Title))
I know I'm late to the party on this, but I had a similar problem and thought I would add what I've found.
I haven't figured out the why yet - I'm still researching (that's how I ended up here) - but I think it has something to do with the fact that you're using the word Title
I've got the exact same scenario and as soon as I changed my model to CustomerTitle instead of Title things worked as expected.
Initial thoughts are that the model binder is finding Title somewhere else in the DOM and getting tripped up. Complete speculation. If I have more time I'll keep digging for the why
What are the actual requirements for ORMLite to project result of the call to stored procedure onto the model. I have a class that has some attributes and it will not map output of the sp correctly. If I remove attributes then it does map it correctly. For example:
public class Test
{
[Alias("InsuredId")]
public string Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleInitial { get; set; }
}
SP returns these columns: InsuredId, LastName, FirstName, MiddleInitial and some more.
If I have Alias attribute all properties are populated with null. If I remove attribute, then all are fine except the Id. Following is the actual code.
var test =
db.SqlList<Test>(
"EXEC up_InsuredSearchTest #ItemId, #FirstName, #LastName, #DateOfBirth, #Max_Search_Records",
new
{
ItemId = memberId,
FirstName = firstName,
LastName = lastName,
DateOfBirth = dateOfBirth.HasValue? dateOfBirth.Value.ToShortDateString() : "",
Max_Search_Records = MAX_SEARCH_RECORDS
});
Not really an issue with ServiceStack. Value returned was an int but was mapped as string, and it was silently failing in ServiceStack, but error was logged. Error was not very informative, so I had to debug through ORMLite source to figure out what the problem was.