I have three entities:
public class MainEntity()
{
public long Id { get; set; }
public long EntityAId { get; set; }
public EntityA OptionalEntityA { get; set; }
public long EntityBId { get; set; }
public EntityB OptionalEntityB { get; set; }
public string SProp { get; set; }
}
public class EntityA()
{
public long Id { get; set; }
public long MainEntityId { get; set; }
public MainEntity RequiredEntity { get; set; }
}
public class EntityB()
{
public long Id { get; set; }
public long MainEntityId { get; set; }
public MainEntity RequiredEntity { get; set; }
}
All three entities have there own Id generated by database:
Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
How can I define the Relationship between MainEntity and EntityA and EntityB in order to have:
MainEntity may have zero or one EntityA
MainEntity may have zero or one EntityB
EntityA must have one MainEntity
EntityB must have one MainEntity
In my MainEntityConfigurationMap I have defined relation like this:
HasOptional(m => m.OptionalEntityA).WithRequired(ea => ea.RequiredEntity);
HasOptional(m => m.OptionalEntityB).WithRequired(eb => eb.RequiredEntity);
Looking at the generated migration I have this for EntityA:
CreateTable(
"dbo.EntityA",
c => new
{
Id = c.Long(nullable: false, identity: true),
MainEntityId = c.Long(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.MainEntity", t => t.Id)
.Index(t => t.Id);
Entity Framework is defining a shared primary key here, is there a way to avoid that and make MainEntityId poiting to MainEntity.Id?
Entity framework supports one-to-one relation only on top of shared primary key (PK in dependent table is also FK to principal table). The reason is that FK in dependent table must be unique to enforce one-to-one relation but EF currently doesn't support unique constraint (except PK).
And to add on what #Ladislav Mrmka said...
Here is a full study on the various one-to-one modalities - and particularly how to make it using FK-s instead of tying up PK-s (but don't raise your hopes up too high).
Create a Unique Constraint To Enforce the Relationship as a One to One
It boils down to executing a SQL manually (during Seed-ing or similar Db initialization) - which adds a UNIQUE CONSTRAINT...
However, even in that case it won't work for you - or not w/o changes.
If I'm not mistaking - you'd have to give up one navigation property - and most likely the OptionEntityA/B - as unique column cannot support NULL.
Related
I'm looking for a way to map several POCO objects into single table in the ServiceStack.
Is it possible to do this in a clean way, without "hacking" table creation process?
As a general rule, In OrmLite: 1 Class = 1 Table.
But I'm not clear what you mean my "map several POCO objects into single table", it sounds like using Auto Mapping to populate a table with multiple POCO instances, e.g:
var row = db.SingleById<Table>(id);
row.PopulateWithNonDefaultValues(instance1);
row.PopulateWithNonDefaultValues(instance2);
db.Update(row);
If you need to maintain a single table and have other "sub" classes that maintain different table in the universal table you can use [Alias] so all Update/Select/Insert's reference the same table, e.g:
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
[Alias(nameof(Poco))]
public class PocoName
{
public int Id { get; set; }
public string Name { get; set; }
}
[Alias(nameof(Poco))]
public class PocoAge
{
public int Id { get; set; }
public int Age { get; set; }
}
Although I don't really see the benefit over having a single table that you use AutoMapping to map your other classes to before using that in OrmLite.
Well, in ServiceStack
where can I read up on the merits and differences of
[References(typeof(ABC))] and
[ForeignKey(typeof(XYZ) ]
What are they used for ? (I know, rather naively put but I have a hard time finding the basic description)
The docs for both are referenced throughout ServiceStack.OrmLite project page.
Use either for simple Foreign Keys
Essentially they're both equivalent to define simple Foreign Keys which you can use either for:
[References(typeof(ForeignKeyTable1))]
public int SimpleForeignKey { get; set; }
[ForeignKey(typeof(ForeignKeyTable1))]
public int SimpleForeignKey { get; set; }
The [References] attribute is also used by other data persistence libraries like PocoDynamo for DynamoDb where it would be preferred when wanting to re-use your existing data models else where, it's also useful as a benign "marker" attribute on different models when you want to include a navigable reference to an associated type for the property.
Fine-grained Foreign Key options
The [ForeignKey] is specific to OrmLite and includes additional fine-grained options for defining foreign key relationships specific to RDBMS's like different cascading options, e.g:
public class TableWithAllCascadeOptions
{
[AutoIncrement] public int Id { get; set; }
[ForeignKey(typeof(ForeignKeyTable1))]
public int SimpleForeignKey { get; set; }
[ForeignKey(typeof(ForeignKeyTable2), OnDelete = "CASCADE", OnUpdate = "CASCADE")]
public int? CascadeOnUpdateOrDelete { get; set; }
[ForeignKey(typeof(ForeignKeyTable3), OnDelete = "NO ACTION")]
public int? NoActionOnCascade { get; set; }
[Default(typeof(int), "17")]
[ForeignKey(typeof(ForeignKeyTable4), OnDelete = "SET DEFAULT")]
public int SetToDefaultValueOnDelete { get; set; }
[ForeignKey(typeof(ForeignKeyTable5), OnDelete = "SET NULL")]
public int? SetToNullOnDelete { get; set; }
}
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;
}
I am new to using DTOs.
I have two domain classes:
Category
Product
as follows
public class Category
{
// Properties
public int Id { get; set; }
public string Name { get; set; }
// Navigation properties
public virtual Category ParentCategory { get; set; }
// Foreign key
public int? ParentCategoryId { get; set; }
// Collections
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<Category> Subcategories { get; set; }
}
public class Product
{
// Properties
public int Id { get; set; }
public string Name { get; set; }
// Navigation property
public virtual Category Category { get; set; }
// Foreign key
public int CategoryId { get; set; }
}
I want to use Automapper.
I am not sure how to design DTOs for the above aggregate (graph).
Should CategoryDTO have a collection of type ProductDTO or a collection of type Product?
Should ProductDTO have a CategoryDTO as navigation property or a Category or just an Id to the Category?
Can anyone suggest the code for DTOs?
How should this structure be flattened (if it should) and mapped to domain classes?
Many thanks in advance.
I design my DTOs to be only the data used for a specific controller action for MVC. Typically this means if I have a CategoryController, then I have a CategoryIndexModel, CategoryDetailsModel, CategoryEditModel etc etc. Only include the information you want on that screen. I flatten as much as I can, I don't create child DTOs unless I have a Partial or collection.
I see these types of model is many samples online.
public class User
{
public long Id { get; set; }
public string Name{ get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Is it considered a good practice to instantiate a collection in the constructor like the code below? If so what are the reasons? How about objects in the model?
public class User
{
public User()
{
Products = new List<Product>();
}
public long Id { get; set; }
public string Name{ get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Well, I would say it depends on the situation, but Products in this case would be filled from the database, via a repository, so most probably ORM of some sort, so no initialization to new List would be needed in the constructor. The meaning of null for Products is indicative that the list isn't loaded yet. On the other hand, let's say that your object must have this collection initialized. For simple objects DDD says constructors are perfectly fine to to these things, but in case of complex objects, move the construction to the Factory.