servicestack ormlite and foreign keys to same table - servicestack

I have a table of links, and some links will be child links, referencing the parent links ID
however i can not get my head around servicestack ormlite and populating a property of children, will all the child links when getting a list of all links.
Here is my model:
public partial class Navigation
{
[Alias("Id"), AutoIncrement]
public int Id { get; set; }
[Alias("ParentId")]
[Display( Name = "ParentId")]
[References(typeof(Navigation))]
public int? ParentId { get; set; }
[Alias("LinkText")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "LinkText")]
public string LinkText { get; set; }
[Alias("Action")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "Action")]
public string Action { get; set; }
[Alias("Controller")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "Controller")]
public string Controller { get; set; }
[Alias("Area")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "Area")]
public string Area { get; set; }
[Alias("Visible")]
[Display( Name = "Visible"),Required(ErrorMessage = " is required" )]
public bool Visible { get; set; }
[Alias("Sequence")]
[Display( Name = "Sequence")]
public int? Sequence { get; set; }
[ForeignKey(typeof(Navigation))]
public virtual ICollection<Navigation> Children { get; set; }
}
any ideas ?

You can do that using inheritance. The parent class will contain a reference to the child class. I had to use it to get which user have created each user. Here is a sample:
public class UserCommon
{
[References(typeof(User))] // Self reference workaround for User ;)
public Guid CreatedBy { get; set; }
}
public class User : UserCommon
{
public Guid Uid { get; set; }
public String Username { get; set; }
public String Password { get; set; }
}
Something you need to pay attention to is to include the Id in the child class not the parent. The table that will be generated is as follow. The foreign key is a self reference
Getting the list of children should be as easy as a simple LINQ query that will fetch all children for a certain parent Guid. CreatedBy is also a property of User becuase of inheritance.
db.Select<User>(q => q.CreatedBy == '734FD814-024D-4795-AFD0-34FECF89A13A');
// Just a sample Guid, you should be able to select
// the Guid you need and insert it here.

Tables in OrmLite are strictly a 1:1 mapping with the underlying db tables.
This means all complex type properties are blobbed into a db text field with the property name, they're never used to auto-map to child relations as you're expecting to do here.
Here's an early answer that shows how you could map many to many relations with OrmLite.

Related

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

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; }
}

ServiceStack - [Reference] or [Ignore]?

We have a DTO - Employee - with many (> 20) related DTOs and DTO collections. For "size of returned JSON" reasons, we have marked those relationships as [Ignore]. It is then up to the client to populate any related DTOs that they would like using other REST calls.
We have tried a couple of things to satisfy clients' desire to have some related Employee info but not all:
We created a new DTO - EmployeeLite - which has the most-requested fields defined with "RelatedTableNameRelatedFieldName" approach and used the QueryBase overload and that has worked well.
We've also tried adding a property to a request DTO - "References" - which is a comma-separated list of related DTOs that the client would like populated. We then iterate the response and populate each Employee with the related DTO or List. The concern there is performance when iterating a large List.
We're wondering if there a suggested approach to what we're trying to do?
Thanks for any suggestions you may have.
UPDATE:
Here is a portion of our request DTO:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
}
There is no code for the service (automagical with QueryDb), so I added some to try the "merge" approach:
public object Get(FindEmployeesRequest request) {
var query = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<Employee> response = AutoQuery.Execute(request, query);
if (response.Total > 0) {
List<Clerkship> clerkships = Db.Select<Clerkship>();
response.Results.Merge(clerkships);
}
return response;
}
This fails with Could not find Child Reference for 'Clerkship' on Parent 'Employee'
because in Employee we have:
[Ignore]
public List<Clerkship> Clerkships { get; set; }
which we did because we don't want "Clerkships" with every request. If I change [Ignore] to [Reference] I don't need the code above in the service - the List comes automatically. So it seems that .Merge only works with [Reference] which we don't want to do.
I'm not sure how I would use the "Custom Load References" approach in an AutoQuery service. And, AFAIKT, the "Custom Fields" approach can't be use for related DTOs, only for fields in the base table.
UPDATE 2:
The LoadSelect with include[] is working well for us. We are now trying to cover the case where ?fields= is used in the query string but the client does not request the ID field of the related DTO:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
.
.
.
[References(typeof(Department))]
public int DepartmentID { get; set; }
.
.
.
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
.
.
.
}
So, for the request
/employees?fields=id,departmentid
we will get the Department in the response. But for the request
/employees?fields=id
we won't get the Department in the response.
We're trying to "quietly fix" this for the requester by modifying the query.SelectExpression and adding , "Employee"."DepartmentID" to the SELECT before doing the Db.LoadSelect. Debugging shows that query.SelectExpression is being modified, but according to SQL Profiler, "Employee"."DepartmentID" is not being selected.
Is there something else we should be doing to get "Employee"."DepartmentID" added to the SELECT?
Thanks.
UPDATE 3:
The Employee table has 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 this:
/employees?fields=employeetype,department,title
I get back all the Employee table fields plus the three related DTOs - with one strange thing - the Employee's ID field is populated with the Employee's TitleID values (I think we saw this before?).
This request fixes that anomaly:
/employees?fields=id,employeetypeid,employeetype,departmentid,department,titleid,title
but I lose all of the other Employee fields.
This sounds like a "have your cake and eat it too" request, but is there a way that I can get all of the Employee fields and selective related DTOs? Something like:
/employees?fields=*,employeetype,department,title
AutoQuery Customizable Fields
Not sure if this is Relevant but AutoQuery has built-in support for Customizing which fields to return with the ?fields=Field1,Field2 option.
Merge disconnected POCO Results
As you've not provided any source code it's not clear what you're trying to achieve or where the inefficiency with the existing solution lies, but you don't want to be doing any N+1 SELECT queries. If you are, have a look at how you can merge disconnected POCO results together which will let you merge results from separate queries based on the relationships defined using OrmLite references, e.g the example below uses 2 distinct queries to join Customers with their orders:
//Select Customers who've had orders with Quantities of 10 or more
List<Customer> customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Qty >= 10)
.SelectDistinct());
//Select Orders with Quantities of 10 or more
List<Order> orders = db.Select<Order>(o => o.Qty >= 10);
customers.Merge(orders); // Merge disconnected Orders with their related Customers
Custom Load References
You can selectively control which references OrmLite should load by specifying them when you call OrmLite's Load* API's, e.g:
var customerWithAddress = db.LoadSingleById<Customer>(customer.Id,
include: new[] { "PrimaryAddress" });
Using Custom Load References in AutoQuery
You can customize an AutoQuery Request to not return any references by using Db.Select instead of Db.LoadSelect in your custom AutoQuery implementation, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.Select(q),
Total = (int)Db.Count(q),
};
return response;
}
Likewise if you only want to selectively load 1 or more references you can change LoadSelect to pass in an include: array with only the reference fields you want included, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.LoadSelect(q, include:new []{ "Clerkships" }),
Total = (int)Db.Count(q),
};
return response;
}

