?
Solved

How to type cast in MVC 4?

Posted on 2014-08-21
61
Medium Priority
?
761 Views
Last Modified: 2016-02-18
This is the case I'm facing:

I do have 3 controllers as following: a) MetricController b) CategoryController c) NavController

Before I go further, I better to share that I have a simple layout on View (HTML page) which consists of a column (on left) representing categories and on right portion of page I have to show a set of information. The information has to change base on selected category (report type) on left column.

MetricController is responsible to retrieve some information from a table (Metrics table) and it is something:



   public class Metric  {   //just a class for IIS log
        public int Id { get; set; }
        public string date { get; set; }
        public string time { get; set; }
        public string c_ip { get; set; }
        public string cs_username { get; set; }
        public string s_sitename { get; set; }
        public string s_computername { get; set; }
        public string s_ip { get; set; }
        AND MORE...

    }

Open in new window



 
public class MetricController : Controller {
        private IMetricRepository repository;

        public MetricController(IMetricRepository metricRepository) {
            this.repository = metricRepository;
        }

        public ViewResult List() {

            MetricsListViewModel model = new MetricsListViewModel {
                Metrics = repository.Metrics
                .OrderBy(p => p.Id)   //just few columns for now
            };

            return View(model);
        }
    }

Open in new window



NavController is responsible for navigation and showing categories (retrieved from its table) in left column as following:

public class NavController : Controller  {

        private ICategoryRepository repository;
        public NavController(ICategoryRepository repo) {
            repository = repo;
        }

        public PartialViewResult Menu() {
            IEnumerable<string> categories = repository.Categories
                                                .Where(x => x.Visible == "1" /*|| x.Visible == "0"*/)
                                                .Select(x => x.CategoryName)
                                                .Distinct()
                                                .OrderBy(x => x);

            return PartialView(categories);
        }

Open in new window


Now, I have an HTML page which shows off a left column populated with categories (or let's say report names in the left column) and need to setup and run a query every time user clicks on one of a report/category name.

One thing I know (feel free to correct me if I'm wrong) is that I have to work on CategoryController but using a code as following ends up to getting a type casting error:

public class CategoryController : Controller   {
        private IMetricRepository repository;

        public CategoryController(IMetricRepository metricRepository) {
            repository = metricRepository;
        }

        public ViewResult List(string category)
        {

            MetricsListViewModel viewModel = new MetricsListViewModel {
                Metrics = repository.Metrics
                    .Select(p => p.cs_username)  <<<<<<<<  cannot convert 
            };
        }
    }

Open in new window


The error is:

Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)

This is my first MVC application and I feel like I'm missing something. I could use a constant list of report and use

@Html.ActionLink(GenerateReport1, action ,Controllername)
@Html.ActionLink(GenerateReport2, action ,Controllername)
@Html.ActionLink(GenerateReport3, action,Controllername)
but need to load the report names from a table. Any help is appreciated.

Regards,
Amit
0
Comment
Question by:akohan
  • 34
  • 15
  • 12
61 Comments
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40277564
Please provide the definition for the MetricsListViewModel class.
0
 

Author Comment

by:akohan
ID: 40277572
Oh sorry! I missed that part ... Thanks for reminding it goes as:


 
   public class MetricsListViewModel  {

        public IEnumerable<Metric> Metrics { get; set; }
        public PagingInfo PagingInfo { get; set; }
        public string CurrentCategory { get; set; }
    }
}

Open in new window

0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40277599
The cs_username property in your last snippet of the OP is of type string. Your problem is that Select will return IEnumerable<string>, but you are trying to assign that result to a property of type IEnumerable<Metric>. This does not work. You need to modify your Select so that it returns the correct type.
0
Fill in the form and get your FREE NFR key NOW!

Veeam is happy to provide a FREE NFR server license to certified engineers, trainers, and bloggers.  It allows for the non‑production use of Veeam Agent for Microsoft Windows. This license is valid for five workstations and two servers.

 

Author Comment

by:akohan
ID: 40277606
OK, so what do you suggest? which part has to be modified to avoid this type cast error?
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40277617
I would think that you don't even need the Select:

e.g.

MetricsListViewModel viewModel = new MetricsListViewModel
{
    Metrics = repository.Metrics
};

Open in new window


...but that would depend on how repository.Metrics is defined. You may still need the Select, but with a tweak:

MetricsListViewModel viewModel = new MetricsListViewModel
{
    Metrics = repository.Metrics.Select(p => new Metric() { cs_username =  p.cs_username });
};

Open in new window

0
 

Author Comment

by:akohan
ID: 40277641
1)
I guess, something is not right. please see the attached picture.
I am new to MVC but I am expecting this to return a variable from ViewResult type and pass it onto a view.


