1

I am working on asp.net mvc3 web application using MS Sql server 2008 express rc2. In my app I have two different brands in DB and one of them have few Null or 'unknown' values (e.g. 'unknown' is added to DB instead of Null). My question is how to pass only non null values to View Engine instead of using If/Else statements in View?

in controller:

var model = _data.GetViewModel(query);
        if (model != null)
        {                
            return View(model);
        }
        else
            return View("Error");

in ViewModel;

    public int Id { get; set; }
    public string Query { get; set; }
    public string Brand { get; set; }
    public string Family { get; set; }
    public string Type { get; set; }

in Model:

    public ViewModel GetViewModel(string query)
    {
        var data = _comp.Get(p => p.Query == query);
        if (data == null) return null;
        return new ViewModel
        {
            Id = data.id,
            Brand = data.brand,
            Family = data.family,
            Type = data.type
        };
   }

in View (I am currently using If statement):

@if (Model.Brand != null)
{
<span class="brand">@Model.Brand</span> 
}

@if (Model.Family != null)
{
<span class="family">@Model.Family</span> 
}

@if (Model.Type != null)
{
<span class="type">@Model.Type</span>
}

Note: I want to avoid If statement because there are too many values in the Database of each brand, and many of the them are Null, So I don't want to generate Html for those Null values. I am using If/Else statement like above code, and for checking too many values in View using If, it costs Memory on server and processor, and it also slow down server response time.

I want to have an alternative method to do this. Should I use Partial views or anything else? Please Please help me to solve this, Your help is very appreciated. Thanks and Regards.

tereško
  • 58,060
  • 25
  • 98
  • 150
aadi1295
  • 982
  • 3
  • 19
  • 47
  • In your View, I think you'll want those ORs `||` to be ANDs `&&`. – ShadowCat7 Aug 14 '13 at 23:41
  • @ShadowCat7: How about now, I have removed the || statement, only checking it with Null now. Problem is I want to avoid using If statement like this in View, because there are over 240 values of each brand, and almost 90% of them can be Null. so in one brand one value is added but for other its null. I don't want to generate html for it in view. – aadi1295 Aug 14 '13 at 23:44
  • What if the Brand is null, but the Family is not null. I assume you still want to show the Family, right? – ataravati Aug 14 '13 at 23:52
  • " I am using If/Else statement like above code, and for checking too many values in View using If, it costs Memory on server and processor, and it also slow down server response time." You can't get away from that. You're wanting to change what is displayed based on a condition. In order to do that, you *must* test for the condition. –  Aug 14 '13 at 23:53
  • The cost of those `if`s in a view is insignificant compared to the total cost of processing the request (*any* request). And using a partial or a razor helper will certainly be slower than that `if`. – Boris B. Aug 15 '13 at 00:05
  • @Amy: But What If I check conditions in controller or in my Business logic layer? – aadi1295 Aug 15 '13 at 01:11

2 Answers2

3

First, some background/context, then my suggestion.

