Structure of Classes in a Scaffolded Application

  • 9 minutes to read

This topic describes the basic structure of generated application Class diagrams and code snippets taken from the https://www.devexpress.com/example=T322553 example. This example is based on the Data Access Layer generated with the Entity Framework Code First. You can download this example or instead create it yourself by completing the following tutorial: UI Generation.

This topic consists of the following sections.

Data Model Layer

The data structure used in the T322553 example is shown below.

scaff_tut1

Two crucial concepts in the Data Model are UnitOfWork and Repository.

UnitOfWork is a transaction. It provides access to Repositories and tracks any changes made to those Repositories.

Repository is a collection of entities of a particular type and is always owned by the UnitOfWork that created it. Any changes made to a Repository can be committed to the database using the associated UnitOfWork.

The UnitOfWork concept is represented by the IUnitOfWork interface. Each scaffolded application contains a IUnitOfWork descendant that contains repositories specific to the application. In the example, it is the IDepartmentContextUnitOfWork interface.

There are two implementations of the specialized IUnitOfWork interface in a generated application. One is used at run-time and the other-at design-time. In the example, these implementations are the DepartmentContextUnitOfWork and DepartmentContextDesignTimeUnitOfWork classes.

The Repository concept is represented by the IRepository and IReadOnlyRepository interfaces. No concrete implementations of these interfaces are generated. Instead, the two generated IUnitOfWork implementations create instances of the IRepository or IReadOnlyRepository interfaces.

To acquire an IUnitOfWork instance, an instance of the IUnitOfWorkFactory interface is needed first. Such an instance can be obtained with the UnitOfWorkSource.GetUnitOfWorkFactory() method. The UnitOfWorkSource class is generated by the Scaffolding Wizard and creates an instance of either the run-time implementation of the IUnitOfWork or design-time implementation.

View Model Layer

All view models generated with the Scaffolding Wizard are POCO classes.

There are two types of view models generated by the Scaffolding Wizard:

  • collection view models that allow you to operate with collections of entities of a specific type;
  • single object view models that allow you to operate with a single entity of a specific type.

Below is the collection view models hierarchy.

scaff_tut22

ReadOnlyCollectionViewModelBase is the base class for view models exposing a read-only collection of entities of a given type using the Entities property. The SelectedItem property can be used to synchronize selected items with the view (the GridControl.SelectedItem property in the generated application). The FilterExpression property can be used to filter loaded data. The Refresh method recreates the underlying unit of work and reloads data. Since CollectionViewModelBase is a POCO view model, an instance of this class will also expose the RefreshCommand property that can be used as a binding source in views.

ReadOnlyCollectionViewModel is an empty partial class that inherits ReadOnlyCollectionViewModelBase and provides the extension point to add custom properties, commands and override methods without modifying the auto-generated code.

CollectionViewModelBase adds Delete, Edit, and Save methods to the ReadOnlyCollectionViewModelBase. These methods will be exposed as commands at runtime (the DeleteCommand, the EditCommand, and the SaveCommand).

CollectionViewModel is an empty partial class that inherits the CollectionViewModelBase class.

The CourseCollectionViewModel, DepartmentCollectionViewModel and EmployeeCollectionViewModel classes represent view models used to operate with collections of specific domain objects. Since all logic is implemented in the base classes, these view models are quite simple.

/// <summary>
    /// Represents the Courses collection view model.
    /// </summary>
    public partial class CourseCollectionViewModel : CollectionViewModel<Course, int, IDepartmentContextUnitOfWork> {

        /// <summary>
        /// Creates a new instance of CourseCollectionViewModel as a POCO view model.
        /// </summary>
        /// <param name="unitOfWorkFactory">A factory used to create a unit of work instance.</param>
        public static CourseCollectionViewModel Create(IUnitOfWorkFactory<IDepartmentContextUnitOfWork> unitOfWorkFactory = null) {
            return ViewModelSource.Create(() => new CourseCollectionViewModel(unitOfWorkFactory));
        }

        /// <summary>
        /// Initializes a new instance of the CourseCollectionViewModel class.
        /// This constructor is declared protected to avoid undesired instantiation of the CourseCollectionViewModel type without the POCO proxy factory.
        /// </summary>
        /// <param name="unitOfWorkFactory">A factory used to create a unit of work instance.</param>
        protected CourseCollectionViewModel(IUnitOfWorkFactory<IDepartmentContextUnitOfWork> unitOfWorkFactory = null)
            : base(unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(), x => x.Courses) {
        }
    }

