ArangoDb.Net upsert always insert - arangodb

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 }

Related

How to search List of name which are present in cosmos documents

I have a list of name. I need to find which name is present in FirstName or LastName Property of Document in Collection.
I had tried the Linq query to archive this but it through error ("Object reference not set to an instance of an object.").
public class UserDoc
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DocumentType { get { return "userdoc"; } private set { } }
}
List<string> Names = new List<string>() { "satya", "singh" };
IEnumerable<UserDoc> Users = await _dBRepository.GetItemsAsync<UserDoc>
(x => (Names.Contains(x.FirstName + " " + x.LastName))&& x.DocumentType == "userdoc");
public async Task<IEnumerable<T>> GetItemsAsync<T>(Expression<Func<T, bool>> predicate) where T : class
{
IDocumentQuery<T> query = _client.CreateDocumentQuery<T>(documentCollectionUri:
UriFactory.CreateDocumentCollectionUri(databaseId: _databaseId, collectionId: _collectionId),
feedOptions: new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
According to your description. You code should be modified as below:
IEnumerable<UserDoc> Users = await _dBRepository.GetItemsAsync<UserDoc>
(x => (Names.Contains(x.FirstName)|| Names.Contains(x.LastName))&& x.DocumentType == "userdoc");
I have tested the code and it worked.
According to your error message, i think the the most likely reason is that the object _client point to Null, please check it and try again.

ASP.Net MVC : ValidationResult does not accept int.tostring() for member name

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?

Azure table entity model design

In most examples of table entity models I see something like:
public class CustomerEntity : TableEntity
{
public CustomerEntity(string lastName, string firstName)
{
this.PartitionKey = lastName;
this.RowKey = firstName;
}
public CustomerEntity() { }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
As we see here lastname and firstname used as partition key and row key accordingly. So later after saving and retrieving an entity I can access those info from PartitionKey and RowKey properties. But what if I want to send this model to client side later as json, I suppose PartitionKey and RowKey of TableEntity base class would not be serialized. So if i add LastName and FirstName as properties to model, then unnecessary data duplication will occur in storage. What is the best way avoid data duplication in storage and in the same time have an access to lastname and firstname after model been serialized.
You can always use a getter method on your class to avoid confusion:
public class CustomerEntity : TableEntity
{
public CustomerEntity(string lastName, string firstName)
{
this.PartitionKey = lastName;
this.RowKey = firstName;
}
public CustomerEntity() { }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string FirstName { get { return this.RowKey; } }
public string LastName { get { return this.PartitionKey; } }
}
Or, you could map the data to an anonymous object in your API and return that via JSON:
var customerJson = new
{
Firstname = customer.RowKey,
LastName = customer. PartitionKey,
customer.Email,
customer.PhoneNumber
};
return JsonConvert.SerializeObject(customerJson);

Populating POCO's with ServiceStack.OrmLite

Consider the following domain model:
class Customer
{
int id {get;set}
string Name {get;set}
List<Contact> Contacts {get;set}
}
class Contact
{
int id {get;set}
string FullName {get;set}
List<PhoneNumber> {get;set}
}
class PhoneNumber
{
int id {get;set}
PhoneType Type {get;set}
string Number {get;set}
}
Customer, Contact & PhoneNumbers are separate entities in our DB.
Any Suggestions as to how to populate a full customer with all its linked contacts and the contacts phone numbers in the most efficient way using ORMLite and/or Dapper, with consideration of calls to the db and cpu cycles to map to the POCOs?
If you're using ORMLite, then it has an attribute called [Ignore] which you can place on your properties, which means they won't be persisted (I find this useful for having navigation properties on my models)
So with that, I would do the following:
public class Customer : IHasIntId
{
[AutoIncrement]
[PrimaryKey]
public int Id {get;set}
public string Name {get;set}
[Ignore]
public List<Contact> Contacts {get;set}
}
public class Contact : IHasIntId
{
[AutoIncrement]
[PrimaryKey]
public int Id {get;set}
public string FullName {get;set}
[References(typeof(Customer)]
public int CustomerId {get;set;}
[Ignore]
public List<PhoneNumber> PhoneNumbers {get;set}
}
public class PhoneNumber : IHasIntId
{
[AutoIncrement]
[PrimaryKey]
public int id {get;set}
public PhoneType Type {get;set}
public string Number {get;set}
[References(typeof(Contact))]
public int ContactId {get;set;}
}
Then in your CustomersService, something like this should suffice:
public class CustomersService : Service {
public object Get(Customers request) {
var customers = Db.Select<Customer>();
var customerIds = customers.Select(c=>c.Id).ToList();
var contacts = Db.Select<Contact>(c=>customerIds.Contains(c.CustomerId));
var contactIds = contacts.Select(c=>c.Id).ToList();
var phoneNumbers = Db.Select<PhoneNumber>(p=>contactIds.Contains(p.ContactId));
customers.ForEach(customer=> {
var customerContacts = contacts.Where(c=>c.CustomerId == customer.Id);
customerContacts.ForEach(contact=> {
contact.PhoneNumbers = phoneNumbers.Where(p=>p.ContactId ==
contact.Id).ToList();
});
customer.Contacts = customerContacts
});
return new CustomersResponse {
Result = customers
}
}
}
Note: I'm using ServiceStack ORMLite version 3.*. I understand in v4 there is a better way of doing this, check here: https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/tests/ServiceStack.OrmLite.Tests/LoadReferencesTests.cs#L80
I have updated my example to pull the flat customer list and transform it into a hierarchy. A couple of things to note:
I'm explicitly specifying columns in the join builder because the Id columns have the same name in the join
the performance should be decent because I'm using a hash for customers and adding phone numbers, so the only real loop is the search for a matching contact
Models:
class CustomerComplete
{
[BelongTo(typeof(Customer))]
public string CustomerName { get; set; }
[BelongTo(typeof(Customer))]
public int CustomerId { get; set; }
[BelongTo(typeof(Contact))]
public int ContactId { get; set; }
[BelongTo(typeof(Contact))]
public string ContactFullName { get; set; }
[BelongTo(typeof(PhoneNumber))]
public int PhoneNumberId { get; set; }
[BelongTo(typeof(PhoneNumber))]
public PhoneType Type { get; set; }
[BelongTo(typeof(PhoneNumber))]
public string Number { get; set; }
}
class Customer
{
public Customer()
{
this.Contacts = new List<Contact>();
}
[AutoIncrement, PrimaryKey]
public int Id { get; set; }
public string Name { get; set; }
[Ignore]
public List<Contact> Contacts { get; set; }
}
class Contact
{
public Contact()
{
this.PhoneNumbers = new List<PhoneNumber>();
}
[AutoIncrement, PrimaryKey]
public int Id { get; set; }
public string FullName { get; set; }
[References(typeof(Customer))]
public int CustomerId { get; set; }
[Ignore]
public List<PhoneNumber> PhoneNumbers { get; set; }
}
class PhoneNumber
{
[AutoIncrement, PrimaryKey]
public int Id { get; set; }
public PhoneType Type { get; set; }
public string Number { get; set; }
[References(typeof(Contact))]
public int ContactId { get; set; }
}
enum PhoneType { None = 0 }
Usage:
db.CreateTableIfNotExists<Customer>();
db.CreateTableIfNotExists<Contact>();
db.CreateTableIfNotExists<PhoneNumber>();
db.Insert<Customer>(new Customer { Name = "Widget Co Inc" });
var customerId = (int) db.GetLastInsertId();
db.Insert<Contact>(new Contact { FullName = "John Smith", CustomerId = customerId });
var contactId = (int)db.GetLastInsertId();
db.Insert<PhoneNumber>(new PhoneNumber { ContactId = contactId, Number = "555.555.5555", Type = PhoneType.None });
db.Insert<PhoneNumber>(new PhoneNumber { ContactId = contactId, Number = "444.444.4444", Type = PhoneType.None });
db.Insert<Contact>(new Contact { FullName = "Jack Smith", CustomerId = customerId });
contactId = (int)db.GetLastInsertId();
db.Insert<PhoneNumber>(new PhoneNumber { ContactId = contactId, Number = "111.111.1111", Type = PhoneType.None });
// contact without phone number test
db.Insert<Contact>(new Contact { FullName = "Jill Smith", CustomerId = customerId });
// build join
var jn = new JoinSqlBuilder<Customer, Customer>()
.LeftJoin<Customer, Contact>(
customer => customer.Id,
contact => contact.CustomerId,
customer => new { CustomerId = customer.Id, CustomerName = customer.Name },
contact => new { ContactId = contact.Id, ContactFullName = contact.FullName })
.LeftJoin<Contact, PhoneNumber>(
contact => contact.Id,
phone => phone.ContactId,
null,
phone => new { PhoneNumberId = phone.Id, phone.Number, phone.Type });
var all = db.Select<CustomerComplete>(jn.ToSql());
var customerHash = new Dictionary<int, Customer>();
foreach (var cc in all)
{
if (!customerHash.ContainsKey(customerId))
customerHash[customerId] = new Customer { Id = cc.CustomerId, Name = cc.CustomerName };
if (cc.ContactId == 0) continue; // no contacts for this customer
var contact = customerHash[customerId].Contacts.Where(x => x.Id == cc.ContactId).FirstOrDefault();
if (null == contact)
{
contact = new Contact
{
CustomerId = customerId,
Id = cc.ContactId,
FullName = cc.ContactFullName
};
// add contact
customerHash[customerId].Contacts.Add(contact);
}
if (cc.PhoneNumberId == 0) continue; // no phone numbers for this contact
contact.PhoneNumbers.Add(new PhoneNumber
{
ContactId = cc.ContactId,
Id = cc.PhoneNumberId,
Number = cc.Number,
Type = cc.Type
});
}
// our hierarchical customer list
var customers = customerHash.ToList();
Also note that v4 seems to support a much easier way of doing this using the Reference attribute, though it may make multiple calls to the DB. See the unit tests here for an example.

Entity Framework Insert object with related object

I am a newbie with Entity Framework and I need to insert an object Comment that has a related FK object User into the database.
public Class Comment
{
public int CommentID { get; set; }
public string CommentContent { get; set; }
public virtual User User { get; set; }
public virtual DateTime CommentCreationTime { get; set; }
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string UserPassword { get; set; }
public string UserImageUrl{get; set;}
public DateTime UserCreationDate { get; set; }
public virtual List<Comment> Comments { get; set; }
}
public void AddComment()
{
User user = new User() { UserID = 1 };
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
var ctx = new WallContext();
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}
Ideally, with T-SQL, if I know the PRIMARY KEY of my User object, I could just insert my Comment object and specify the PK of my 'User' in the insert statement.
I have tried to do the same with Entity Framework and it doesn't seem to work. It would be overkill to have to first fetch the User object from the database just to insert a new 'Comment'.
Please, how can I achieve this ?
You need to attach the user object to the context so that the context knows its an existing entity
public void AddComment()
{
var ctx = new WallContext();
User user = new User() { UserID = 1 };
ctx.Users.Attach(user);
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}

Resources