1

This same problem keeps cropping up. I have a viewModel that doesn't have any persistent backing. It is just a ViewModel to generate a search input form.

I want to build a large where clause from the values the user entered. If the Action Accepts as a parameter SearchViewModel How do I do this without passing my viewModel to my service layer? Service shouldn't know about ViewModels right? Oh and if I serialize it, then it would be a big string and the key/values would be strongly typed.

SearchViewModel this is just a snippet.

[Display(Name="Address")]
  public string AddressKeywords { get; set; }

        /// <summary>
        ///     Gets or sets the census.
        /// </summary>
        public string Census { get; set; }

        /// <summary>
        ///     Gets or sets the lot block sub.
        /// </summary>
        public string LotBlockSub { get; set; }

        /// <summary>
        ///     Gets or sets the owner keywords.
        /// </summary>
        [Display(Name="Owner")]
        public string OwnerKeywords { get; set; }

In my controller action I was thinking of something like this. but I would think all this logic doesn't belong in my Controller.

ActionResult GetSearchResults(SearchViewModel model){
    var query = service.GetAllParcels();
    if(model.Census != null){
    query = query.Where(x=>x.Census == model.Census);
    }
    if (model.OwnerKeywords != null){
    query = query.Where(x=>x.Owners == model.OwnerKeywords);
    }
    return View(query.ToList());
    }
tereško
  • 58,060
  • 25
  • 98
  • 150
Doug Chamberlain
  • 11,192
  • 9
  • 51
  • 91

3 Answers3

2

What you're doing here is using LINQ in place of the more strict OO "specification pattern." Nothing wrong with that, if your application layering allows it (i.e. if you have direct access to the database from your web front ends, which not all architectures do), though you may want to push it into a "service" class rather than performing a lot of query logic in the controller. How strict you want to be in separation of these kinds of things depends on the size and complexity of your application.

One way to introduce a bit of abstraction is to make your view model implement an interface (for example: ISearchSpecification) which defines the properties which can be used in your query predicates. Then, you can create a class or extension method that operates on the interface.

public interface IMySearchSpec 
{
   string City { get; }
   string State { get; }
}

public class MyViewModel : IMySearchSpec
{
   ...implement IMySearchSpec and perhaps other things...
}

public static class QueryExtensions
{
   public static IQueryable<T> ApplyPredicate(this IQueryable<T> query, IMySearchSpec searchSpec) 
   {
        if (searchSpec.City != null )
        {
            query = query.Where( x => x.City == searchSpec.city );
        }
        // ..etc..
       return query;
   }
}

// then, in your controller
var filteredQuery = query.ApplyPredicate(viewModel);

You can also apply paging/sorting logic in a similar way.

jlew
  • 10,491
  • 1
  • 35
  • 58
  • haha! this looks pretty good. I've been under so much pressure on this project and working so quickly I've been forgetting about tools available to me. like interfaces... – Doug Chamberlain Jun 25 '13 at 19:18
2

How do I do this without passing my viewModel to my service layer? Service shouldn't know about ViewModels right?

Right, view-models are presentation layer objects, so keeping them out of your service layer or persistence layer is a good idea. Also, the controller should be lean and not have much logic, so factoring this code somewhere else is the right approach, I think. Here are some alternatives for your situation:

  • Pass your values to your service layer from the controller
  • Create a helper class that creates the string for you
  • Map your view-model to another class/entity and pass this to your service layer
Dai
  • 141,631
  • 28
  • 261
  • 374
Big Daddy
  • 5,160
  • 5
  • 46
  • 76
  • "Map your view-model to another class/entity and pass this to your service layer". This is how I would do it. – Josh Noe Jun 25 '13 at 19:24
1

Usually you would not take a viewmodel as parameter from the view, instead; viewmodels are passed from the controller to the view.

The reasoning behind my logic is that if a large (lots of data) viewmodel is passed from the controller to the view, and than passed from the view back to the controller could become an issue (performance); especially if you do not need all the information in the controller action. I guess you could send a viewmodel created by the view to the controller (a subset of the original larger viewmodel). My understanding of the question was the he wanted to pass the same viewmodel back and forth between the controller -> view -> controller.

From stackoverflow question In MVC, what is a ViewModel?:

The ViewModel may also perform conversions from the type of data that your Model carries to the type of data your View can conveniently work with; this might even mean that the ViewModel does not carry Models directly but other vessels that carry (possibly a subset of) the same information in a more suitable format.

Consider that you could have a Library model that aggregates Albums and DVDs -- the difference between such a model and the corresponding ViewModel is precisely that the Model doesn't care (or even know about) the View while the ViewModel has the express purpose of facilitating it.

Change the parameter to your controller action to a class, like SearchParameters. It is fine to pass SearchParameters to your service layer.

Community
  • 1
  • 1
Felix
  • 3,246
  • 4
  • 23
  • 25
  • 1
    "Usually you would not take a viewmodel as parameter from the view," - can you elaborate on this as - from my interpretation - it is exactly the opposite of the truth? – Ant P Jun 25 '13 at 19:00
  • 1
    Agreed with Ant P that this is not true. Viewmodels are for both passing to the view, and for receiving as a parameter in a post action. – Josh Noe Jun 25 '13 at 19:20
  • The reasoning behind my logic is that if a large (lots of data) viewmodel is passed from the controller to the view, and than passed from the view back to the controller could become an issue (performance); especially if you do not need all the information in the controller action. I guess you could send a viewmodel created by the view to the controller (a subset of the original larger viewmodel). My understanding of the question was the he wanted to pass the same viewmodel back and forth between the controller -> view -> controller. – Felix Jun 25 '13 at 20:37