The single object view models hierarchy is shown below.

scaff_tut23

SingleObjectViewModelBase is the base class exposing a single entity (the Entity property) and CRUD operations against this entity. The Close, Delete, Save methods will be exposed as commands at runtime (CloseCommand, EditCommand, SaveCommand).

SingleObjectViewModel is an empty partial class that inherits SingleObjectViewModelBase.

CourseViewModel, DepartmentViewModel and EmployeeViewModel represent view models used to operate with specific domain objects. CourseViewModel and EmployeeViewModel expose the LookUpDepartments property that provides a look-up collection of all departments that is the items source for the Grid Lookup Editor used to edit the Employee.Department and Course.Department properties in the UI. DepartmentViewModel exposes two nested collection view models (DepartmentCoursesLookUp and DepartmentEmployeesLookUp) used to represent the Department's detail collections (Department.Courses, Department.Employees) in the UI. The code of these view models is below.

/// <summary>
    /// Represents the single Course object view model.
    /// </summary>
    public partial class CourseViewModel : SingleObjectViewModel<Course, int, IDepartmentContextUnitOfWork> {

        /// <summary>
        /// Creates a new instance of CourseViewModel as a POCO view model.
        /// </summary>
        /// <param name="unitOfWorkFactory">A factory used to create a unit of work instance.</param>
        public static CourseViewModel Create(IUnitOfWorkFactory<IDepartmentContextUnitOfWork> unitOfWorkFactory = null) {
            return ViewModelSource.Create(() => new CourseViewModel(unitOfWorkFactory));
        }

        /// <summary>
        /// Initializes a new instance of the CourseViewModel class.
        /// This constructor is declared protected to avoid undesired instantiation of the CourseViewModel type without the POCO proxy factory.
        /// </summary>
        /// <param name="unitOfWorkFactory">A factory used to create a unit of work instance.</param>
        protected CourseViewModel(IUnitOfWorkFactory<IDepartmentContextUnitOfWork> unitOfWorkFactory = null)
            : base(unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(), x => x.Courses, x => x.Title) {
        }


        /// <summary>
        /// The view model that contains a look-up collection of Departments for the corresponding navigation property in the view.
        /// </summary>
        public IEntitiesViewModel<Department> LookUpDepartments
        {
            get { return GetLookUpEntitiesViewModel((CourseViewModel x) => x.LookUpDepartments, x => x.Departments); }
        }
    }

DepartmentContextViewModel is located in the root of the project. It provides an entry point view model of the generated application and uses IDocumentManagerService to create collection views.

View Layer

View Layer consists of collection views (CourseCollectionView, DepartmentCollectionView and EmployeeCollectionView) bound to collection view models and single object views (CourseView, DepartmentView, EmployeeView) bound to single object view models.

POCO view models are created using ViewModelSourceExtension in XAML.

<UserControl x:Class="DevExpressWalkthrough.Views.CourseView"
                  xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
                  DataContext="{dxmvvm:ViewModelSource viewmodel:CourseViewModel}" ...>

View model commands are represented in the UI by the RibbonControl that automatically merges with the ribbon in DepartmentContextView at runtime.