type cast error
0
 

Author Comment

by:akohan
ID: 40277650
One question to add here: if there is no need to use a SELECT or no linq involved then how can I retrieve the data from table?
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40277682
1. Change the ending semi-colon ( ; ) to a comma ( , )
2. It looks like you are using Entity Framework. Without going into too much detail, the repository.Metrics effectively is your table. When ASP.NET gets to a point in the page life cycle where it actually needs to spit out the data, then it will make the appropriate calls out to the database just by virtue of having set the code up as I mentioned above.

Having said that, I seem to recall that you cannot pass the "table" back to the view in the fashion I outlined above. If you run into any issues with the above code, then try adding in a ToList call at the end.

e.g.

Metrics = repository.Metrics.Select(p => new Metric() { cs_username =  p.cs_username }).ToList();

Open in new window

0
 

Author Comment

by:akohan
ID: 40277715
Thanks. yes, I'm using EF 5.0 in MVC 4. I followed the steps and it runs but then in runtime I get this error

The entity or complex type 'NameSpace.Domain.Concrete.Metric' cannot be constructed in a LINQ to Entities query.
0
 

Author Comment

by:akohan
ID: 40277723
just to have it reviewed by you. I'm getting error on this line 26:

Line 24:         {
Line 25:
Line 26:             MetricsListViewModel viewModel = new MetricsListViewModel
Line 27:             {
Line 28:                 Metrics = repository.Metrics.Select(p => new Metric() { cs_username = p.cs_username}).ToList(),
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40277727
OK, my fault. (We don't get to use EF at work, so I've only played with it a handful of times.) From what I see on the web, you cannot create instances of entities that exist for the purpose of modeling your database. You would need to create an additional class that has the same structure, and then Select into that. This means you would have to change your view model to use the new class as well.

Let me see if I can get someone with a little bit more EF experience to pop in.
0
 

Author Comment

by:akohan
ID: 40277755
Thank you so much! I'm new to EF and also to MVC so have to confess totally feel hopeless.
However, just to make sure I am following you right: so I have to make a new class and replace the View Model? or should I change Metric class instead?

I truly appreciate your concern and help OK I will wait for your inputs.

Regards.
Amit
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40277761
I sent a message to a guy who I know is knowledgeable about EF stuff. I'm not sure when he'll see it, but I'm sure he'll drop buy when he gets it  = )
0
 

Author Comment

by:akohan
ID: 40277798
Thank you Kaufmed!
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40278076
Hi akohan;

If I understand what is going on here in this line of code,

Metrics = repository.Metrics.Select(p => new Metric() { cs_username =  p.cs_username }).ToList();

Open in new window

Metric() is a class that was created by Entity Framework to map to the database table Metrics. Entity Framework will not allow you to create new instance objects of the same object type that it is returning from the query. You can return the complete object or create a Data Transfer Object, DTO, and fill only those fields that are needed. There is a third way and that is to return an Anonymous type from the query but in a case like this I would not suggest that. The DTO class does not need to have all the same fields as the Metric class only those fields that are needed or you can have all if you want that. And as Kaufmed has stated, "you would have to change your view model to use the new class as well."
0
 

Author Comment

by:akohan
ID: 40279427
Hello Fernando,

I truly appreciate you and Kaufmed for the care and time you both took to address this issue! OK, now let me bug you with this. This is the MetricsListViewModel class that you both said it should be changed:

public class MetricsListViewModel  {

        public IEnumerable<Metric> Metrics { get; set; }
        public string CurrentCategory { get; set; }
    }

Open in new window


As you see, I have IEnumerable<Metric> defined in there. Also, my model is Metric as following:
  public class Metric  { // information from IIS log - FYI
        public int Id { get; set; }
        public string date { get; set; }
        public string time { get; set; }
        public string c_ip { get; set; }
        public string cs_username { get; set; }
        public string s_sitename { get; set; }
        public string s_computername { get; set; }
        public string s_ip { get; set; }

Open in new window



I'm assuming from "changing" you mean I have to get rid of IEnumerable<Metric>. Right? if so, what it should be changed to?

In existing code in linq,  compiler is trying to type case IEnumerable<string> to IEnumerable<Metric>. Right?

Thank you,
Amit
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40279585
Hi Amit;

In the Model when you/EDMX designer created the classes to map to the database, for the table Metrics the designer created a class called Metric and I am assuming that the class you posted in your last post is that same class Metric. If I am wrong about that please let me know because the following information is based on that assumption. The reason you were getting the following error, "The entity or complex type 'NameSpace.Domain.Concrete.Metric' cannot be constructed in a LINQ to Entities query.", is that EF was returning a Metric object and you in your query were asking it to create an object of the same type Metric and assign to one of its field the value from the Metric the query had already created. This is not allowed.

So let me ask the question what do you want this query to have accomplished for you?:
Metrics = repository.Metrics.Select(p => new Metric() { cs_username =  p.cs_username }).ToList();

Open in new window


To your question in your last post, "I'm assuming from "changing" you mean I have to get rid of IEnumerable<Metric>. Right?", yes. And to this part of the question, "if so, what it should be changed to?", It should become a collection of object created by the query. But to give a more complete answer I need to understand what the query is returning.
0
 

Author Comment

by:akohan
ID: 40279677
Thank you again!

As you advised let me share few things to make sure we both are on the same page:

I did not use EDMX designer, I manually created a Metric class and after getting to  "Server Explorer" in VS 2012, I was able to see the table structure table which was made/generated by  EF 5.0.0.0

In response to your question I should Yes, the class is Metric.cs I already posted it here.

Now, regarding Linq code that you addressed here: that was given to me by Kaufmed as he was helping me with the problem.

What am I trying to do? it is simple.

I have two tables one called Metric which contains few columns (imported from IIS log file e.g. client IP address, Server IP Address and etc) and one is called Category which in fact is a list of Report names in it e.g.  as following:

    public class Category {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }  // in fact, it should be ReportName
        public string Visible { get; set; }  // to hide/visible a report
    }

Open in new window


When user clicks on a category (or report name ), I need to run a specific query or linq script to generate a report so I did make a controller as:

public class CategoryController : Controller   {

        private IMetricRepository repository;

        public CategoryController(IMetricRepository metricRepository) {
            repository = metricRepository;
        }

        public ViewResult List(string category)   {
             
            IF category is "TOP 10 VISITED PAGES" THEN RUN ITS QUERY OR LINQ CODE
            IF category is "LIST OF UNIQUE VISITORS" THEN RUN ITS QUERY OR LINQ CODE

        }
}

As you notice, it is a simple application and I just need to have code choose and call a query base on selected item or report item from a menu (left column menu on page).  Yes, I can create a static menu in View as:


@Html.ActionLink("New vs Returning", "GetNewVsRetruningUsers", "Metric")
@Html.ActionLink("Total Visits", "GetTop10Page", "Metric")
@Html.ActionLink("Browser", "GetBrowserTypeUsed", "Metric")

Open in new window


but like to be more flexible and also I'm still learning MVC. Please ask me more questions if it is not clear.
Once again I appreciate your help.

Thanks
AK
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40279691
Is it possible for you to zip up the complete project with a test database so I can run and test on my system?
0
 

Author Comment

by:akohan
ID: 40279805
Sure, why not?!!
That's so nice of you. How may I send it over?
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40280074
Hi akohan;

Well you can't sent it through EE email messaging I got 24 emails with the same message but no files. The first way is to zip the complete project and test database into one zip file and use the attach file link below the "Post a Comment" text box as shown in the image below. The next way if for some reason it does not allow you is to use the EE-Stuff web site which has  the same username and password as here. Once you login to EE-Stuff click on Expert Area Tab at the top left of the page. Then on the next page click on the "Upload a new file" link and follow the instructions on the page. If all else fails you can post to a web site such as Microsoft OneDrive but only if all else fails. For the second and third options please post the link to the downloads so we can download them.
0
 

Author Comment

by:akohan
ID: 40282238
Hello Fernando,

I hope all is well. I'm kinda surprised that how you have received 24 emails from me!!! I sent you one to share my email.
No idea why this happened!

OK sure, I will email you the zip file just one question. Where does the database is made by EF? it is not in under the project folder.


Any idea?

Thanks,
Akohan
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40282325
OK, if your using localdb for development then look here, On my system I found them at the following location.

C:\Users\<USER NAME>\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\Projects

If you can not find them there look in the web.config file under the connectionstring node look at the property AttachDbFileName = " PATH TO LOCATION OF DB ", It could also be in the  App_Data directory.

If you are not using localdb please tell me which SQL Server you are using.
0
 

Author Comment

by:akohan
ID: 40282361
Thank you so much! I'll be by my computer tonight and will follow the instructions.

Regards,
Akohan
0
 

Author Comment

by:akohan
ID: 40283566
Hello,

No it is not under the path you addressed but also I looked at web.config yet I don't see the property "AttachDbFileName ".  

[i] <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="EFDbContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=Analytics;Integrated Security=True" providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <appSettings>[/i]

Open in new window


Any advice? meanwhile, I will be looking for more information on how to find the DB. This EF doesn't make me feel so comfortable first , I'm new to it and when things get automated nobody exactly knows what is happening in the background.




Thanks.
0
 

Author Comment

by:akohan
ID: 40283582
Ok found it! it was hidden.
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40283628
Hi akohan;

Try this path to see if the database file Analytics is there.

C:\Program Files\Microsoft SQL Server\110\LocalDB\Binn

If it is note and you have a 64 bit machine then also try this location.

C:\Program Files (x86)\Microsoft SQL Server\110\LocalDB\Binn

If that also gives no joy try a file system search for the file Analytics.*
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40283728
Were did you find it so I will know in the future.
0
 

Author Comment

by:akohan
ID: 40283744
No, you are right but I guess it is a networking thing.
My username is akohan but I had to look at akohan.companyName folder instead.

I did a search for the DB name and set the Windows Explorer to show hidden files and it was under C:\Users\MyUsernameDot.CompnayName

So nothing new. I had to pay more attention!

I'm uploading them on server then will email it to your EE contact email.

Would that be OK?
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40283760
I have explained in this post ID: 40280074 how to up load. I do not think EE mail will do it.
0
 

Author Comment

by:akohan
ID: 40283881
I read the other day and today Monday forgot about it!
Here is the URL

please let me know if it works or not.

Regards,
Ak
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40283899
Hi Akohan;

Have downloaded the two zip files and will be looking at them soon.
0
 

Author Comment

by:akohan
ID: 40283926
Thank you!!!
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40283935
The afa.zip file seems to be corrupted with an unknown error. Can you re-zip the project and try again. The DB.zip file came down with no errors and I unzip'ed it without issue.
0
 

Author Comment

by:akohan
ID: 40283992
Sure, this is the URL for new zip file, not sure why my upload is super slow!
URL

URL
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40284013
I get it and was able to un-zip it fine.
0
 

Author Comment

by:akohan
ID: 40284022
Great!

Thanks for confirm it.
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40284096
OK, so lets get on the same page now that I have something to look at.

I see that you have comment out the original EF query we were working on. So what is the current issue you need help with?
0
 

Author Comment

by:akohan
ID: 40284152
OK, let's us forget to the statement we had and commented out.
my question is:

How can I click on one of report names on left column and run a specific query?

And see the result on page?

Thanks
0
 

Author Comment

by:akohan
ID: 40284225
In fact, I am thinking of something like case statement so that each case will contain or run a unique query to get specific results per report.
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40284379
When kaufmed asked me to take a look at that this question it was for the Entity Framework part. As I told him I do not know MVC well enough to help with that. I sent him a email to have him help with this part.
0
 

Author Comment

by:akohan
ID: 40284443
That is fine I appreciate your concern and help.
The problem is that I'm new to both MVC and EF :)

See, as I said I was trying to invoke a specific linq query base on selected report (listed on left column) which get both MVC and EF involved. OK, I will wait for Kaufmed but if you don't mind I might ask questions related to EF if your time allows.

Once again, I appreciate all your help!

Regards,
ak
0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40284450
Hi akohan;

In you original question you were trying to display cs_username on the page. Here is a way that can be done. Once those changes are made you can click on Unique Users on the left of the screen and the middle should change.

// In this class CategoryController change this method as shown below.

public ViewResult List(string category)
{
    List<String> viewModel = new List<String>();
    viewModel.AddRange(repository.Metrics.Select(u => u.cs_username).ToList<String>());
    
    return( viewModel );
}


// In the view for the above method found in the View folder for Category in List.cshtml make these chanages.

@model List<String>           

<h2>List of Reports</h2>

@{
    ViewBag.Title = "Airforce Tracer";
}

@foreach (var p in Model) { 
    <div id="item">
        <h3>@p</h3>
    </div>
}

Open in new window

0
 
LVL 64

Expert Comment

by:Fernando Soto
ID: 40284463
I will stick around until the end to help in any way I can. I will also be learning somethings with this question.
0
 

Author Comment

by:akohan
ID: 40284575
Thanks. yes, it looks a right way of doing but here is the situation:

This left menu is populated by a controller called NavController so my assumption is we have to call in there, however, when I do so it doesn't like it and throws error as:

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[System.String]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Afa.Domain.Entities.Category]'.