Serialize Object Azure Mobile Services

I am trying to serialize an object to Azure Mobile Services.
The object contains an array of a second object which should also be serialized.
[DataContract()]
class ObjectA
{
[DataMember(Name= "id")]
public int Id { get; set; }
[DataMember(Name = "info")]
public string info{ get; set; }
[DataMember(Name = "collectionOfB")]
public ObjectB[] myArrayOfB{ get; set; }
}
[DataContract()]
class ObjectB
{
[DataMember(Name= "id")]
public int Id { get; set; }
[DataMember(Name = "info")]
public string info{ get; set; }
}
I have loaded both table's properly and can insert an individual item into each of the tables.
However when I call the InsertAsync method on the table for objectA I receive an error
Cannot serialize member 'myArrayOfB' of type 'namespace.ObjectB[]' declared on type 'ObjectA'
Any idea's on what I need to do to fix this?
Mobile Services doesn't support serialization of arrays. There are two good posts here that show how you might support this:
http://blogs.msdn.com/b/carlosfigueira/archive/2012/08/30/supporting-arbitrary-types-in-azure-mobile-services-managed-client-simple-types.aspx
http://blogs.msdn.com/b/carlosfigueira/archive/2012/09/11/supporting-complex-types-in-azure-mobile-services-clients-implementing-1-n-table-relationships.aspx

need help to map ViewModel to Entity using AutoMapper

I am new to automapper. need some help to map from ViewModel to Entity.
Here's my user entity
public class User
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public DateTime CreatedDate { get; set; }
public string DisplayName { get; set; }
}
here's my ViewModel
public class UserViewModel
{
public string Email { get; set; }
public string Password { get; set; }
}
I create a map. its not working
CreateMap<UserLoginViewModel, User>()
.ForMember(dest=>dest.CreatedDate, DateTime.Now)
.ForMember(dest=>dest.DisplayName, "");
DisplayName and CreatedDate are required fields. since its not in the ViewModel, I will make DisplayName = "" and CreateDate = datetime.now.
I want to know if I can do it using the automapper, or I have to do it after the mapping.
please show me some sample code.
You mentioned that you have UserViewModel view model and User entity however your mapping configuration contains third type - UserLoginViewModel. Supposing that UserLoginViewModel is the same as UserViewModel, you should change you configuration as below.
Mapper.CreateMap<UserViewModel, User>()
.ForMember(dest=>dest.CreatedDate, t=> t.MapFrom(s=> DateTime.Now))
.ForMember(dest=>dest.DisplayName, t=> t.MapFrom(s=> ""));
AutoMapper wiki.

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