<dxr:RibbonControl RibbonStyle="Office2010" DockPanel.Dock="Top">
    <dxr:RibbonDefaultPageCategory Caption="defaultCategory">
        <dxr:RibbonPage Caption="Home">
            <dxr:RibbonPageGroup Caption="Course Tasks">
                <dxb:BarButtonItem Content="Save" Command="{Binding SaveCommand}" LargeGlyph="{dx:DXImage Image=Save_32x32.png}" Glyph="{dx:DXImage Image=Save_16x16.png}" />
                <dxb:BarButtonItem Content="Delete" Command="{Binding DeleteCommand}" LargeGlyph="{dx:DXImage Image=Delete_32x32.png}" Glyph="{dx:DXImage Image=Delete_16x16.png}" />
                <dxb:BarButtonItem Content="Close" Command="{Binding CloseCommand}" LargeGlyph="{dx:DXImage Image=Close_32x32.png}" Glyph="{dx:DXImage Image=Close_16x16.png}" />
            </dxr:RibbonPageGroup>
        </dxr:RibbonPage>
    </dxr:RibbonDefaultPageCategory>
</dxr:RibbonControl>

The entity detail editing form is represented by the LayoutControl.

<dxlc:LayoutControl Orientation="Vertical">
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand Event="{x:Static Binding.SourceUpdatedEvent}" Command="{Binding UpdateCommand}" />
    </dxmvvm:Interaction.Behaviors>
    <dxlc:LayoutItem Label="Course ID">
        <dxe:TextEdit EditValue="{Binding Entity.CourseID, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnSourceUpdated=True}" MaskType="Numeric" Mask="d" MaskUseAsDisplayFormat="True" HorizontalContentAlignment="Right" />
    </dxlc:LayoutItem>
    <dxlc:LayoutItem Label="Title">
        <dxe:TextEdit EditValue="{Binding Entity.Title, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnSourceUpdated=True}" />
    </dxlc:LayoutItem>
    <dxlc:LayoutItem Label="Department">
        <dxg:LookUpEdit EditValue="{Binding Entity.Department, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnSourceUpdated=True}" ItemsSource="{Binding LookUpDepartments}" DisplayMember="Name" IsTextEditable="False" />
    </dxlc:LayoutItem>
</dxlc:LayoutControl>

The collection view contains a GridControl that allows you to group, sort, filter and search through the data.

<dxg:GridControl Name="gridControl" ItemsSource="{Binding Entities}" ShowBorder="False" SelectedItem="{Binding SelectedEntity}">
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand PassEventArgsToCommand="True" Command="{Binding EditCommand}" EventName="MouseDoubleClick">
            <dxmvvm:EventToCommand.EventArgsConverter>
                <dx:EventArgsToDataRowConverter />
            </dxmvvm:EventToCommand.EventArgsConverter>
        </dxmvvm:EventToCommand>
    </dxmvvm:Interaction.Behaviors>
    <dxg:GridControl.Columns>
        <dxg:GridColumn IsSmart="True" FieldName="CourseID" />
        <dxg:GridColumn IsSmart="True" FieldName="Title" />
        <dxg:GridColumn FieldName="Department.Name" Header="Department" ReadOnly="True" />
    </dxg:GridControl.Columns>
    <dxg:GridControl.TotalSummary>
        <dxg:GridSummaryItem SummaryType="Count" Alignment="Right" />
    </dxg:GridControl.TotalSummary>
    <dxg:GridControl.GroupSummary>
        <dxg:GridSummaryItem SummaryType="Count" />
    </dxg:GridControl.GroupSummary>
    <dxg:GridControl.View>
        <dxg:TableView Name="tableView" AllowEditing="False" ShowFixedTotalSummary="True" AllowPerPixelScrolling="True">
        </dxg:TableView>
    </dxg:GridControl.View>
</dxg:GridControl>

Note that the GridControl has the EventToCommand behavior attached. It executes the CollectionViewModel.EditCommand when the user double clicks a data row.

The DepartmentContextView is located in the root of the project and provides an entry point view that contains a RibbonControl, a Tabbed MDI Container and a Navigation Pane forming the Microsoft Office-inspired UI of the application. See the Functionality Overview topic for more details.

sc-func-01