Either I have to change the design/structure of the application or there is a way to do so.

Just as reminder:

MetricController handles the data item
CategoryController handles the name of categories or reports.
NavController handles the navigation so maybe I have to get rid of it?

Thanks.
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40284586
Is the project still available? The link doesn't show anything for me.
0
 

Author Comment

by:akohan
ID: 40284600
oh sure I will enable it again.
give me few minutes

Thanks.
0
 

Author Comment

by:akohan
ID: 40284610
Hi again Kaufmed

Please try this URL

Please confirm it it working for you and when you are done downloading.
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40284616
I grabbed it.
0
 

Author Comment

by:akohan
ID: 40284625
Great! thanks to you both for helping!!!
0
 
LVL 75

Accepted Solution

by:
käµfm³d   👽 earned 2000 total points
ID: 40284744
OK, so here's an example that takes into account what we've discussed. I've started with an action that you haven't implemented yet:  UniqueVisitors (I renamed it a bit, but that's a bit of a stylistic change; you can leave it as "GetUniqueVisitors" if you like...after all, it's your app!). So we create a new view model class to represent what gets put into the view from the database:

using System.Collections.Generic;

namespace Airforcea.WebUI.Models
{
    public class UniqueVisitorsViewModel
    {
        public IEnumerable<string> Usernames { get; set; }
    }
}

