ASP MVC Inventory management checkout process help
Hi all, I have been working for many months in school to complete this project and am getting to the final stretch but was hoping to get some advice on my check out process. Essentially users can navigate to the items page, add items to their cart and then view the cart, edit quantitities and check out. Some items can be returned as well so checked-out items have a null field for return date that is populated with a DateTime when returned. I have not gotten the check-in function working at all and am not quite finished with check out, I still need to get validation and the cart view to work properly. I have included some of the relevant files to my use cases and would like to know if I am on the right track and how you would recommend I get this process working. Thank you again and if you are willing to voice chat please let me know.
156 Replies
To get the best possible help, its recommended to ask a specific question, preferably about some specific code.
and if u have a github that would be even easier to follow as u can quote specific lines of the code
Dumping 10 files of MVC code and saying "is this good?" is more akin to a code review, which we also do (but in #code-review ) and should ideally wait until its finished.
The next step I am trying to figure out is in my AddToCart function in the cart controller
unfortunately your files are too long to be shown inline in discord, and downloading them as.txt files is a bit annoying
Github would be ideal, but $paste can work too
If your code is too long, you can post to https://paste.mod.gg/ and copy the link into chat for others to see your shared code!
Oh okay, I would love to just give the repo on Github but I don't know if my other group members would be okay with that
I can do the paste.mod
Thank you for the tips, I'll do that right away
paste.mod
u can add new tabs
so u can add many files separated
and then saving will give u a link with everything together
BlazeBin - uljhkvfgyfhp
A tool for sharing your source code with the world!
I only included a couple of files to focus on but can add more as needed
Line 55 of CartController is what Im working on now
I need to make it verify if the quantity requested is above the max available
u need to check against your inventory
yeah currently I can pass whatever number I want in for
itemId
and your system will allow it
also, is that redirect appropriate?
otherwise that seems okay at a glance, if very basicI'm not sure why it wouldn't be?
When I add something to a cart, I don't expect to be redirected to the details page for that item
granted, I dont know how your shop works atm
Ah I see, it's because they can only be added on the details page
So it doesn't move pages
ok. thats not ideal UX, but its fine for now
The item details page just basically allows them to move to cart or go back and add more
Should I have it just boot them back a page to add more items and have a cart button in the corner?
if you know they came from the details page, going back there seems fine
Yeah, they always do
normally u would use jquery/ajax to do the request adding to the cart so it doesn't have to redirect or anything
^
(or just fetch) 😄
I'm not sure how
javascript
you'd issue a request in the background so as to not hijack the browser
and then use MORE javascript to refresh the cart
Like Async
but thats a slightly more advanced variation here
Hm. Alright
with async for sure, but this isnt related to async itself
The function does interact with my Javascript as well, which is at the bottom of item details page
Is that Ajax?
no
there is something called "the fetch api" or just
fetch
which is the modern way to make XHR requests from javascript
this is how modern websites make requests without interrupting the browser
before fetch, we had to rely on an old-school method called ajax or jqueries alternatives to do the same
you seem to be using javascript to create a form element and submitting it via code, which is very similar to just making a post request with fetch
, except a form submit will hijack the browserOh okay, I think I'm fetching now? I'm sorry if I'm a bit unknowlegable this is my first time with the architecture
we cant see any changes you make btw
this is why we prefer github when possible
regardless, moving over to fetch and doing the cart logic in the background would be a major change to your application
not something I'd recommend if you just want to finish this up
Gotcha
Ok, thank you I'll try to fix the validation and let you guys know if I run into anything more specific
I've just been feeling paralyzed by the scope of this project, even if it looks very simple
a bit unrelated to the topic, but is there any specific reason why you use concrete classes in type declarations?
Can you give me an example?
i.e.: in
CartViewModel
public List<CartItemViewModel>? CartItems { get; set; }
any specific reason not declaring as IList<...> ?
As per SOLID principle you should depend on abstractions, hence the IList.
For built in types it might not be that harsh, yet it carries the promise of a future refactor.
Ofc there are cases when there is a good valid reason to do that. I am just unsure if there is one here?
Also, if this is a school project, whoever review this might address this concern?
Also, as an employee, a senior developer might challenge you on that so it does not seems to be a bad habit to adopt it.Oh, I see
Yeah I don't have a specific reason, I just didn't think to use a generic list because I am not as familiar with C#
https://paste.mod.gg/vtibvqfgpiky/3
Also I added the CartService because its where my AddToCart function works but I'm pretty sure it sucks lol
BlazeBin - vtibvqfgpiky
A tool for sharing your source code with the world!
When I try to add a second item it says that there is a thread already open
The method is line 97 of CartService
Because a
DbContext
is intended to be used for a single operation and you're caching it as a field within your CartService
(which I assume is a singleton). While you can reuse a DbContext
, it can cause stuff like this when you have async workflows and multiple requests attempt to run concurrently.
If you intend to keep CartService
as a singleton and don't want to include a fresh DbContext
as a parameter to your methods, you'll need to use an IDbContextFactory
as shown here: https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazorDbContext Lifetime, Configuration, and Initialization - EF Core
Patterns for creating and managing DbContext instances with or without dependency injection
Multiple requests should not be running concurrently though, users should only be able to add an item to the cart one at a time
Also how come in my SQL viewer it doesn't show the field for my inventory items list, did I do something wrong?
error says otherwise, something is attempting to execute concurrent requests to your dbcontext ¯\_(ツ)_/¯
Yeah and I have no idea why
I'll trace it and add breakpoints
Well, show your cart controller code
*cart service
BlazeBin - vtibvqfgpiky
A tool for sharing your source code with the world!
cart service, not cart controller
Its the 4th file
Cart service
oh, didn't realize there were multiple files
meeting though, will be slow to respond
Gotcha
Okay no problem, thank you
Show your startup.cs file as well, need to see how you're setting up your DI
how it is registered to the DI container?
I second to @ded assumption that it is the lifetime that is incorrect
https://paste.mod.gg/poewwqrjikik/4
I added Program.cs my startup file
BlazeBin - poewwqrjikik
A tool for sharing your source code with the world!
IIRC,
DbContext
by default is request scoped, so you'll get a single DbContext
instance from the DI container per-request (without using an IDbContextFactory
).
My assumption is that you're getting the same instance injected into everything and somewhere in there you have two overlapping async requests.I don't want two overlapping async requests
I have no idea where I do that
DI lifetime issue is not confirmed to me based on program.cs
can you add InventoryService as well?
BlazeBin - nrppferesfjc
A tool for sharing your source code with the world!
don't need inventory service
Added
var isConsumable = IsConsumableAsync(itemID);
this is your problem
async call without an awaitAh
well, I think that's the problem, there could be other cases of it
isnt the IDE warning for it though?
How can I fix it? isConsumable is just an attribute of InventoryItem is there a beter way to check?
I dont think so?
it's in an async method, just
await
it lol
it's part of your AddToCartAsync
method in CartService
What does await even do?
thats deep water 🙂
https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/async-scenarios
waits for an async operation to complete before continuing
Thank you
I awaited it
I've now realized that itemDetails is not properly passing quantity and is for some reason always 1
Do I need to add a hidden input field or something?
I'm not even sure how its getting to the constructor without passing the specific quantity or why it always equals 1
It looks like it should get it
when it comes to javascript, anything can happen 🙂
You intialize quantity with the default value of 1
It seems you ain't even hit the
if
block, thus it remains on the default. Thus it either means isConsumable
and/or quantitySelect
is null / undefined.
Later on you do add it to the form
also I believe isConsumable is already a boolean. I doubt the
isConsumable === 'True'
is needed in the function call. you can safely add isConsumable
simplyfunction addToCart(itemID, itemName, price, isConsumable) {
price is redundant here. you aint use it in the given function
if you have 2000 on stock, this select
element will be very awkward.
I would set some limit to it (you know what is reasonable based on context)
or what is a common solution for it is to add a +
/ -
button to increase / decrease the quantity. And disable the + button if you selected QuantityInstock
and disable the - button if you hit 0.Thank you for the advice and I will change that in my view, you make a good point. However I am now really struggling to assign the items in my cart a quantity. I added a list of item quantities and have no idea how to implement it
I am very confused
I tried doing this but its making no sense
Don't think you can use DbContextFactory along with DbContext registered as scoped because it will select the first one that is registered. You can work around this ,but should probably use IServiceScopeFactory to resolve scoped services in a singleton to avoid captive dependecy
I think I've resolved that issue anyway
How?
It is all singleton
I didnt need a factory
Wait you are registering dbcontext as a singleton?
I dont know what that means
but yes
you shouldnt do that
Then no
they didn't register it as a singleton, it was scoped
they just messed up and forgot to
await
an async db operationMy real issue is I have been working for 5 hours to implement the simplest thing and I cant do it
I feel like a complete idiot
alright, but my point still stands, id use iservicescopefactory to resolve dbcontext in a singleton rather than dbcontextfactory
their cart service isn't a singleton either
Can you please help me with the quantity
Either of you
Please
ah ok, anyway dont fret about things taking time. they always do, and you will get better
Id just have quantity as a primitive (int) rather than a dedicated class
I have no idea what I am doing appartently and cant get a simple list to work
How would I do that?
Its not quantity for one item, its for a whole cart/ list of items
No, sorry. You should create a CartItem class that has the item id and orderquantity int
Or should and should, thats what id do at least
you already have a cartitem, but move the quantity into it
I tried that first but then I would have to change all of my code that uses the cart items list
First off, I'd create a
CartItem
model that references InventoryItem
and has a field for quanitity. Then keep a list of those in your cart.well, sometimes you need to change your code
you shouldnt hold on to it like its holy
if refactoring will improve your code, you should refactor
And then I tried changing all of it to fit that model and it broke in a way that couldn't fix
But I'll try it again
most problems originate from bad data structures
i.e. not thinking through your data structures and how they will affect the way you write your code
Do you have a reason to keep
CartItemQuantityID
?Dont I still need a primary key?
I'd personally use a composite key (of cart ID and item ID) in this instance, but that's my own preference
And since it's no longer
CartItemQuantity
, I'd rename the variable to reflect the new modelSo can I just make the model Inventory Item and Quantity?
Depends.
If you want primary key - you need an ID field similar to what you have
If you want composite key - set up navigation properties for the cart and inventory (I usually add a foreign key field for each 1-1 nav property) and then use the fluent api to configure your relations
Primary key is simpler and requires less knowledge of entity framework to set up.
Yeah I just made it CartItemID
Don't forget to update the type of your list in
Cart
.And thats where there 11 errors roll in
I highly recommend using
string
over String
, we ain't writing Java. Also, I'd recommend using navigation properties alongside foreign keys, never foreign keys aloneThis will probably take a long time to change, huh?
Why?
Whats wrong with String
You can technically write a custom class/struct named
String
No such danger with string
Since it's a reserved keywordidk how I glossed over the use of
String
, lol, been looking at too much java at workOh, that makes sense
Thanks for the clarification
I just changed all of the instances of InventoryItem to CartItem where neccesary
Weird
Assuming your database has no real data in it... I'd just drop and re-create from a fresh migration.
Yeah I did that
It worked
Had to do it twice lol
How would you reccomend I fix this?
What's the issue?
I forgot what to put at line 120, I need to make a new Cart Item for inventory item using itemID
Give it a try first and show me what you come up with. No better way to learn than experiment and make mistakes.
You're right
Turns out I never even made a method to get item by ID
Lol
luckily you've got an example on 115 to use
I can do that with just the item ID?
Oh
I am being silly
My brain is frying
Protip: use the
DbSet<T>
properties of DbContext instead of .Set<T>
for added free safetyHow does that get me added free safety?
I dont even know what it means
You can do
.Set<int>()
and EF would not know what to do with it
But the DbContext
can have only the sets that are actually valid
Which saves you from potentially writing code that would cause a runtime errorOh cool! Thanks
I still am not sure if theres an easy way to just find one inventory item by its ID without adding a new GetInventoryItemByID Method in the controller/service
Why?
That is, why avoid adding a new method?
no, its has scoped lifecycle
Yeah at this point I'll just make the new method
also having an entity (matching db set) should not neccessarily mirror to the UI.
use logical DTOs to transfer and transform the data between different layers
which I believe is the case here as well
How would you make a GetInventoryItemByID method? Becuase I can't figure it out
What part of making the method are you stuck on?
Just a method with this code inside
I cant use FirstOrDefault because it doesnt work
Isnt that only for lists?
the issue here is not the method. but you miss what to assign it for
FirstOrDefault of what?
DbSet
is a collection. .FirstOrDefault()
does work with collections.you are using an object initializer, but no property is listed, to which you assign the value retrieved in the method
Or, well,
IQueryable
to be precise
And, yes, it fetches the data, and... does nothing with itWhat property of
CartItem
do you want to assign it to?
the
Id =
part is missing
or whatever is the property nameInventoryItem
Then do that'
Oh
This will work?
And a semicolon
With a semicolon, yes
semicolon after ==> this
}
Got it
Thank you
I am just getting burnt out I should've realized I could use DBSet
Well, it wouldn't make much difference in your current code
Just would provide some nice and cozy safety
I kept getting this one too
And no idea why it didnt set automatically
I set it manually but its still weird to me
my impression is you are kinda lost in the woods. Happens when one delves too much into the details. My advise would be to make a step back and design it from a bird-eye view distance.
You have an app --> in the app you have loged in
You shall have separate models for backup up Views. (View, ViewModel) You want to have specific DTOs based on your business need in your services And you want to have specific classes representing database entitites.
If you are using EF az ORM you want to set up DbSets and configure Entity Framework. There several ways to do it, I suggest FluentAPI. When you create your entities representing your DB tables, you want to have a primary key (for start use int?) and use Navigation properties (not a must and comes with some issues, but in the beginning it helps a lot) you have set the Id of
user
--> the user might have a Cart
hosted in a view
--> the view is backed with a Viewmodel
--> The viewmodel supports probably the Cart
and the InventoryItems
collection --> upon interaction, you want to move from this collection items to the cart --> you have a collection of CartItems
s --> each CartItem
you have quantities picked by the user and the InventoryItemIds
as reference.
Another aspect you might consider is how to layer the application. Separation of Concerns
is your keyword to look for.You shall have separate models for backup up Views. (View, ViewModel) You want to have specific DTOs based on your business need in your services And you want to have specific classes representing database entitites.
If you are using EF az ORM you want to set up DbSets and configure Entity Framework. There several ways to do it, I suggest FluentAPI. When you create your entities representing your DB tables, you want to have a primary key (for start use int?) and use Navigation properties (not a must and comes with some issues, but in the beginning it helps a lot) you have set the Id of
CartViewModel
, not CartItem
I do have EF set up
And mostly every class is in its own file
But a couple like cart item I put with cart
Oh
How the heck did I do that? lol
Cart view model is not set