Breeze doesn't expand TPH entities correctly - entity-framework-5
Breeze doesn't expand TPH entities correctly.
When using expand in breeze if you are using TPH expand will only work for the first entity, the others properties will be null. If I change the entity not to use inheritances it works fine. I've also tested returning each entity separately in an expand query that also worked fine.
//client side code
var getResidentById = function (id, obs) {
var query = EntityQuery.from('Residents')
.where('id', '==', id)
.expand('user, currentUnit, leases, leases.unit, leases.leaseStatus');
return manager.executeQuery(query).then(function (data) {
if (obs) {
obs(data.results[0])
}
}, queryFailed);
};
//Controler Endpoint
[HttpGet]
public IQueryable<Resident>
{
return _context.Context.UserDetails.OfType<Resident>();
}
//Model
public class UserDetail : EntityBase<int>, IArchivable, IHasPhoto, IDeactivatableEntity, IUpdatable
{
public bool IsArchived { get; set; }
public int LastUpdatedById { get; set; }
public UserProfile LastUpdatedBy { get; set; }
public DateTimeOffset LastUpdatedDate { get; set; }
public string PhotoUri { get; set; }
public bool IsInactive { get; set; }
}
public abstract class UserBelongsToApartmentComplex : UserDetail, IBelongsToApartmentComplex
{
public int ApartmentComplexId { get; set; }
public virtual ApartmentComplex ApartmentComplex { get; set; }
public virtual bool IsInSameComplexAs(IRelatedToApartmentComplex otherEntity)
{
return ApartmentComplexId == otherEntity.ApartmentComplexId;
}
}
public class Staff : UserBelongsToApartmentComplex
{
public string Title { get; set; }
}
public class Admin : UserDetail
{
public string AccessLevel { get; set; }
}
public class Resident : UserBelongsToApartmentComplex
{
public string Pets { get; set; }
public bool HasInsurance { get; set; }
public virtual IList<Lease> Leases { get; set; }
public int? CurrentUnitId { get; set; }
public virtual Unit CurrentUnit { get; set; }
public Resident()
{
Leases = new List<Lease>();
}
}
//response data from sever from endpoint public IQueryable Residents()
[{"$id":"1","$type":"RadiusBlue.Core.Models.Resident, RadiusBlue.Core","Pets":"Sadie, a westie","HasInsurance":false,"Leases":[{"$id":"2","$type":"RadiusBlue.Core.Models.Lease, RadiusBlue.Core","Start":"2012-05-23T00:00:00.000","End":"2013-05-23T00:00:00.000","UnitId":2,"Unit":{"$id":"3","$type":"RadiusBlue.Core.Models.Unit, RadiusBlue.Core","Building":"B","Floor":2,"ModelName":"Tera","RentAmount":2500.00,"NumberOfBeds":1,"NumberOfBaths":3,"UnitName":"102A","IsInactive":true,"Inhabitants":[],"ApartmentComplexId":1,"ApartmentComplex":{"$id":"4","$type":"RadiusBlue.Core.Models.ApartmentComplex, RadiusBlue.Core","Name":"The Stratford","StreetAddress":"100 S Park Ave","City":"Winter Park","StateId":10,"ZipCode":"32792","PropertyManagementCompanyId":1,"IsInactive":false,"TimeZoneId":"Eastern Standard Time","TimeZone":{"$id":"5","$type":"System.TimeZoneInfo, mscorlib","Id":"Eastern Standard Time","DisplayName":"(UTC-05:00) Eastern Time (US & Canada)","StandardName":"Eastern Standard Time","DaylightName":"Eastern Daylight Time","BaseUtcOffset":"-PT5H","AdjustmentRules":[{"$id":"6","$type":"System.TimeZoneInfo+AdjustmentRule, mscorlib","DateStart":"0001-01-01T00:00:00.000","DateEnd":"2006-12-31T00:00:00.000","DaylightDelta":"PT1H","DaylightTransitionStart":{"$id":"7","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":4,"Week":1,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false},"DaylightTransitionEnd":{"$id":"8","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":10,"Week":5,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false}},{"$id":"9","$type":"System.TimeZoneInfo+AdjustmentRule, mscorlib","DateStart":"2007-01-01T00:00:00.000","DateEnd":"9999-12-31T00:00:00.000","DaylightDelta":"PT1H","DaylightTransitionStart":{"$id":"10","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":3,"Week":2,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false},"DaylightTransitionEnd":{"$id":"11","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":11,"Week":1,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false}}],"SupportsDaylightSavingTime":true},"Users":[{"$ref":"1"}],"Groups":[],"IsArchived":false,"ApartmentComplexId":1,"Id":1},"Id":2},"ResidentId":3,"Resident":{"$ref":"1"},"LeaseStatusId":4,"LeaseStatus":{"$id":"12","$type":"RadiusBlue.Core.Models.LeaseStatus, RadiusBlue.Core","Description":"Lost","Id":4},"Id":1},{"$id":"13","$type":"RadiusBlue.Core.Models.Lease, RadiusBlue.Core","Start":"2013-05-24T00:00:00.000","End":"2014-05-24T00:00:00.000","UnitId":1,"Unit":{"$id":"14","$type":"RadiusBlue.Core.Models.Unit, RadiusBlue.Core","Building":"A","Floor":2,"ModelName":"Aqua","RentAmount":2000.00,"NumberOfBeds":2,"NumberOfBaths":1,"UnitName":"101A","IsInactive":true,"Inhabitants":[{"$ref":"1"}],"ApartmentComplexId":1,"ApartmentComplex":{"$ref":"4"},"Id":1},"ResidentId":3,"Resident":{"$ref":"1"},"LeaseStatusId":1,"LeaseStatus":{"$id":"15","$type":"RadiusBlue.Core.Models.LeaseStatus, RadiusBlue.Core","Description":"Active","Id":1},"Id":2}],"CurrentUnitId":1,"CurrentUnit":{"$ref":"14"},"ApartmentComplexId":1,"ApartmentComplex":{"$ref":"4"},"Id":3,"User":{"$id":"16","$type":"RadiusBlue.Core.Models.UserProfile, RadiusBlue.Core","UserName":"vjiawon#gmail.com","FirstName":"Vishal","LastName":"Jiawon","Age":27,"PhoneNumber":"123 456 7890","IsInactive":false,"UserDetail":{"$ref":"1"},"GroupMembers":[],"MaintenanceRequests":[],"Id":3},"IsArchived":false,"LastUpdatedById":1,"LastUpdatedDate":"0001-01-01T00:00:00.000+00:00","IsInactive":false,"CreatedById":1,"CreatedDate":"0001-01-01T00:00:00.000+00:00"}]
I do not doubt that there is a bug in BreezeJS somewhere.
I can report that, at least as of v.1.3.4, Breeze can expand multiple navigation properties of a TPH class ... and not just on the first entity returned.
I just modified the "can navigate to AccountType eagerly loaded with expand" test in inheritanceTests.js in DocCode so that (a) it also expands the Status navigation and (b) the tests are performed on the 3rd entity returned rather than the 1st.
The query is something like this:
var em = newEm(); // clean, empty EntityManager
return EntityQuery.from('bankRootTPHs').take(3)
.expand('AccountType, Status'))
.using(em).execute().then(success).fail(handleFail);
...
function success(data) {
var entity = data.results[data.results.length-1]; // get the last one (the 3rd)
var type = data.query.entityType.shortName;
if (!entity) {
ok(false, "a query failed to return a single " + type);
}
// more tests
// I just set a breakpoint and inspected
// entity.accountType() and entity.status()
// Both returned the expected related entities
}
I see that both the related AccountType and the related Status are available from the entity.
So something else is wrong.
Questions about your Example
First I am compelled to observe that you have a lot of expands. I count 5 related entities. That can hurt performance. I know we're not talking about that but I'm calling it out.
Second, the super class UserDetail is concrete but the intermediate derived class UserBelongsToApartmentComplex is abstract. You have inheritance class hierarchies that go concrete/abstract/concrete. The queried type, Residents is one such class. And a class at every level maps to the "UserDetail" table, yes?
I'm pretty sure we didn't test for that scenario ... which is pretty uncommon. I wasn't even sure that worked! For now I have to take your word for it that EF allows such a construct.
It would seem that BreezeJS is confused about it. We'll take a look.
Related
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; }
Automapper, mapping single destination property as a concatenation of multiple source property
I have a situation where I need to map a single property as a combination of multiple source properties based on some conditions. Destination : public class Email { public Email() { EmailRecipient = new List<EmailRecipient>(); } public string Subject{get; set;} public string Body {get; set;} public virtual ICollection<EmailRecipient> EmailRecipient { get; set; } } public class EmailRecipient { public int EmaiId { get; set; } public string RecipientEmailAddress { get; set; } public int RecipientEmailTypeId { get; set; } public virtual Email Email { get; set; } } Source: public class EmailViewModel { public List<EmailRecipientViewModel> To { get; set; } public List<EmailRecipientViewModel> Cc { get; set; } public string Subject { get; set; } public string Body { get; set; } } public class EmailRecipientViewModel { public string RecipientEmailAddress { get; set; } } I want Mapper.Map<EmailViewModel,Email>() Here I would like to map my Email.EmailRecipient as a combination of EmailViewModel.To and EmailViewModel.Cc. However the condition is, Email.EmailRecipient.RecipientEmailTypeId will be 1 for To and 2 for Cc Hope my question is clear.
One possible way to achieve this is to create a map that uses a specific method for this conversion. The map creation would be: Mapper.CreateMap<EmailViewModel, Email>() .ForMember(e => e.EmailRecipient, opt => opt.MapFrom(v => JoinRecipients(v))); Where the JoinRecipients method would perform the conversion itself. A simple implementation could be something like: private ICollection<EmailRecipient> JoinRecipients(EmailViewModel viewModel) { List<EmailRecipient> result = new List<EmailRecipient>(); foreach (var toRecipient in viewModel.To) { result.Add(new EmailRecipient { RecipientEmailTypeId = 1, RecipientEmailAddress = toRecipient.RecipientEmailAddress }); } foreach (var ccRecipient in viewModel.Cc) { result.Add(new EmailRecipient { RecipientEmailTypeId = 2, RecipientEmailAddress = ccRecipient.RecipientEmailAddress }); } return result; }
I'm a huge opponent of converters, mostly because for other people in your project, things will just happen 'like magic' after the mapping call. An easier way of handling this would be to implement the property as a method that converts other properties on the viewmodel to the required formatting. Example: public class EmailViewModel { public ICollection<EmailRecipient> EmailRecipient { get { return To.Union(Cc); } } public List<EmailRecipientViewModel> To { get; set; } public List<EmailRecipientViewModel> Cc { get; set; } public string Subject { get; set; } public string Body { get; set; } } Now automapper automatically maps from EmailRecipient property to EmailRecipient property, and if someone is trying to figure out how it happens, they just need to look on the viewmodel. Editing this some years later: Just as a warning, doing things this way means that every time you call EmailRecipient, you incur the o(n) task of unioning the To and Cc fields. This is fine if you're only dealing with one email, but if you're reusing the viewmodel and someone sticks it in a loop with say, every other email in the system, it might be a huge performance issue. In that case I'd go with the accepted answer so that you dodge this potential performance pitfall.
DDD Service Method Granularity
So, I'm building a system for managing contacts. My contact domain model has quite a few string properties, as well as booleans. In the spirit of keeping behavior inside of the domain models, I've gone down the path of creating "update methods." I'm starting to feel like it's getting a bit burdensome. In the past, CRUD apps would just have a single update method and it would set all of the properties in one shot. Am I on the right path? I'm concerned about having 10 - 15 update methods on my domain service and domain entities. FYI, the example given is a bit contrived, so imagine a model with lots of string and boolean properties. // Application Layer Stuff public class UpdateContactCommand { public UpdateNamePredicate UpdateName { get; set; } public UpdatePhonePredicate UpdatePhone { get; set; } public int ContactId { get; set; } } public class UpdateNamePredicate { public string NewFirstName { get; set; } public string NewLastName { get; set; } } public class UpdatePhonePredicate { public string NewPHone { get; set; } } public class UpdateContactResponse { public bool Success { get; set; } public string Message { get; set; } } public interface IWcfService { UpdateContactResponse UpdateContact(UpdateContactCommand updateContactCommand); } public class WcfService : IWcfService { private readonly IContactService _contactService; public WcfService(IContactService contactService) { _contactService = contactService; } public UpdateContactResponse UpdateContact(UpdateContactCommand updateContactCommand) { if (updateContactCommand.UpdateName != null) { _contactService.UpdateName(updateContactCommand.ContactId, updateContactCommand.UpdateName.NewFirstName, updateContactCommand.UpdateName.NewLastName); } if (updateContactCommand.UpdatePhone != null) { _contactService.UpdatePhone(updateContactCommand.ContactId, updateContactCommand.UpdatePhone.NewPHone); } return new UpdateContactResponse(); } } // Domain Layer public interface IContactService { // There are lots more of these void UpdateName(int contactId, string newFirstName, string newLastName); void UpdatePhone(int contactId, string newPhone); } public class ContactService : IContactService { private readonly IContactRepository _contactRepository; public ContactService(IContactRepository contactRepository) { _contactRepository = contactRepository; } public void UpdateName(int contactId, string newFirstName, string newLastName) { var contact = _contactRepository.GetById(contactId); contact.SetName(newFirstName, newLastName); _contactRepository.Commit(); } public void UpdatePhone(int contactId, string newPhone) { var contact = _contactRepository.GetById(contactId); contact.SetPhone(newPhone); _contactRepository.Commit(); } } public interface IContact { int Id { get; set; } // There are lots more of these void SetName(string newFirstName, string newLastName); void SetPhone(string newPhone); } public class Contact : IContact { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Phone { get; set; } public void SetName(string newFirstName, string newLastName) { FirstName = newFirstName; LastName = newLastName; } public void SetPhone(string newPhone) { Phone = newPhone; } } public interface IContactRepository { IContact GetById(int id); void Commit(); } public class ContactRepository : IContactRepository { public IContact GetById(int id) { // Not important throw new NotImplementedException(); } public void Commit() { // Not important throw new NotImplementedException(); } }
First of all, not all applications lend themselves well to a DDD approach. If you say your application could pretty much have been implemented in a CRUDish way before, chances are it's still CRUD now. Don't try to apply DDD on any app because it's the shiny new thing. That being said, you don't just write "update methods" for the fun of it. They have to reflect the domain tasks your user wants to perform. Why does the user want to update a Contact ? Has the contact moved or just changed phone number ? Changed marital status and name ? Has the point of contact in a company been taken over by another employee ? Usually, you won't have tons of update methods for a given entity. There's always a way to group changes in operations that are meaningful for the domain. Good ways to force yourself to do it are : Think about the maximum number of form fields you can reasonably display to the user. Couldn't you split that complex UI into smaller, more meaningful screens ? From there you have to start reasoning (preferably with the help of a domain expert) about the tasks these should reflect. Make your entity fields immutable from the outside. Thus you'll have to think harder about their true nature -- what should be in the constructor ? what should some other manipulation methods be ?
JSON.NET Object Deserialisation with class
I am using these classes: public class MasteryPages { internal MasteryPages() { } [JsonProperty("pages")] public List<MasteryPage> Pages { get; set; } [JsonProperty("summonerId")] public long SummonerId { get; set; } } [Serializable] public class MasteryPage { internal MasteryPage() { } [JsonProperty("current")] public bool Current { get; set; } [JsonProperty("id")] public long Id { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("talents")] public List<Talent> Talents { get; set; } } [Serializable] public class Talent { internal Talent() { } [JsonProperty("id")] public int Id { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("rank")] public int Rank { get; set; } } This is the code I'm using to deserialise the object //MASTERIES var jsonMasteries = requester.CreateRequest(string.Format(RootUrl, Region) + string.Format(MasteriesUrl, summonerId)); var objAllMasteryPages = JsonConvert.DeserializeObject<MasteryPages>(jsonMasteries); The jsonMasteries object is correctly serialized and gives me this: http://pastebin.com/3dkdDHdx (Rather large, to view easily: go to http://www.jsoneditoronline.org/ and paste it) The second line is giving me troubles however. Normally my object should be filled with the data. It unfortunately isn't and I have no idea what's wrong. Anyone could help me out?
Your problem is in this part of serialized JSON: "42177333": { ... } As I understand - this is some kind of ID and it's dynamic. Possible solutions are: One of possible resolutions is here: C# deserialize dynamic JSON Cut this part of dynamic JSON. Try to modify the serialization stuff to avoid this dynamic ID.
Thanks to sleepwalker I saw what was wrong. (Dynamic Id (number), first line) Now, the James Newtonking JSON library has a solution for dynamic id's like this. I edited my code to this: var jsonMasteries = requester.CreateRequest(string.Format(RootUrl, Region) + string.Format(MasteriesUrl, summonerId)); var objAllMasteriePages = JsonConvert.DeserializeObject<Dictionary<long, MasteryPages>>(jsonMasteries).Values.FirstOrDefault().Pages; (First line stays the same, the magic is in the second line) Now, i use a dictionary with the key being my given Id, and my custom class. This works wonders
DDD/CQRS, Entity has access to Query, Command?
public class PageRoleService { public void SetRoles(Page page, User activeUser) { var rb = page.Project.ProjectType.GetRoleFor(activeUser.UserType); page.RolesForPage.Add(activeUser, rb); var managers = GetAllManagersOf(activeUser); foreach (var m in managers) { page.RolesForPage.Add(m, rb); } } } public class Project : Entity { public ProjectType ProjectType { get; set; } public IList<Page> Pages { get; set; } } public class Page : Entity { public string Name { get; set; } public Project Project { get; set; } public IDictionary<User, RoleBehaviour> RolesForPage { get; set; } } public class ProjectType : Entity { public IQueryProcessor QueryProcessor { get; set; } public IList<RoleBehaviour> RoleBehaviours { get; set; } public RoleBehaviour GetRoleFor(USerType userType) { var behaviour = return QueryProcessor.Execute(new GetRolesByUserAndProjectTypeQuery() { ProjectType = this, UserType = userType }); // Filter behaviour attributes for project type properties, business rules, etc... // FilterBehaviour(behaviour); return behaviour; } } public class GetRolesByUserAndProjectTypeQuery { public UserType UserType { get; set; } public ProjectType ProjectType { get; set; } } public class GetRolesByUserAndProjectTypeQueryHandler { public Db Db { get; set; } public RoleBehaviour Execute(GetRolesByUserAndProjectTypeQuery query) { return Db.FirstOrDefault(r => r.UserType == query.UserType && r.ProjectType == query.projectType); } } public class RoleBehaviour : Entity { public Role ROleForArea1 { get; set; } public Role ROleForArea2 { get; set; } public UserType UserType { get; set; } public ProjectType ProjectType { get; set; } public IDictionary<string, string> Attributes { get; set; } } public enum UserType { A, B, C, D } public class Role : Entity { public IList<string> Permissions { get; set; } } I don't use repository, no need data abstraction, I use CQRS for crud operations. (CreateProjectCommand, GetRolesByUserAndProjectTypeQuery, etc..) Users related a lot of project and page. Users have more than role for each Page Entity and is dynamically created when user (client) request to fetch All projects page or single page item. My Page Role Service determinates page roles for active user and its managers. My MVC Controller use PageRoleService. PageRoleService is Application Service or Domain Service or .....? QueryProcessor in Entity (ProjectType) is invalid approach? How can handle this/their problems without lazy or eager loading? RoleBehaviour is Entity or Value Object? PageRoleService is a service or business logic in domain?
I know that I'm some years later, but: I would put away the base class Entity, because it looks that this are just Dtos returned by the queryhandler (infact GetRolesByUserAndProjectTypeQueryHandler.Execute returns a RoleBehaviour). Given this, I think that: PageRoleService is a simple service that completes a Dto, hence it looks a kind of factory Given that ProjectType here has two different roles (a Dto and Entity, and this is against CQRS), if: it's a Dto, then use a service/factory/ORM to load extra data on it it's an Entity, try to load all the data that's needed by it. This because there're great changes that you'll need it on the way to execute your command (great explanation about DDD and entities). The object has it's own identity? Has it an Id that, even if things will change, remains the same? Looking at it, it looks just a Dto, with nothing really interesting (at business level). see 1.