Open in new window


It's very simple:  there's only one property. Notice that I've named the view model the same as the view it correlates to. Again, this is stylistic, but it helps clarify what the view the view model applies to. (There could certainly be situations where you share view models between views, but I tend to start simple, and then adjust as necessary.) So, now that I have a view model for my view, what might the view look like?

@model Airforcea.WebUI.Models.UniqueVisitorsViewModel

@{
    ViewBag.Title = "UniqueVisitors";
}

<h2>UniqueVisitors</h2>
@if (Model.Usernames.Any())
{
    <ul>
        @foreach (var username in Model.Usernames)
        {
            <li>@username</li>
        }
    </ul>
}
else
{
    <text>No users found.</text>
}

Open in new window


Again, rather simple. This particular view just checks to see if there are any usernames contained within the model. If there are, then we spit them out as a part of a <ul> element. Otherwise, we simply print out a message that there were no users found. Note the use of the <text></text> tags. These are special tags in Razor. Whenever you use an @ symbol you are introducing server-side code that runs as a part of building the actual HTML that gets returned to the browser. When you have braces (e.g. @{ } ), then you introduce an entire block that is effectively server-side code. While you can include HTML as a part of your server side code, sometimes the Razor parser gets confused, and you have to explicitly tell it that what you just typed is not server-side code but raw text. That is what those special tags do.

