This is sort of a generic question in regards to mapping between data entities, domain objects, and ViewModels. I may not be asking it right but hopefully I can make some sense of it. Below is a simplified problem.
Pretend I have an Entity Framework model which maps 1:1 to my database tables, but my domain objects may not be identical, and my ViewModel is drastically different again. As a pseudo-example:
Database/EF Entities:
- MembershipAccount
- MembershipAccountExtraInfo
Domain:
- Account
- Profile
- Preferences
ViewModel:
Let's say I need to display a UserProfileModel which has: Username (from MembershipAccount), SignupDate (from MembershipAccount), FullName (from MembershipAccountExtraInfo), and TimeZone (from MembershipAccountExtraInfo)
What sort of relationships might I need here, and what sort of mapping mechanisms? Is it common to have something like an AccountMapper that takes both a MembershipAccount and MembershipAccountExtraInfo and returns an Account? I'm a bit stuck on the mapping when several objects are needed to create a single domain entity, and vice versa.
If it helps: I'm designing an API for managing User Accounts, User Profiles, User Preferences, etc. but the database tables are all over the place. A single User Profile might need to be created from data spanning 4-5 tables and 2 databases. There is no 1:1 mapping between my database tables and any (logical) domain objects.
Thanks!
I like to work keeping my domain objects as close to the objects that they represent as possible. What I mean by this is that if an account has preferences, then the domain Account
object should contain a Preferences
property, most likely represented by a collection of Preference
objects. If nothing else, this helps the users understand the data structure of the application easily.
As for constructing the view models, that's the easiest bit... you add just properties for anything that is required. What types of properties you would need would really depend on how you have structured your domain objects.
If your view has the requirements that you mentioned in your question and you modelled your domain objects closely on the objects that they represent, then by the sounds of it, you would just need an Account
object because that would contain the Preference
and Profile
objects inside it.
Finally, the only 'mapping' that needs to be done can be done with a LinQ
query using the Entity Framework. It is at this point that I join the tables and pull whatever data that I need for whichever object I am working on. Here is an example of instantiating objects from data from three tables (using LinQ2SQL
):
public AudioTracks GetAudioTracks(AudioTrackSearchOptions searchOptions)
{
AudioTracks audioTracks;
using (MidasDataContext dataContext = DataContext)
{
audioTracks = new AudioTracks(
from audioTrack in dataContext.DbAudioTracks
join masterTrack in dataContext.DbMasterTracks on audioTrack.MasterTrackId equals masterTrack.Id
join masterTrackArtist in dataContext.DbDataLists on masterTrack.ArtistId equals masterTrackArtist.Id
orderby string.Concat(masterTrack.Title, " (", audioTrack.Mix, ") - ", masterTrackArtist.Text)
where (searchOptions.IsInactiveAudioTrackIncluded || audioTrack.IsActive)
&& (searchOptions.IsDeletedAudioTrackIncluded || !audioTrack.IsDeleted)
select new AudioTrack(audioTrack.Id, masterTrack.Id, audioTrack.Isrc, masterTrack.Title, masterTrackArtist.Text, audioTrack.Mix, audioTrack.IsContentExplicit, audioTrack.IsActive, audioTrack.IsDeleted));
}
audioTracks.Sort(a => a.TitleWithMix);
return audioTracks ?? new AudioTracks();
}
UPDATE >>>
Extending my AudioTracks
example and working backwards, the GetAudioTracks
method is in a project called DataProviders
. It is called from a GetAudioTracks
method in a DataController
class which just adds user feedback and re-try options. That in turn is called by a TracksModel
in the Models
project which just contains a subsection of methods from the DataController
class that relate to the various types of tracks in the application.
Finally, the AudioTracksViewModel
in the ViewModels
project calls the TracksModel.GetAudioTracks
method upon initialisation which happens when the AudioTracksView
is loaded by the user. The AudioTracksView
has a ListBox
on the left containing all of the AudioTrack
objects that meet the users search and/or filter selections. The right of the screen has the fields for the selected AudioTrack
. Here is what it looks like (if the link seems broken, you can view the image here):
The more transparent fields with an edit Button
on the right are read only fields connected to collections. The edit Button
opens a dialog to let the user enter multiple items, which are then summarised in the field. All of the objects in the application have similar views of more or less complexity.