(By the way, this all applies to any version of ASP.NET MVC or ASP.NET NancyFX (yes, there's another option out there!!), etc)

Context / Background

To solve this, people generally fall into two types of categories:

  1. Just get data and let the View decide what to show (common one, not the proper way IMO).
  2. The Controller should handle all the heavy lifting and give the view the exact answer (proper way, IMO).

The first way is quick and dirty. Sure it works, but it puts too much logic into the view. Views are not supposed to do any logic at all (exception: for loops, and maybe the odd if/else, maybe). The main reason for this is testing. Yep, that dirty word which people hate and think it's for hippies only. Or .. I don't have the time to test.. so I manually test, etc.. If you put any business logic into a view, you cannot test that.

The second way might seem a bit slower at first, but that's like anything - the more you practice, the faster you go. This is (IMO) the preferred method of doing things because you can test the controller. The controller should create a view model which will have -the exact- results that the view needs. Not extra. For example, imagine you want to return a list of Brands to the display/view. Most people do (the equivalent of) Get-all-brands into a list, and send that list to the view, even though 80% of those properties are -not- going to be used by that view! Even if ONE property is not going to be used by that view, do not retrieve it nor send it to the view!

So - TL;DR; do all the heavy lifting in the controller. The View is dumb. Just dump the exact view model data, to the view.

Solution to your problem

Ok, so let's roll with idea #2 and get all this stuff happening in the controller.

// Grab the results.
// ASSUMPTION: It is only returning the -exact- data I need. No more, no less.
var results = _data.GetViewModel(query);
if (model == null)
{                

// Project the results into a perfectly tight & svelte view model 
// 100% specific for this view.
var viewModel = results.
                Select(x => new ViewModel
                {
                    Id = x.Id,
                    Brand = string.IsNullOrEmpty(x.Brand) 
                              ? string.Empty 
                              : x.Brand,
                    Family = string.IsNullOrEmpty(x.Family) 
                              ? string.Empty 
                              : x.Family,
                    Type = string.IsNullOrEmpty(x.Type) 
                              ? string.Empty 
                              : x.Type,
                }).ToList();    


return viewModel;

Testing this..

[Fact]
public void GivenSomeBrands_Index_ReturnsAViewModel()
{
    // Arrange.
    // NOTE: Our fake repostitory has some fake data. In it ..
    //       Id: 1, Brand: Gucci.
    //       Id: 22, Brand: null.
    var controller = new BrandController(SomeFakeRepositoryThingy);

    // Act.
    var result = controller.Index(); // This calls that controller code, above.

    // Assert.
    Assert.IsNotNull(result); // Controller returned some result.
    Assert.IsNotNull(result.Model); // We have some model data.

    var model = result.Model as IList<ViewModel>(); // Cast the Model value.
    Assert.NotNull(model); // We have a strongly typed view model.

    // We check the first brand value.
    Assert.Equal("Gucci", model.First().Brand); 

    // We know this item has a null Brand, 
    Assert.Equal(string.Empty, model[21].Brand); but the ViewModel converted it.
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
  • Thats more like it. Thanks for explaining very briefly. I was thinking that too to send only clean data to View. I will surely test it. – aadi1295 Aug 15 '13 at 00:45
  • Great! any more questions, just ask. BTW, that test code is a based of using `xunit` testing framework. But it gives you a basic, starting idea. :) – Pure.Krome Aug 15 '13 at 00:47
  • A question: Using If in View is faster than using partial view or razor helper? – aadi1295 Aug 15 '13 at 01:23
  • To be perfectly honest, an `if` should be VERY cpu cheap. I also leverage `partial views` and `razor helpers`. But like i said, don't put ANY logic in the `View` besides a foreach loop. – Pure.Krome Aug 15 '13 at 01:55
  • This code is just converting null to empty string, which isn't any different. `@Model.Brand` displays nothing if Brand is null or empty string. – ataravati Aug 15 '13 at 18:27
0

You could write a custom HTML helper:

public static string MyHelper<V>(this HtmlHelper helper, V value, string css)
{
    if (value == null)
        return "";

    return String.Format("<span class='{0}'>{1}</span>", value, css);
}

Then in your view:

@Html.MyHelper(Model.Brand, "brand");
@Html.MyHelper(Model.Family, "family");
@Html.MyHelper(Model.Type, "type");
  • don't you think using a partial or a razor helper will certainly be slower than that if? – aadi1295 Aug 15 '13 at 00:46
  • `if` statements aren't that slow, you know. –  Aug 15 '13 at 17:22
  • -1 for the wrong return type, `string` instead `IHtmlString` (it allows to not re-encode the whole content), and for writing HTML as a constant straight into the code, instead of using [TagBuilder](https://msdn.microsoft.com/library/system.web.mvc.tagbuilder(v=vs.111).aspx). [This](https://stackoverflow.com/a/26322868/1401886) is an example of a correct way to create HTML inside C# code (he returns `MvcHtmlString`, that implements `IHtmlString`). – T-moty May 23 '18 at 07:43