Now, we've created a view model as well as a view to display that model. But how do we populate that view model? We obviously have to query the database to fill the view model. So in the controller we might do:

public ViewResult UniqueVisitors()
{
    UniqueVisitorsViewModel viewModel = new UniqueVisitorsViewModel();

    viewModel.Usernames = this.repository.Metrics.Select(metric => metric.cs_username)
                                                    .Distinct()
                                                    .ToList();

    return View(viewModel);
}

Open in new window


Notice above that I create a new instance of my view model class, and then right after I craft the query that goes out to the database. The Select allows me to only pull the cs_username field, and the subsequent Distinct ensures that I only deal in unique values. The ToList is required to force the query to actually execute--neither of the previously-mentioned method will cause query execution. If you don't include the ToList, then you will encounter strange errors pertaining to your context no longer being valid.

So we take the result of the query and assign it to the Usernames property of the view model. Then, we push the view model to the view by passing it to the View call.

So what do we end up seeing? With the current state of the data:

Screenshot
If we modify the data a touch--since your database has NULL for every username at present:

Screenshot
You see a blank in the first spot because I left some of the rows as NULL. You could account for this in your query, of course.
0
 

Author Comment

by:akohan
ID: 40286344
Thank you dear Kaufmed,

One question only ... the controller you were talking about in your example, is it a new one or you used an existing one?
if existing one then which one?

