There is a lot of work left to be done but I thought it would be a good idea to package up a Preview so that people can play with the site and the code.
Episode 24 Has Been Cut
I've been working on Episode 24 for the last few days, which deals with how to implement coupons and incentives. I finished the system (and I'm happy with it) but the screencast put me to sleep. It was pretty dull, to say the least.
So I've decided to focus everything on cleaning this up and getting it ready for general use. If you're really interested in how the coupons were created, I'll dive into it at a later time. For now I really want this site to be finished!
To install, you'll need to unzip into a directory of your choice and then restore the DB backup file that's in the "Commerce.Web\App\DBFiles" directory (we've removed the DB from the App_Data directory).
Make sure you're using Visual Studio 2008 with MVC Beta 1, and you're good to go.
Awesome job all this by the way.
As I see this series of webcast coming to a close I would like to ask you if you would consider doing a webcast on the architecture of the whole thing. Myself, and many others I presume, are new developers and we would greatly benefit from a video that walks through the application from a user perspective, that shows how all the pieces are connected together.
For example, when a user logins by pressing the login button, what happens in the background that results in the page or view the user sees as a result? Since this Storefront may be used by many novices like me, I am hoping that there is value in this video request.
In any case, great job!
Best regards,
Antoine@Toronto
Yes, I haven't changed to using the futures version of the helpers.
RE ProductSummaryDisplay - you can't "set properties" anymore with RenderPartial, and I was jumping through some messed up ViewData hacks to get it to work - so I just made a new display.
Yes - the GAC always takes precedence.
The web.config has to be in the root unfortunately - but I'll probably add one to App to keep it from being browsable.
Thanks for the help :)
I think when this is all over I'll do a quick overview of each system and why I made it that way - that's a good idea.
http://odetocode.com/Blogs/scott/archive/2008/05/21/12122.aspx
DB is not restore..
that an Expression is actually built backwards, and the projection is called
when the thing is executed, not before or after. I know it looks like magic
(and in many cases you might even call it Voodoo) - but this doesn't happen.
The only thing that will fire the query is enumerating or singling it -
appending an IQueryable expression to it will not do this. Unless you've
seen otherwise?
Order controller: ItemAdded has invalid parameter comment and userName is never used
Authentication controller:
OpenIDLogin function: isValid variable doesn't appear to be used
Personalization controller requires an instance of an orderService but doesn't appear to get used.
sqlOrderRepository : SaveAddress: variable isInsert is defined but never used
CatalogFilters: Check parameter name comments on every function
Model.CreditCard: IsValid and MaskedNumber - Check parameter comment
CatalogService constructor parameter comment
OrderService GetRecommended has an unreachable return statement (last line)
MigrateCurrentOrder elseif has a check on toOrder but toOrder will never be null there
ViewExtensions: ToFormattedList: variable en is never used. Also check paramter comments.
RegisterScript: variables scriptLibFile & result are never used
RegisterJS: variables scriptRoot, scriptFormat, scriptLibFile don't appear to be used
RenderCommerceControl check parameter comments
Looks like you need ReSharper :)
My trial version is about to expire :(
I keep getting an error when starting the solution.
Invalid column name 'CategoryName'.
"Category category = _catalogService.GetCategory(categoryID) ??
Source File: E:\mvc\demostorefront\MVCStore_Preview1A\Commerce.Web\App\Controllers\CatalogController.cs Line: 32
Any ideas
The Beta is looking sweet. Excellent work. I am following your progress and working on a similar project. Gotta tell you - learning a lot of things here.
I have a quick question though.
how in the world do you clear all pending changes in the datacontext.ChangeSet?
Imagine that you have to edit order that is already placed. More than likely you will retrieve the order from the DB, store it in some session("EditedOrder") variable and go through a checkout-like editing process that spans over multiple screens until you reach the end where you will submit all changes back to DB and destroy session("EditedOrder")
well..if you decide to cancel the edit process in the middle and go back to the ViewOrder screen .. all partial changes will be shown because the datacontext is keeping track of them even though submitchanges has not been called yet.
If you do datacontext.refresh(refreshmode.OverwriteCurrentValue, order) it will not refresh any child collections (i.e. orderitems)
Worst is .. for some reason there is a datacontext.GetChangeSet method but nothing like datacontext.ClearChangeSet and all deletes,inserts,updates of ClearChangeSet are readonly collections
I hope you understand what I mean because sooner or later as your project grows you will hit the same roadblock.
Any ideas?
I loaded up the project (in VS2008 Express I'm afraid) and restored the database CommerceDB - all went well. On running the website everything looks great - until I try to add something to my cart, at that stage I'm told that 'LineItemPrice' does not exist. Damn! So I thought I'd try to add it into OrderItems - I guess you've just wrapped up a slightly out of date database (or my setup is seriously flawed!).
That got over the first hurdle - I added the item to the cart and... failed again. On line 125 of OrderService.cs:-
public Order GetCurrentOrder(string userName)
{
Order result= _orderRepository.GetOrders().CurrentOrderForUser(userName).SingleOrDefault();
Telling me that the cast is not valid (integer must be less than infinity) - although where that is within this statement, I'm not sure... perhaps in LINQ within GetOrders?
It's 3am now and I have to go get some sleep before I'm interupted by the heating system salesman in a few hours... Any ideas much appreciated as what you're doing is exactly what I want - and I much prefer this architecture (MVC) to the page style architecture that I had forced myself to get used to!
THANK YOU!
(Looks like Mike posted before I'd finished writing/investigating - but the thanks still count, right?)
I am having a few issues and would appreciate some help
a) I am unable to restore the database. The restore fails with a (Access is denied) error.
I can see that the Username is : Redmond\robcon and the server name is PEAHI\SQLExpress
How do I override these to restore the database
b) Is there some test configuration I am missing? I am using Resharper 4.1 and when I try to run the tests, all of them are being ignored. However I am able to "Debug" the selected tests.
Your help is appreciated
Thanks
-RVZ
Now I REALLY should get some sleep... but now it's working I'm tempted to play some more... Damn my over developed sense of curiosity...
Thanks again!
I was wondering if you could provide your take on the current debate about Linq To SQL being dead in favour of Linq To Entities. Also how you could refactor your application to use each one.
A few people have commented on the whole linq to sql beaing dead:
http://ayende.com/Blog/archive/2008/10/31/microsoft-kills-linq-to-sql.aspx
http://christian.comunic.no/2008/10/31/linq-to-sql-dead/
Regards DotnetShadow
Thanks for all the hard work. The special guests -- Ayende, Scott, Jeremy, et al. -- are a great touch and round out the segments.
I have been trying to learn MVC by applying your design to a new application. I have encountered a problem and your blog, and those that frequent it, might be the best place to pose the question.
My application is for expense reporting. In my app, users have approvers. In addition to the ubiquitous main admin, there will also be administrators who manage all users within certain departments.
I could get users their data this way:
userService.GetAllUsers().WithDepartment(currentUser.Department)
and
userService.GetAllUsers().WithApprover(currentUser)
I need to restrict users from accessing data outside their own hierarchy, and outside of their own department. I don't want users to be able to hack the URL params to see how much their manager -- or, especially the CEO -- spent on hotels and meals last month.
I was thinking I would have to add a custom handler via Unity and decorate my method calls with the needed parameters. But the idea of creating a custom policy handler that accesses the data layer makes me wonder if I would violate some programming law.
Any ideas?
Thanks again for the hard work.
I would have a service method like GetUnapprovedUsers(approver). Then inside that I would do something like _repository.GetAllUsers().ForApprover(approver).ToList(). The approver being passed in would just be the approver's username that they use to login to your system.
ForApprover would look up the approver in a table that maps them to departments they are allowed to approve, then would look up users in those departments that need approving.
Does that fit what you're trying to do?
On a side note, your code sample looks like you're trying to use filter extensions on a service call. You probably don't want to do that since the service makes a database call when it returns the IList. So you'd be getting all the data from the database then deciding not to return some of it which isn't very efficient. Where you would normally use an extension filter is on the repository since it returns an IQueryable which hasn't executed a call to the database yet.
Good luck with your project,
Brian
I was hoping that I wouldn't have to filter like that, but I guess it's a necessary evil.
My hope was that I could pre-invoke check the current principal against their parameters somehow to authorize. But, that involves a database call that creates overhead -- and some really bad performance hitches later. I guess I can at least perform role security on the more obvious methods.
This means that I have to simply filter the user and return nothing when their parameters require a trip outside their sphere of data.
You answered my question. Thanks!
1: http://www.codeplex.com/mvcsamples/SourceControl/FileView.aspx?itemId=286456&changeSetId=17126
You can help me by focusing on the bigger picture (meant with all due respect) - I need help with the checkout and usability experience :). Also would love feedback on the manageability of the app, etc. Using statements are the least of my concern right now.
To help gather feedback, do you have a public version of the site running anywhere? Maybe a public instance (auto-created from a check-in build-process, of course ;-)) would allow more casual devs to help out. I've watched all of the videos and read through much of the source code on Codeplex, but I've never actually gotten it up and running on my box (due to DB and other configuration issues I encountered the one time I tried). Maybe I'll contribute an MSBuild script to install and populate a fresh instance of the DB, since others seem to be having issues there (as did I).
As for ReSharper, I'm surprised you don't like the experience. It can be a bit intrusive at times, but through its options, you can really fine-tune the level of critique it provides.
I've been following your project with great interest. I am experiencing issues with restoring the database as many others.
When attempting to restore CommerceDB I get this error message:
Directory lookup for the file "D:\@SQL\Commerce.MVC.mdf" failed with the operating system error 21(The device is not ready).
When adding the Commerce.MVC.bak file to the list of restore devices I get this error message:
CommerceDB.bak is not part of a multiple family media set. BACKUP WITH FORMAT can be used to form a new media set. RESTORE HEADERONLY ist terminating abnormally.
I hope these error messages are helpful.
Mahalo Nui Loa
Eric
Here are some things I found with this preview:
1) Switched to CommerceDB? Seems to be some duplicate entries in web.config. Is the connection string even needed there anymore? Also, duplicate "routing" entry in the namespaces section.
2) LineItemPrice - missing column in OrderItems table (CommerceDB). So can't add product to cart. I see its use in "SaveItems" in SqlOrderRepository, dbml, etc.
3) Microsoft.Web.Mvc -- your version is 1.0.30826.0. The Futures Beta 1 is 1.0.31003.0 ... I noticed because I was curious how you were going to update "Html.RowButtonList" (in finalize.aspx) since the methods overloads have change dramatically from Preview 5.
Questions:
Was curious about the change to add "ProductSummaryDisplay". Was there an issue passing the anonymous Object via RenderPartial helper?
By leaving the assembly references in web.config (abstraction,mvc,routing), does the GAC take precedence?
Would moving the "web.config" from Views to App root be beneficial with this modifed structure?
Thanks, hope the information is helpful. I shopping cart is going to be a great product, and a terrific learning tool... I know I have learned a TON!!
When I downloaded the sample I was able to get it to run...but dont want to use OpenId..rather user ASP.NET membership. I see that the membership stuff is not fully implemented. Should I try to plugin ASP.NET membership or are you planning to have updated code at some latter time. Any help would be awsome to get me moving along. I also noticed that some of the IUserRepository and the User entity were not a part of the solution.
Are you planning on to show us localization and binding (IModelBinder that is) features? It would be great...
\^^/ you rock \^^/
Thanks!
but it kept on giving me a [MissingMethodException: Method not found: 'Void System.Web.Mvc.UrlHelper..ctor(System.Web.Mvc.ViewContext)'.]
error. Any ideas?
Maybe I am missing something in the code but how do you handle product attributes, like sizes, colors or other variations? Do you have a unique product record for each red shirt (small through large)? Do you do any type of grouping of those products? Thanks.
Here are some things I found with this preview:
1) Switched to CommerceDB? Seems to be some duplicate entries in web.config. Is the connection string even needed there anymore? Also, duplicate "routing" entry in the namespaces section.
2) LineItemPrice - missing column in OrderItems table (CommerceDB). So can't add product to cart. I see its use in "SaveItems" in SqlOrderRepository, dbml, etc.
3) Microsoft.Web.Mvc -- your version is 1.0.30826.0. The Futures Beta 1 is 1.0.31003.0 ... I noticed because I was curious how you were going to update "Html.RowButtonList" (in finalize.aspx) since the methods overloads have change dramatically from Preview 5.
Questions:
Was curious about the change to add "ProductSummaryDisplay". Was there an issue passing the anonymous Object via RenderPartial helper?
By leaving the assembly references in web.config (abstraction,mvc,routing), does the GAC take precedence?
Would moving the "web.config" from Views to App root be beneficial with this modifed structure?
Thanks, hope the information is helpful. I shopping cart is going to be a great product, and a terrific learning tool... I know I have learned a TON!!
Yes, I haven't changed to using the futures version of the helpers.
RE ProductSummaryDisplay - you can't "set properties" anymore with RenderPartial, and I was jumping through some messed up ViewData hacks to get it to work - so I just made a new display.
Yes - the GAC always takes precedence.
The web.config has to be in the root unfortunately - but I'll probably add one to App to keep it from being browsable.
Thanks for the help :)
I loaded up the project (in VS2008 Express I'm afraid) and restored the database CommerceDB - all went well. On running the website everything looks great - until I try to add something to my cart, at that stage I'm told that 'LineItemPrice' does not exist. Damn! So I thought I'd try to add it into OrderItems - I guess you've just wrapped up a slightly out of date database (or my setup is seriously flawed!).
That got over the first hurdle - I added the item to the cart and... failed again. On line 125 of OrderService.cs:-
public Order GetCurrentOrder(string userName)
{
Order result= _orderRepository.GetOrders().CurrentOrderForUser(userName).SingleOrDefault();
Telling me that the cast is not valid (integer must be less than infinity) - although where that is within this statement, I'm not sure... perhaps in LINQ within GetOrders?
It's 3am now and I have to go get some sleep before I'm interupted by the heating system salesman in a few hours... Any ideas much appreciated as what you're doing is exactly what I want - and I much prefer this architecture (MVC) to the page style architecture that I had forced myself to get used to!
THANK YOU!
(Looks like Mike posted before I'd finished writing/investigating - but the thanks still count, right?)
Now I REALLY should get some sleep... but now it's working I'm tempted to play some more... Damn my over developed sense of curiosity...
Thanks again!
Order controller: ItemAdded has invalid parameter comment and userName is never used
Authentication controller:
OpenIDLogin function: isValid variable doesn't appear to be used
Personalization controller requires an instance of an orderService but doesn't appear to get used.
sqlOrderRepository : SaveAddress: variable isInsert is defined but never used
CatalogFilters: Check parameter name comments on every function
Model.CreditCard: IsValid and MaskedNumber - Check parameter comment
CatalogService constructor parameter comment
OrderService GetRecommended has an unreachable return statement (last line)
MigrateCurrentOrder elseif has a check on toOrder but toOrder will never be null there
ViewExtensions: ToFormattedList: variable en is never used. Also check paramter comments.
RegisterScript: variables scriptLibFile & result are never used
RegisterJS: variables scriptRoot, scriptFormat, scriptLibFile don't appear to be used
RenderCommerceControl check parameter comments
Looks like you need ReSharper :)
My trial version is about to expire :(
I am having a few issues and would appreciate some help
a) I am unable to restore the database. The restore fails with a (Access is denied) error.
I can see that the Username is : Redmondrobcon and the server name is PEAHISQLExpress
How do I override these to restore the database
b) Is there some test configuration I am missing? I am using Resharper 4.1 and when I try to run the tests, all of them are being ignored. However I am able to "Debug" the selected tests.
Your help is appreciated
Thanks
-RVZ
Thanks for all the hard work. The special guests -- Ayende, Scott, Jeremy, et al. -- are a great touch and round out the segments.
I have been trying to learn MVC by applying your design to a new application. I have encountered a problem and your blog, and those that frequent it, might be the best place to pose the question.
My application is for expense reporting. In my app, users have approvers. In addition to the ubiquitous main admin, there will also be administrators who manage all users within certain departments.
I could get users their data this way:
userService.GetAllUsers().WithDepartment(currentUser.Department)
and
userService.GetAllUsers().WithApprover(currentUser)
I need to restrict users from accessing data outside their own hierarchy, and outside of their own department. I don't want users to be able to hack the URL params to see how much their manager -- or, especially the CEO -- spent on hotels and meals last month.
I was thinking I would have to add a custom handler via Unity and decorate my method calls with the needed parameters. But the idea of creating a custom policy handler that accesses the data layer makes me wonder if I would violate some programming law.
Any ideas?
Thanks again for the hard work.
I would have a service method like GetUnapprovedUsers(approver). Then inside that I would do something like _repository.GetAllUsers().ForApprover(approver).ToList(). The approver being passed in would just be the approver's username that they use to login to your system.
ForApprover would look up the approver in a table that maps them to departments they are allowed to approve, then would look up users in those departments that need approving.
Does that fit what you're trying to do?
On a side note, your code sample looks like you're trying to use filter extensions on a service call. You probably don't want to do that since the service makes a database call when it returns the IList. So you'd be getting all the data from the database then deciding not to return some of it which isn't very efficient. Where you would normally use an extension filter is on the repository since it returns an IQueryable which hasn't executed a call to the database yet.
Good luck with your project,
Brian
I was hoping that I wouldn't have to filter like that, but I guess it's a necessary evil.
My hope was that I could pre-invoke check the current principal against their parameters somehow to authorize. But, that involves a database call that creates overhead -- and some really bad performance hitches later. I guess I can at least perform role security on the more obvious methods.
This means that I have to simply filter the user and return nothing when their parameters require a trip outside their sphere of data.
You answered my question. Thanks!
DB is not restore..
1: http://www.codeplex.com/mvcsamples/SourceContro...
You can help me by focusing on the bigger picture (meant with all due respect) - I need help with the checkout and usability experience :). Also would love feedback on the manageability of the app, etc. Using statements are the least of my concern right now.
To help gather feedback, do you have a public version of the site running anywhere? Maybe a public instance (auto-created from a check-in build-process, of course ;-)) would allow more casual devs to help out. I've watched all of the videos and read through much of the source code on Codeplex, but I've never actually gotten it up and running on my box (due to DB and other configuration issues I encountered the one time I tried). Maybe I'll contribute an MSBuild script to install and populate a fresh instance of the DB, since others seem to be having issues there (as did I).
As for ReSharper, I'm surprised you don't like the experience. It can be a bit intrusive at times, but through its options, you can really fine-tune the level of critique it provides.
I've been following your project with great interest. I am experiencing issues with restoring the database as many others.
When attempting to restore CommerceDB I get this error message:
Directory lookup for the file "D:@SQLCommerce.MVC.mdf" failed with the operating system error 21(The device is not ready).
When adding the Commerce.MVC.bak file to the list of restore devices I get this error message:
CommerceDB.bak is not part of a multiple family media set. BACKUP WITH FORMAT can be used to form a new media set. RESTORE HEADERONLY ist terminating abnormally.
I hope these error messages are helpful.
Mahalo Nui Loa
Eric
I was wondering if you could provide your take on the current debate about Linq To SQL being dead in favour of Linq To Entities. Also how you could refactor your application to use each one.
A few people have commented on the whole linq to sql beaing dead:
http://ayende.com/Blog/archive/2008/10/31/micro...
http://christian.comunic.no/2008/10/31/linq-to-...
Regards DotnetShadow
Awesome job all this by the way.
As I see this series of webcast coming to a close I would like to ask you if you would consider doing a webcast on the architecture of the whole thing. Myself, and many others I presume, are new developers and we would greatly benefit from a video that walks through the application from a user perspective, that shows how all the pieces are connected together.
For example, when a user logins by pressing the login button, what happens in the background that results in the page or view the user sees as a result? Since this Storefront may be used by many novices like me, I am hoping that there is value in this video request.
In any case, great job!
Best regards,
Antoine@Toronto
I think when this is all over I'll do a quick overview of each system and why I made it that way - that's a good idea.
I keep getting an error when starting the solution.
Invalid column name 'CategoryName'.
"Category category = _catalogService.GetCategory(categoryID) ??
Source File: E:mvcdemostorefrontMVCStore_Preview1ACommerce.WebAppControllersCatalogController.cs Line: 32
Any ideas
The Beta is looking sweet. Excellent work. I am following your progress and working on a similar project. Gotta tell you - learning a lot of things here.
I have a quick question though.
how in the world do you clear all pending changes in the datacontext.ChangeSet?
Imagine that you have to edit order that is already placed. More than likely you will retrieve the order from the DB, store it in some session("EditedOrder") variable and go through a checkout-like editing process that spans over multiple screens until you reach the end where you will submit all changes back to DB and destroy session("EditedOrder")
well..if you decide to cancel the edit process in the middle and go back to the ViewOrder screen .. all partial changes will be shown because the datacontext is keeping track of them even though submitchanges has not been called yet.
If you do datacontext.refresh(refreshmode.OverwriteCurrentValue, order) it will not refresh any child collections (i.e. orderitems)
Worst is .. for some reason there is a datacontext.GetChangeSet method but nothing like datacontext.ClearChangeSet and all deletes,inserts,updates of ClearChangeSet are readonly collections
I hope you understand what I mean because sooner or later as your project grows you will hit the same roadblock.
Any ideas?
that an Expression is actually built backwards, and the projection is called
when the thing is executed, not before or after. I know it looks like magic
(and in many cases you might even call it Voodoo) - but this doesn't happen.
The only thing that will fire the query is enumerating or singling it -
appending an IQueryable expression to it will not do this. Unless you've
seen otherwise?
http://odetocode.com/Blogs/scott/archive/2008/0...
When I downloaded the sample I was able to get it to run...but dont want to use OpenId..rather user ASP.NET membership. I see that the membership stuff is not fully implemented. Should I try to plugin ASP.NET membership or are you planning to have updated code at some latter time. Any help would be awsome to get me moving along. I also noticed that some of the IUserRepository and the User entity were not a part of the solution.
Are you planning on to show us localization and binding (IModelBinder that is) features? It would be great...
^^/ you rock ^^/
Thanks!
but it kept on giving me a [MissingMethodException: Method not found: 'Void System.Web.Mvc.UrlHelper..ctor(System.Web.Mvc.ViewContext)'.]
error. Any ideas?
Maybe I am missing something in the code but how do you handle product attributes, like sizes, colors or other variations? Do you have a unique product record for each red shirt (small through large)? Do you do any type of grouping of those products? Thanks.