Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm designing a 3-tiered application using ASP.NET MVC 4. I used the following resources as a reference.

I have the following desingn so far.

Presentation Layer (PL) (main MVC project, where M of MVC was moved to Data Access Layer):

MyProjectName.Main
    Views/
    Controllers/
    ...

Business Logic Layer (BLL):

MyProjectName.BLL
    ViewModels/
    ProjectServices/
    ...

Data Access Layer (DAL):

MyProjectName.DAL
    Models/
    Repositories.EF/
    Repositories.Dapper/
    ...

Now, PL references BLL and BLL references DAL. This way lower layer does not depend on the one above it.

In this design PL invokes a service of the BLL. PL can pass a View Model to BLL and BLL can pass a View Model back to PL.

Also, BLL invokes DAL layer and DAL layer can return a Model back to BLL. BLL can in turn build a View Model and return it to PL.

Up to now this pattern was working for me. However, I've ran into a problem where some of my ViewModels require joins on several entities. In the plain MVC approach, in the controller I used a LINQ query to do joins and then select new MyViewModel(){ ... }. But now, in the DAL I do not have access to where ViewModels are defined (in the BLL).

This means I cannot do joins in DAL and return it to BLL. It seems I have to do separate queries in DAL (instead of joins in one query) and BLL would then use the result of these to build a ViewModel. This is very inconvenient, but I don't think I should be exposing DAL to ViewModels.

Any ideas how I can solve this dilemma? Thanks.

share|improve this question
add comment

2 Answers 2

up vote 10 down vote accepted

main MVC project, where M of MVC was moved to Data Access Layer

Common misconception. The M of MVC has nothing to do with data, despite the many examples and tutorials that claim so.

M is your ViewModel and should reside in your MVC project. The ViewModels you have in your BLL are actually to be named DataContracts or BusinessModels.

In your controller you have something comparable to this:

Get(id):
    dataContract = _service.Get(id);
    viewModel = Map(dataContract);
    return viewModel

In your service, something like this:

Get(id):
    dataModel = _dataAccess.Get(id);
    dataContract = Map(dataModel);
    return dataContract;

And in the DataAccess, you perform the proper joins according to the object requested. You are however of course free to add custom methods to your DataAccess when required, so your service can call those methods:

GetWithBars():
    dataModels = _repository.Query("select from foos join bars");
    return dataModels;
share|improve this answer
1  
Thank you for such a detailed explanation! –  mikhairu Nov 8 '13 at 15:05
add comment

View Models are special data containers that facilitates viewing data in presentation layer, along with providing clean separation of presentation logic. You can try EISK v7.0 – ASP.NET MVC edition, to see how view models should be implemented.

public class EmployeeViewModel
{
    Employee _employee;
    ControllerHelper _controllerHelper;

    public EmployeeViewModel(Employee employee, Controller controller)
    {
        _employee = employee;
        _controllerHelper = new ControllerHelper(controller);

    }

    public int EmployeeId
    {
        get
        {
            return _employee.Id;
        }
    }

    public string FullName
    {
        get
        {
            return StringHelper.ConnectStrings(" ", _employee.TitleOfCourtesy, _employee.FirstName, _employee.LastName);
        }
    }

    public string Title
    {
        get
        {
            return _employee.Title;
        }
    }

    public string HireDate
    {
        get
        {
            return string.Format("{0:M/dd/yyyy}", _employee.HireDate);
        }
    }

    public string BirthDate
    {
        get
        {
            if (_employee.BirthDate != null)
                return string.Format("{0:M/dd/yyyy}", _employee.BirthDate);
            else
                return "No birthday provided.";
        }
    }

    public string FullAddress
    {
        get
        {
            return StringHelper.ConnectStrings(", ", _employee.Address.AddressLine,
            _employee.Address.City, _employee.Address.Region, _employee.Address.PostalCode, _employee.Address.Country);

        }
    }

    public string PhoneWithExtension
    {
        get
        {
            return StringHelper.ConnectStrings(" - ", _employee.Phone, _employee.Extension);
        }
    }

    public string EmployeeImageSource
    {
        get
        {
            return _controllerHelper.Url.Action("EmployeeImageFile", new { id = _employee.Id });
        }
    }

    public IHtmlString SupervisorFullName
    {
        get
        {
            if (_employee.Supervisor != null)
            {
                string supervisorDetailsUrl = _controllerHelper.Url.Action("Details", new { id = _employee.Supervisor.Id });
                return MvcHtmlString.Create(HtmlConverter.LinkWrapper(StringHelper.ConnectStrings(" ", _employee.Supervisor.TitleOfCourtesy,
                    _employee.Supervisor.FirstName, _employee.Supervisor.LastName),
                    supervisorDetailsUrl));
            }

            return MvcHtmlString.Create("Damn lucky guy!");
        }
    }

    public IHtmlString ShortNotes
    {
        get
        {
            if (_employee.Notes != null)
            {
                if (_employee.Notes.Length > 1000)
                {
                    _employee.Notes = _employee.Notes.Substring(0, 1000) + " ...(<b>notes shortened).</b>";
                }

                _employee.Notes = HtmlConverter.ConvertTextToPlainHtml(_employee.Notes);
            }

            return MvcHtmlString.Create(_employee.Notes);
        }
    }
}
share|improve this answer
    
this does not answer the question asked "how I can solve this dilemma?" –  gnat Nov 20 '13 at 6:46
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.