Regards,
ak
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40286382
The Metric controller. You can tell in the URL:

Screenshot
0
 

Author Comment

by:akohan
ID: 40286388
oh yes!!! got it this time.
0
 

Author Comment

by:akohan
ID: 40286514
One question to add here. Currently you have one column displayed in the report or query and its object name is usernames right?
now if I want to have multiple columns to show on report, I think I don't need to modify the Model. Just have to access to all other columns using the same usernames.ColumnName.

Right?
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40286532
I'll expand the example when I get home this afternoon. The short answer is no.
0
 

Author Comment

by:akohan
ID: 40286754
Thank you! please take your time.
0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 40287491
My apologies for the delay. I hit the pillow when I got home, and apparently went in to hibernation mode  = )

So, let's extend the example to add in additional information. Recall above that we said we would have a 2nd class that basically mimics the EF entity. We have that above, but now we need to add in the additional columns that we care about. Let's add in IP address and site name. Previously, I made the Usernames property very simple:  it only contains a "list" of strings that represent the usernames of people in the system. But we now want to add in more information about each user. At this point, though, we wouldn't want to just add in more properties to the view model. Rather, we want to modify the Usernames property to reflect that we have additional user information, not just usernames. (I'll explain why in at the end.) So, let's add an additional class that represents user information:

namespace Airforcea.WebUI.Models
{
    public class VisitorModel
    {
        public string Username { get; set; }
        public string IpAddress { get; set; }
        public string SiteName { get; set; }
    }
}

Open in new window


This class captures all of the "user" information for an individual user. Then, since our view model will contain information about all users that have visited us, we tweak that class:

using System.Collections.Generic;

namespace Airforcea.WebUI.Models
{
    public class UniqueVisitorsViewModel
    {
        public IEnumerable<VisitorModel> Visitors { get; set; }
    }
}

Open in new window


