PocoDynamo (the provided key element does not match the schema) - servicestack

I have created a table in Dynamo Db, with Id as the primary key and customerID as sortkey.
When i query an item by Id as shown below, I get error "the provided key element does not match the schema"
var db = new PocoDynamo(awsDb);
db.GetItem("aa4f0371-6144-4bd9-8980-5066501e37aa");
When I remove the sortkey from the dynamo DB, it works as expected.
What is the correct way to get an item by Id, which also has a sort key associated with it.
public class Notification
{
[PrimaryKey]
public Guid Id { get; set; }
[RangeKey] //Sort Key
public Guid CustomerId { get; set; }
public Guid LinkId { get; set; }
public string PreviewText { get; set; }
}

In PocoDynamo you can specify both Hash Key and Range Key with a [CompositeKey] attribute, e.g:
[CompositeKey(nameof(Id), nameof(CustomerId))]
public class Notification
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Guid LinkId { get; set; }
public string PreviewText { get; set; }
}

Related

PATCH in ServiceStack

I am trying to patch a object with the following code.
public object Patch(EditBlog request)
{
using (var db = _db.Open())
{
try
{
request.DateUpdated = DateTime.Now;
Db.Update<Blog>(request, x => x.Id == request.Id);
return new BlogResponse { Blog = Blog = Db.Select<Blog>(X=>X.Id == request.Id).SingleOrDefault()};
}
catch (Exception e)
{
return HttpError.Conflict("Something went wrong");
}
}
}
In Postman, I am calling the function like this "api/blog/1?=Title=Test1&Summary=Test&UserId=1".
When debugging I can see that those values has been assigned to the request.
During the Update it throws: "Cannot update identity column 'Id'"
My model looks like this
public class Blog
{
[AutoIncrement]
public int Id { get; set; }
public IUserAuth User { get; set; }
[Required]
public int UserId { get; set; }
[Required]
public string Title { get; set; }
public string Summary { get; set; }
public string CompleteText { get; set; }
[Required]
public DateTime DateAdded { get; set; }
public DateTime DateUpdated { get; set; }
}
And the EditBlog DTO looks like this:
[Route("/api/blog/{id}", "PATCH")]
public class EditBlog : IReturn<BlogResponse>
{
public int Id { get; set; }
public IUserAuth User { get; set; }
public int UserId { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string CompleteText { get; set; }
public DateTime DateUpdated { get; set; }
}
The error message "Cannot update identity column 'Id'" does not exist anywhere in ServiceStack.OrmLite, it could be an error returned by the RDBMS when you're trying to update the Primary Key which OrmLite wouldn't do when updating a Model annotated with a Primary Key like your Blog class has with its annotated [AutoIncrement] Id Primary Key.
The error is within your Db.Up<T> method that's performing the update, which is not an OrmLite API, so it's likely your own custom extension method or an alternative library.
I would implement a PATCH Request in OrmLite with something like:
var blog = request.ConvertTo<Blog>();
blog.DateUpdated = DateTime.Now;
Db.UpdateNonDefaults(blog);
i.e. using OrmLite's UpdateNonDefaults API to only update non default fields and updating using the Blog Table POCO not the EditBlog Request DTO.
Also you should use the Single APIs when fetching a single record, e.g:
Blog = Db.SingleById<Blog>(request.Id)
or
Blog = Db.Single<Blog>(x => x.Id == request.Id)
Instead of:
Blog = Db.Select<Blog>(X=>X.Id == request.Id).SingleOrDefault()

Automapper projection results in empty collection for nested Dto

I have a .Net Core 2 webapi in which I am using automapper to map to Dtos. Everything works fine, except I am seeing an unexpected behaviour when I map an object to a Dto, and where the Dto also contains mappings for a collection. E.g
CreateMap<Order, OrderDto>();
CreateMap<Product, ProductDto>();
Where classes are like this
public partial class Order
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products{ get; set; }
public int ProductCount {return Products.Count;}
}
public partial class Product
{
public int Id { get; set; }
public int OrderId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
The following works as expected;
The class is mapped, and the ProjectCount is correct in the Dto
public partial class OrderDto
{
public int Id { get; set; }
public virtual ICollection<Product> Products{ get; set; }
public int ProductCount{ get; set; }
}
_context.Orders.Include<>(Products).ProjectTo<>(OrderDto)
But doing the following, the productcount is always zero.
E.g. if I do this;
public partial class OrderDto
{
public int Id { get; set; }
public virtual ICollection<ProductDto> Products{ get; set; }
public int ProductCount{ get; set; }
}
public partial class ProductDto
{
public int Id { get; set; }
public int OrderId { get; set; }
public string Name { get; set; }
}
_context.Orders.Include<>(Products).ProjectTo<>(OrderDto)
Why does this happen, and how can I ensure that it doesnt? This is a real world example where I need a property which references the collection - and I need it in both the base and the Dto. I can do the following which works fine, but it doesnt appear that this should be how it works...
public partial class OrderDto
{
public int Id { get; set; }
public virtual ICollection<ProductDto> Products{ get; set; }
public int ProductCount {return Products.Count;}
}
public partial class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
}
_context.Orders.Include<>(Products).ProjectTo<>(OrderDto)
I profiled the SQL and found that Automapper changes the way the query is formed. Without the nested projection, two queries are made;
[Queries are more complex than this and use joins, but you get the idea]
Select Id from orders
Select Id,Name from products where productid in [select id from orders ]
With the nested projection, are executed for each nested Dto
Select Id from orders
Select Id,Name from products where id=1
Select Id,Name from products where id=2

