DTO design supporting multiple tables
-
I'm looking for some feedback on the best/alternate ways to handle the following scenario: Widget (defines the pool from which to create widgets) ======== Id UserControlPath DefaultWidget (provides a set of default properties for a widget instance) ========== Id WidgetId Name UserWidget (defines a widget with properties customized by the user) =========== Id WidgetId DefaultWidgetId Name Given the 3 database tables above, how would you create a DTO object? I could have a class per table, but then I have to do additional queries or work with additional objects for all the data I need. I could use nested object (UserWidget has Widget.UserControlPath property). I could flatten out the UserWidget to have those properties plus a UserControlPath property. When loading a UserWidget, I cannot display it without the UserControlPath value. When saving updates to the properties of a UserWidget, the UserControlPath is read only - but WCF data contracts do not support the idea of read-only properties(As far as I know). Saving full object graphs can quickly become complex. How do you model similar relationships? Thx.
-
I'm looking for some feedback on the best/alternate ways to handle the following scenario: Widget (defines the pool from which to create widgets) ======== Id UserControlPath DefaultWidget (provides a set of default properties for a widget instance) ========== Id WidgetId Name UserWidget (defines a widget with properties customized by the user) =========== Id WidgetId DefaultWidgetId Name Given the 3 database tables above, how would you create a DTO object? I could have a class per table, but then I have to do additional queries or work with additional objects for all the data I need. I could use nested object (UserWidget has Widget.UserControlPath property). I could flatten out the UserWidget to have those properties plus a UserControlPath property. When loading a UserWidget, I cannot display it without the UserControlPath value. When saving updates to the properties of a UserWidget, the UserControlPath is read only - but WCF data contracts do not support the idea of read-only properties(As far as I know). Saving full object graphs can quickly become complex. How do you model similar relationships? Thx.
Leftyfarrell wrote: I could have a class per table I think this is a bad idea. Your tables are designed so data is normalized. Your code--classes--should be designed to normalize behavior not data. Think of your problem domain, jot down all things you need to do to these objects, assign these things to appropriate classes. Try to normalize behavior by using inheritance. That is all I can say because I don't know too much about what you are trying to do.
CodingYoshi Artificial Intelligence is no match for Human Stupidity.
-
Leftyfarrell wrote: I could have a class per table I think this is a bad idea. Your tables are designed so data is normalized. Your code--classes--should be designed to normalize behavior not data. Think of your problem domain, jot down all things you need to do to these objects, assign these things to appropriate classes. Try to normalize behavior by using inheritance. That is all I can say because I don't know too much about what you are trying to do.
CodingYoshi Artificial Intelligence is no match for Human Stupidity.
Thanks for your reply. I agree that I don't like the class per table idea. Tables are normalized. Classes should represent proper domain objects. The question then becomes, where do you draw the line on CRUD operations. To me, a UserWidget without the UserControlPath property is kind of useless. On the other hand, when you save a new UserWidget, you cannot edit/change the UserControlPath property value. You could consider it a lookup and allow them to change the WidgetId (indirectly choosing a different UserControlPath). But, if the UserWidget is exposed via WCF, read-only properties are not supported. So I can expose the WidgetId, which is editable, but if I provide a string UserControlPath property, it becomes editable on a WCF data contract, which I don't want. So they load up a UserWidget object, change the UserControlPath string value, and submit the object for saving... now what? Ignore the property value? Allow the saving of this instance to change the value for all other instances? Neither sounds very elegant.
-
Thanks for your reply. I agree that I don't like the class per table idea. Tables are normalized. Classes should represent proper domain objects. The question then becomes, where do you draw the line on CRUD operations. To me, a UserWidget without the UserControlPath property is kind of useless. On the other hand, when you save a new UserWidget, you cannot edit/change the UserControlPath property value. You could consider it a lookup and allow them to change the WidgetId (indirectly choosing a different UserControlPath). But, if the UserWidget is exposed via WCF, read-only properties are not supported. So I can expose the WidgetId, which is editable, but if I provide a string UserControlPath property, it becomes editable on a WCF data contract, which I don't want. So they load up a UserWidget object, change the UserControlPath string value, and submit the object for saving... now what? Ignore the property value? Allow the saving of this instance to change the value for all other instances? Neither sounds very elegant.
Do something like the following the following--private setter: [DataMember()] partial public class Foo { public string FooBar { get {;} } { private set { ; } } } Leftyfarrell wrote: The question then becomes, where do you draw the line on CRUD operations. What do you mean draw the line?
CodingYoshi Artificial Intelligence is no match for Human Stupidity.
-
Do something like the following the following--private setter: [DataMember()] partial public class Foo { public string FooBar { get {;} } { private set { ; } } } Leftyfarrell wrote: The question then becomes, where do you draw the line on CRUD operations. What do you mean draw the line?
CodingYoshi Artificial Intelligence is no match for Human Stupidity.
Thx, will check into that. By draw the line, I mean with nested objects in an object graph. Not really shown in my object/table example above. But for instance say you have a Course, Session, and Location. A course has multiple sessions, and each session has a location. Does your Course object have a Collection objects for reading convenience? What about CRUD on the Course, where do you "draw the line"... if you add a Session to the collection, do you now save the Course AND insert a new Session? If the Session has a Location within it, and you add a new Location to a new Session and add that to a Course and save the course, do you Insert the Session and the Location? It seems to me that dealing with object graphs for CRUD operations can get complex quickly. But having those properties does properly represent the domain model, and certainly does improve the way you work with the data when only reading it (loading Course, Session and Location info for display purposes). Then for performance reasons, you might get into lazy loading, although that may not be feasible either across WCF boundaries. It seems all about the trade-offs and I'm just looking for the right balance that makes sense.
-
Thx, will check into that. By draw the line, I mean with nested objects in an object graph. Not really shown in my object/table example above. But for instance say you have a Course, Session, and Location. A course has multiple sessions, and each session has a location. Does your Course object have a Collection objects for reading convenience? What about CRUD on the Course, where do you "draw the line"... if you add a Session to the collection, do you now save the Course AND insert a new Session? If the Session has a Location within it, and you add a new Location to a new Session and add that to a Course and save the course, do you Insert the Session and the Location? It seems to me that dealing with object graphs for CRUD operations can get complex quickly. But having those properties does properly represent the domain model, and certainly does improve the way you work with the data when only reading it (loading Course, Session and Location info for display purposes). Then for performance reasons, you might get into lazy loading, although that may not be feasible either across WCF boundaries. It seems all about the trade-offs and I'm just looking for the right balance that makes sense.
Okay I know what you mean. It all depends on the requirements. For example, let's say you are managing customers, their orders, and lineitems. Now the questions to ask are: 1. Within my problem domain, do I want to create LineItems or do I only select from LineItems and add to order? If you only select then LineItem will not have any CRUD operations except Retrieval. 2. What about Customers? Will I be adding new customers who have absolutely no orders into the system? Chances are yes, therefore, therefore their might be times you want to load a customer to edit their information. It does not make sense when you call Retrieve (R from CRUD) on customer to also load their orders, and all the line items for the order if all you need to do is change the customer's name from Bob to Rob. But if within your domain you never do any CRUD operations except Retrieve on customers (customers are added using some other app), and the only time you retrieve them is to load their orders then it make sense to retrieve all orders and lineitems when a customer is loaded. Where to draw the line? You choose by looking at the problem at hand.
CodingYoshi Artificial Intelligence is no match for Human Stupidity.
-
Okay I know what you mean. It all depends on the requirements. For example, let's say you are managing customers, their orders, and lineitems. Now the questions to ask are: 1. Within my problem domain, do I want to create LineItems or do I only select from LineItems and add to order? If you only select then LineItem will not have any CRUD operations except Retrieval. 2. What about Customers? Will I be adding new customers who have absolutely no orders into the system? Chances are yes, therefore, therefore their might be times you want to load a customer to edit their information. It does not make sense when you call Retrieve (R from CRUD) on customer to also load their orders, and all the line items for the order if all you need to do is change the customer's name from Bob to Rob. But if within your domain you never do any CRUD operations except Retrieve on customers (customers are added using some other app), and the only time you retrieve them is to load their orders then it make sense to retrieve all orders and lineitems when a customer is loaded. Where to draw the line? You choose by looking at the problem at hand.
CodingYoshi Artificial Intelligence is no match for Human Stupidity.
That is a good analysis of some sample requirements. The next complexity that comes to mind is definition of the entities to support both scenarios. Admin area allows you create a customer. User area allows viewing the Customer, together with Orders and LineItems. I assume the same Customer entity is used for both areas of the system. Now in the admin area when creating a customer, you simply have a Collection that is null. This keeps things fairly simple. If it was not null, would you design your system to handle it (insert Orders too) or would you throw an exception and force things to remain simple for CRUD operations beyond reads?
-
That is a good analysis of some sample requirements. The next complexity that comes to mind is definition of the entities to support both scenarios. Admin area allows you create a customer. User area allows viewing the Customer, together with Orders and LineItems. I assume the same Customer entity is used for both areas of the system. Now in the admin area when creating a customer, you simply have a Collection that is null. This keeps things fairly simple. If it was not null, would you design your system to handle it (insert Orders too) or would you throw an exception and force things to remain simple for CRUD operations beyond reads?
A Customer should have a collection of Orders, and Orders should have a collection of LineItems. As soon as you create a customer, you know you need orders, therefore, instantiate a collection of orders. Do not leave it null but leave it empty and then orders can be added. If orders are not allowed to be added then throw exception or handle it some other way. Leftyfarrell wrote: you simply have a Collection that is null. Like I said above this should be empty but not null--at least this is how I will design it. When a customer enters a store, he/she grabs a shopping cart but it is empty and then if he/she needs something she adds it. If he/she does not grab a shopping cart then what? Someone else has to grab it--I don't know about this.
CodingYoshi Artificial Intelligence is no match for Human Stupidity.