We change the Usernames property to a name more fitting, and we change its type from IEnumerable<string> to IEnumerable<VisitorModel>. We next have to change the query to populate this new class. We'll need to modify the Select to create new instances of the VisitorModel class which each contain those bits of information that we care about from the query (i.e. the properties we put into VisitorModel). So:

public ViewResult UniqueVisitors()
{
    UniqueVisitorsViewModel viewModel = new UniqueVisitorsViewModel();

    viewModel.Visitors = this.repository.Metrics.Select(metric => new VisitorModel() { IpAddress = metric.c_ip, SiteName = metric.s_sitename, Username = metric.cs_username })
                                                .Distinct()
                                                .ToList();

    return View(viewModel);
}

Open in new window


Notice that in a LINQ query a Select can basically create anything you like. Here, I create instances of the VisitorModel, whereas previously I was selecting just strings. All of the remaining code in the controller is the same. Now we can modify the view to show this additional information. Previously we had:

@model Airforcea.WebUI.Models.UniqueVisitorsViewModel

@{
    ViewBag.Title = "UniqueVisitors";
}

<h2>UniqueVisitors</h2>
@if (Model.Usernames.Any())
{
    <ul>
        @foreach (var username in Model.Usernames)
        {
            <li>@username</li>
        }
    </ul>
}
else
{
    <text>No users found.</text>
}

Open in new window


...but we no longer have a Usernames property--we now have Visitors. Let's change the view to dump out all of the information that we Selected in the query:

@model Airforcea.WebUI.Models.UniqueVisitorsViewModel

@{
    ViewBag.Title = "UniqueVisitors";
}

<h2>UniqueVisitors</h2>
@if (Model.Visitors.Any())
{
    <ul style="float: left;">
        @foreach (var visitor in Model.Visitors)
        {
            <li>@visitor.Username</li>
        }
    </ul>
    <ul style="float: left;">
        @foreach (var visitor in Model.Visitors)
        {
            <li>@visitor.IpAddress</li>
        }
    </ul>
    <ul style="float: left;">
        @foreach (var visitor in Model.Visitors)
        {
            <li>@visitor.SiteName</li>
        }
    </ul>
}
else
{
    <text>No users found.</text>
}

Open in new window


Here I've added in additional <ul> tags, one for each property of the Visitor that I care about. (I've floated them left just so that each <ul> will be next to each other horizontally rather than vertically.) The results of all of these changes are:

Screenshot
Now, as to why I created an additional class for the visitor information. Your view model class is a class that encompasses all of the data that your view should display. In this simple example, we only had visitor information, but what if we'd also had information about the individual sites that we'd also like displayed? Your view model functions kind of like a container for all of the data items you'd like to show. If each data item has specific elements to it, then it often times makes sense to create a class that represents the data item, and include the specifics in that class. That is what I did with the VisitorModel:  that class captures the specifics about each visitor that I have. Should I want to display site information also, then I would simply create a SiteModel class that represents the specifics about a site. I would then include a Sites property on my view model class to contain all of the sites that I pulled from the database.
0
 

Author Comment

by:akohan
ID: 40288874
LOL. I'm glad you did that ... I did the same thing the night before and crashed big time yet my toddler woke me up by 4:30 am and had to entertain him.
Thank you
I will get back to you soon.  I wish I could assign more than 500 score to this thread!
0
 

Author Comment

by:akohan
ID: 40299321
Hello Kaufmed,

Sorry for the delay been busy and today I'm getting back to the code again. I just want to thank you for solving the issue and also so much thanks to Fernando for his help and compiling my problem back to you.

I hope I will be in touch with you both and thanks to you both for sharing your knowledge.

Regards,
Ak
0
 

Author Closing Comment

by:akohan
ID: 40299324
Cannot thank you enough for solving this!!!
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

International Data Corporation (IDC) prognosticates that before the current the year gets over disbursing on IT framework products to be sent in cloud environs will be $37.1B.
Performance in games development is paramount: every microsecond counts to be able to do everything in less than 33ms (aiming at 16ms). C# foreach statement is one of the worst performance killers, and here I explain why.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question