Data Entities > Domain Objects > ViewModels, each

2019-02-21 01:16发布

问题:

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:

  • UserProfileModel

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!

回答1:

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.