ServiceStack AutoQuery - Anomaly When Using "?Fields="

We have noticed an anomaly when using "?Fields=" in version 4.0.55 (pre-release on MyGet).
We have an Employee table with three 1:1 relationships - EmployeeType, Department and Title:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
[References(typeof(EmployeeType))]
public int EmployeeTypeID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
[References(typeof(Title))]
public int TitleID { get; set; }
.
.
.
}
public class EmployeeType {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
}
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
[Reference]
public List<Title> Titles { get; set; }
}
public class Title {
[PrimaryKey]
public int ID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
public string Name { get; set; }
}
The latest update to 4.0.55 allows related DTOs to be requested using ?Fields= on the query string like this:
/employees?fields=id,firstname,lastname,departmentid,department
Note that the "proper" way to request a related DTO (department) is to also request the foreign key field (departmentid, in this case).
We wondered if there was a way to return all of the Employee table fields and only selected related DTOs, so in testing we found that this request works:
/employees?fields=department
We get back all the Employee table fields plus the related Department DTO - with one strange thing - the Employee's ID field is populated with the Employee's TitleID values.
Specifying the foreign key field in the request fixes that anomaly:
/employees?fields=id,departmentid,department
but we lose all of the other Employee fields.
Is there a way that to get all of the Employee fields and selected related DTOs?
Here is our AutoQuery DTO:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee>,
IJoin<Employee, EmployeeType>,
IJoin<Employee, Department>,
IJoin<Employee, Title> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string[] UserIDs { get; set; }
public int? EmployeeTypeID { get; set; }
public int[] EmployeeTypeIDs { get; set; }
public int? DepartmentID { get; set; }
public int[] DepartmentIDs { get; set; }
public int? TitleID { get; set; }
public int[] TitleIDs { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
[QueryDbField(Template = "(MONTH({Field}) = {Value})", Field = "DateOfBirth")]
public int? BirthMonth { get; set; }
[QueryDbField(Template = "(DAY({Field}) = {Value})", Field = "DateOfBirth")]
public int? BirthDay { get; set; }
[QueryDbField(Template = "(FirstName LIKE {Value} OR LastName LIKE {Value} OR PreferredName LIKE {Value})", ValueFormat = "%{0}%", Field = "ID")]
public string NameSearch { get; set; }
[QueryDbField(Template = "(FirstName LIKE {Value} OR LastName LIKE {Value} OR PreferredName LIKE {Value} OR Department.Name LIKE {Value} OR Title.Name LIKE {Value})", ValueFormat = "%{0}%", Field = "ID")]
public string BasicSearch { get; set; }
[QueryDbField(Template = "({Field} LIKE {Value})", Field = "EmployeeTypeName", ValueFormat = "%{0}%")]
public string EmployeeTypeSearch { get; set; }
[QueryDbField(Template = "({Field} LIKE {Value})", Field = "DepartmentName", ValueFormat = "%{0}%")]
public string DepartmentSearch { get; set; }
[QueryDbField(Template = "({Field} LIKE {Value})", Field = "TitleName", ValueFormat = "%{0}%")]
public string TitleSearch { get; set; }
}
Support for wildcard custom field lists was added in this commit where you can specify all fields of the primary or joined table by adding a .* suffix, e.g:
?fields=id,departmentid,department,employee.*
It essentially serves as a substitute placeholder which will be replace it with all fields in the specified table.
This change is available from v4.0.55 that's now available on MyGet.

