akohan
asked on
How to type cast in MVC 4?
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:
NavController is responsible for navigation and showing categories (retrieved from its table) in left column as following:
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:
The error is:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generi c.IEnumera ble'. 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(GenerateR eport1, action ,Controllername)
@Html.ActionLink(GenerateR eport2, action ,Controllername)
@Html.ActionLink(GenerateR eport3, action,Controllername)
but need to load the report names from a table. Any help is appreciated.
Regards,
Amit
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...
}
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);
}
}
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);
}
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
};
}
}
The error is:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generi
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(GenerateR
@Html.ActionLink(GenerateR
@Html.ActionLink(GenerateR
but need to load the report names from a table. Any help is appreciated.
Regards,
Amit
Please provide the definition for the MetricsListViewModel class.
ASKER
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; }
}
}
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.
ASKER
OK, so what do you suggest? which part has to be modified to avoid this type cast error?
I would think that you don't even need the Select:
e.g.
...but that would depend on how repository.Metrics is defined. You may still need the Select, but with a tweak:
e.g.
MetricsListViewModel viewModel = new MetricsListViewModel
{
Metrics = repository.Metrics
};
...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 });
};
ASKER
ASKER
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?
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.
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();
ASKER
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.
The entity or complex type 'NameSpace.Domain.Concrete
ASKER
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(),
Line 24: {
Line 25:
Line 26: MetricsListViewModel viewModel = new MetricsListViewModel
Line 27: {
Line 28: Metrics = repository.Metrics.Select(
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.
Let me see if I can get someone with a little bit more EF experience to pop in.
ASKER
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
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
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 = )
ASKER
Thank you Kaufmed!
Hi akohan;
If I understand what is going on here in this line of code,
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();
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."
ASKER
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:
As you see, I have IEnumerable<Metric> defined in there. Also, my model is Metric as following:
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
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; }
}
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; }
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
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?:
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.
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
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();
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.
ASKER
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:
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(IMetric Repository 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:
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
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
}
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(IMetric
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")
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
Is it possible for you to zip up the complete project with a test database so I can run and test on my system?
ASKER
Sure, why not?!!
That's so nice of you. How may I send it over?
That's so nice of you. How may I send it over?
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.
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.
ASKER
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
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
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\Micros oft\Micros oft 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.
C:\Users\<USER NAME>\AppData\Local\Micros
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.
ASKER
Thank you so much! I'll be by my computer tonight and will follow the instructions.
Regards,
Akohan
Regards,
Akohan
ASKER
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 ".
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.
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]
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.
ASKER
Ok found it! it was hidden.
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.*
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.*
Were did you find it so I will know in the future.
ASKER
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.Com pnayName
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?
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.Com
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?
I have explained in this post ID: 40280074 how to up load. I do not think EE mail will do it.
ASKER
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
Here is the URL
please let me know if it works or not.
Regards,
Ak
Hi Akohan;
Have downloaded the two zip files and will be looking at them soon.
Have downloaded the two zip files and will be looking at them soon.
ASKER
Thank you!!!
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.
ASKER
I get it and was able to un-zip it fine.
ASKER
Great!
Thanks for confirm it.
Thanks for confirm it.
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?
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?
ASKER
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
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
ASKER
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.
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.
ASKER
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
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
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 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>
}
I will stick around until the end to help in any way I can. I will also be learning somethings with this question.
ASKER
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.Generi c.List`1[S ystem.Stri ng]', but this dictionary requires a model item of type 'System.Collections.Generi c.IEnumera ble`1[Afa. Domain.Ent ities.Cate gory]'.
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.
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.Generi
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.
Is the project still available? The link doesn't show anything for me.
ASKER
oh sure I will enable it again.
give me few minutes
Thanks.
give me few minutes
Thanks.
ASKER
Hi again Kaufmed
Please try this URL
Please confirm it it working for you and when you are done downloading.
Please try this URL
Please confirm it it working for you and when you are done downloading.
I grabbed it.
ASKER
Great! thanks to you both for helping!!!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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
ASKER
oh yes!!! got it this time.
ASKER
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?
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?
I'll expand the example when I get home this afternoon. The short answer is no.
ASKER
Thank you! please take your time.
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:
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:
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:
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:
...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:
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:
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.
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; }
}
}
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; }
}
}
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);
}
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>
}
...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>
}
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:
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.
ASKER
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!
Thank you
I will get back to you soon. I wish I could assign more than 500 score to this thread!
ASKER
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
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
ASKER
Cannot thank you enough for solving this!!!