How can I set a foreign key to allow nulls using ServiceStack OrmLite?

I am using ServiceStack v4.x VS2013
By default ServiceStack ORMLite (SqlServer) defines foreign keys with "NOT NULL".
The following code produces a foreign key "FooId (FK, long, not null)"
How can I tell ServiceStack this foreign key may be null?
public class Blah
{
[AutoIncrement]
public long Id { get; set; }
public string Name { get; set; }
[References(typeof(Foo))]
public long FooId { get; set; }
}
public class Foo
{
[AutoIncrement]
public long Id { get; set; }
public string Description { get; set; }
}
To specify a value type is nullable in OrmLite, make it nullable in C#:
public class Blah
{
[AutoIncrement]
public long Id { get; set; }
public string Name { get; set; }
[References(typeof(Foo))]
public long? FooId { get; set; }
}

Subsonic 3 - Simple repository and creating a foreign key

There seems to be hardly any examples out there so here goes:
Here are my three structures but it doesn't seem to create the tables properly and when I call the following line it says Id is not recognised:
IEnumerable<Permission> permissions = _data.Find<RolePermission>(x => x.Role.RoleKey == roleKey).Select(x => x.Permission);
RolePermission:
public class RolePermission
{
[SubSonicPrimaryKey]
public int RolePermissionId { get; set; }
public int RoleId { get; set; }
public int PermissionId { get; set; }
//Foreign Key of Role
public Role Role { get; set; }
//Foreign key of Permission
public Permission Permission { get; set; }
}
Permission:
public class Permission
{
[SubSonicPrimaryKey]
public int Id { get; set; }
[SubSonicLongString]
public string PermissionKey { get; set; }
[SubSonicLongString]
public string PermissionDescription { get; set; }
}
Role:
public class Role
{
[SubSonicPrimaryKey]
public int Id { get; set; }
[SubSonicLongString]
public string RoleKey { get; set; }
[SubSonicLongString]
public string RoleDescription { get; set; }
}
I don't know if this has been fixed in a current release but I remember a silly bug in subsonic's primary key detection.
If your Object contains a property named Id subsonic assumes that is your primary key.
If not you have to tell subsonic with is your PK by decorating a property with the SubSonicPrimaryKey attribute (like you did).
If you have a property called Id and it is also decorated with the attribute (like your Role and Permission class) subsonic finds the property twice and does not check if they both equals. Then it throws an exception because it can't reliably determine which one to take.
Long story short, you should try:
a) Remove the Attribute from your Id column
b) Rename the property to RoleId or PermissionId (which would be more consistend because your RolePermission class has it's PK called RolePermissionId)
If that doesn't help, please provide a stacktrace.

Resources