C
C#•9mo ago
Mrguy235

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
Pobiega
Pobiega•9mo ago
To get the best possible help, its recommended to ask a specific question, preferably about some specific code.
leowest
leowest•9mo ago
and if u have a github that would be even easier to follow as u can quote specific lines of the code
Pobiega
Pobiega•9mo ago
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.
Mrguy235
Mrguy235OP•9mo ago
The next step I am trying to figure out is in my AddToCart function in the cart controller
Pobiega
Pobiega•9mo ago
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
MODiX
MODiX•9mo ago
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!
Mrguy235
Mrguy235OP•9mo ago
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
leowest
leowest•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
BlazeBin - uljhkvfgyfhp
A tool for sharing your source code with the world!
Mrguy235
Mrguy235OP•9mo ago
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
leowest
leowest•9mo ago
u need to check against your inventory
Pobiega
Pobiega•9mo ago
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 basic
Mrguy235
Mrguy235OP•9mo ago
I'm not sure why it wouldn't be?
Pobiega
Pobiega•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
Ah I see, it's because they can only be added on the details page So it doesn't move pages
Pobiega
Pobiega•9mo ago
ok. thats not ideal UX, but its fine for now
Mrguy235
Mrguy235OP•9mo ago
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?
Pobiega
Pobiega•9mo ago
if you know they came from the details page, going back there seems fine
Mrguy235
Mrguy235OP•9mo ago
Yeah, they always do
leowest
leowest•9mo ago
normally u would use jquery/ajax to do the request adding to the cart so it doesn't have to redirect or anything
Pobiega
Pobiega•9mo ago
^ (or just fetch) 😄
Mrguy235
Mrguy235OP•9mo ago
I'm not sure how
Pobiega
Pobiega•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
Like Async
Pobiega
Pobiega•9mo ago
but thats a slightly more advanced variation here
Mrguy235
Mrguy235OP•9mo ago
Hm. Alright
Pobiega
Pobiega•9mo ago
with async for sure, but this isnt related to async itself
Mrguy235
Mrguy235OP•9mo ago
The function does interact with my Javascript as well, which is at the bottom of item details page Is that Ajax?
Pobiega
Pobiega•9mo ago
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 browser
Mrguy235
Mrguy235OP•9mo ago
Oh okay, I think I'm fetching now? I'm sorry if I'm a bit unknowlegable this is my first time with the architecture
Pobiega
Pobiega•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
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
Neophyte
Neophyte•9mo ago
a bit unrelated to the topic, but is there any specific reason why you use concrete classes in type declarations?
Mrguy235
Mrguy235OP•9mo ago
Can you give me an example?
Neophyte
Neophyte•9mo ago
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.
Mrguy235
Mrguy235OP•9mo ago
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#
Mrguy235
Mrguy235OP•9mo ago
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!
Mrguy235
Mrguy235OP•9mo ago
When I try to add a second item it says that there is a thread already open
Mrguy235
Mrguy235OP•9mo ago
No description
Mrguy235
Mrguy235OP•9mo ago
The method is line 97 of CartService
Keswiik
Keswiik•9mo ago
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-blazor
DbContext Lifetime, Configuration, and Initialization - EF Core
Patterns for creating and managing DbContext instances with or without dependency injection
Mrguy235
Mrguy235OP•9mo ago
Multiple requests should not be running concurrently though, users should only be able to add an item to the cart one at a time
Mrguy235
Mrguy235OP•9mo ago
Also how come in my SQL viewer it doesn't show the field for my inventory items list, did I do something wrong?
No description
No description
Keswiik
Keswiik•9mo ago
error says otherwise, something is attempting to execute concurrent requests to your dbcontext ¯\_(ツ)_/¯
Mrguy235
Mrguy235OP•9mo ago
Yeah and I have no idea why I'll trace it and add breakpoints
Keswiik
Keswiik•9mo ago
Well, show your cart controller code *cart service
Mrguy235
Mrguy235OP•9mo ago
BlazeBin - vtibvqfgpiky
A tool for sharing your source code with the world!
Keswiik
Keswiik•9mo ago
cart service, not cart controller
Mrguy235
Mrguy235OP•9mo ago
Its the 4th file Cart service
Keswiik
Keswiik•9mo ago
oh, didn't realize there were multiple files meeting though, will be slow to respond
Mrguy235
Mrguy235OP•9mo ago
Gotcha Okay no problem, thank you
Keswiik
Keswiik•9mo ago
Show your startup.cs file as well, need to see how you're setting up your DI
Neophyte
Neophyte•9mo ago
how it is registered to the DI container? I second to @ded assumption that it is the lifetime that is incorrect
Mrguy235
Mrguy235OP•9mo ago
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!
Keswiik
Keswiik•9mo ago
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.
Mrguy235
Mrguy235OP•9mo ago
I don't want two overlapping async requests I have no idea where I do that
Neophyte
Neophyte•9mo ago
DI lifetime issue is not confirmed to me based on program.cs can you add InventoryService as well?
Mrguy235
Mrguy235OP•9mo ago
BlazeBin - nrppferesfjc
A tool for sharing your source code with the world!
Keswiik
Keswiik•9mo ago
don't need inventory service
Mrguy235
Mrguy235OP•9mo ago
Added
Keswiik
Keswiik•9mo ago
var isConsumable = IsConsumableAsync(itemID); this is your problem async call without an await
Mrguy235
Mrguy235OP•9mo ago
Ah
Keswiik
Keswiik•9mo ago
well, I think that's the problem, there could be other cases of it
Neophyte
Neophyte•9mo ago
isnt the IDE warning for it though?
Mrguy235
Mrguy235OP•9mo ago
How can I fix it? isConsumable is just an attribute of InventoryItem is there a beter way to check? I dont think so?
Keswiik
Keswiik•9mo ago
it's in an async method, just await it lol it's part of your AddToCartAsync method in CartService
Mrguy235
Mrguy235OP•9mo ago
What does await even do?
Keswiik
Keswiik•9mo ago
waits for an async operation to complete before continuing
Mrguy235
Mrguy235OP•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
It looks like it should get it
No description
Neophyte
Neophyte•9mo ago
when it comes to javascript, anything can happen 🙂 You intialize quantity with the default value of 1

let quantity = 1; // Default quantity

// If the item is consumable, get the selected quantity from the dropdown
if (isConsumable && quantitySelect != null) {
quantity = parseInt(document.getElementById('quantitySelect').value, 10);
}

let quantity = 1; // Default quantity

// If the item is consumable, get the selected quantity from the dropdown
if (isConsumable && quantitySelect != null) {
quantity = parseInt(document.getElementById('quantitySelect').value, 10);
}
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
let quantityInput = document.createElement('input');
quantityInput.type = 'hidden';
quantityInput.name = 'quantity';
quantityInput.value = quantity;
form.appendChild(quantityInput);
let quantityInput = document.createElement('input');
quantityInput.type = 'hidden';
quantityInput.name = 'quantity';
quantityInput.value = quantity;
form.appendChild(quantityInput);
Neophyte
Neophyte•9mo ago
also I believe isConsumable is already a boolean. I doubt the isConsumable === 'True' is needed in the function call. you can safely add isConsumable simply
No description
Neophyte
Neophyte•9mo ago
function addToCart(itemID, itemName, price, isConsumable) { price is redundant here. you aint use it in the given function
<select id="quantitySelect" class="form-control">
@for (int i = 1; i <= Model.Consumable.QuantityInStock; i++)
{
<option value="@i">@i</option>
}
</select>
<select id="quantitySelect" class="form-control">
@for (int i = 1; i <= Model.Consumable.QuantityInStock; i++)
{
<option value="@i">@i</option>
}
</select>
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.
Mrguy235
Mrguy235OP•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
I tried doing this but its making no sense
No description
linqisnice
linqisnice•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
I think I've resolved that issue anyway
linqisnice
linqisnice•9mo ago
How?
Mrguy235
Mrguy235OP•9mo ago
It is all singleton I didnt need a factory
linqisnice
linqisnice•9mo ago
Wait you are registering dbcontext as a singleton?
Mrguy235
Mrguy235OP•9mo ago
I dont know what that means but yes
linqisnice
linqisnice•9mo ago
you shouldnt do that
Mrguy235
Mrguy235OP•9mo ago
Then no
Keswiik
Keswiik•9mo ago
they didn't register it as a singleton, it was scoped they just messed up and forgot to await an async db operation
Mrguy235
Mrguy235OP•9mo ago
My 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
linqisnice
linqisnice•9mo ago
alright, but my point still stands, id use iservicescopefactory to resolve dbcontext in a singleton rather than dbcontextfactory
Keswiik
Keswiik•9mo ago
their cart service isn't a singleton either
Mrguy235
Mrguy235OP•9mo ago
Can you please help me with the quantity Either of you Please
linqisnice
linqisnice•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
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
linqisnice
linqisnice•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
I tried that first but then I would have to change all of my code that uses the cart items list
Keswiik
Keswiik•9mo ago
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.
linqisnice
linqisnice•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
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
linqisnice
linqisnice•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
No description
Keswiik
Keswiik•9mo ago
Do you have a reason to keep CartItemQuantityID?
Mrguy235
Mrguy235OP•9mo ago
Dont I still need a primary key?
Keswiik
Keswiik•9mo ago
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 model
Mrguy235
Mrguy235OP•9mo ago
So can I just make the model Inventory Item and Quantity?
Keswiik
Keswiik•9mo ago
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.
Mrguy235
Mrguy235OP•9mo ago
Yeah I just made it CartItemID
Keswiik
Keswiik•9mo ago
Don't forget to update the type of your list in Cart.
Mrguy235
Mrguy235OP•9mo ago
And thats where there 11 errors roll in
Angius
Angius•9mo ago
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 alone
Mrguy235
Mrguy235OP•9mo ago
This will probably take a long time to change, huh? Why? Whats wrong with String
Angius
Angius•9mo ago
You can technically write a custom class/struct named String No such danger with string Since it's a reserved keyword
Keswiik
Keswiik•9mo ago
idk how I glossed over the use of String, lol, been looking at too much java at work
Mrguy235
Mrguy235OP•9mo ago
Oh, that makes sense Thanks for the clarification I just changed all of the instances of InventoryItem to CartItem where neccesary
Mrguy235
Mrguy235OP•9mo ago
Weird
No description
Keswiik
Keswiik•9mo ago
Assuming your database has no real data in it... I'd just drop and re-create from a fresh migration.
Mrguy235
Mrguy235OP•9mo ago
Yeah I did that It worked Had to do it twice lol
Mrguy235
Mrguy235OP•9mo ago
How would you reccomend I fix this?
No description
Keswiik
Keswiik•9mo ago
What's the issue?
Mrguy235
Mrguy235OP•9mo ago
I forgot what to put at line 120, I need to make a new Cart Item for inventory item using itemID
Keswiik
Keswiik•9mo ago
Give it a try first and show me what you come up with. No better way to learn than experiment and make mistakes.
Mrguy235
Mrguy235OP•9mo ago
You're right Turns out I never even made a method to get item by ID Lol
Keswiik
Keswiik•9mo ago
luckily you've got an example on 115 to use
Mrguy235
Mrguy235OP•9mo ago
I can do that with just the item ID? Oh I am being silly My brain is frying
Angius
Angius•9mo ago
Protip: use the DbSet<T> properties of DbContext instead of .Set<T> for added free safety
Mrguy235
Mrguy235OP•9mo ago
How does that get me added free safety? I dont even know what it means
Angius
Angius•9mo ago
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 error
Mrguy235
Mrguy235OP•9mo ago
Oh cool! Thanks
Mrguy235
Mrguy235OP•9mo ago
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
No description
Angius
Angius•9mo ago
Why? That is, why avoid adding a new method?
Neophyte
Neophyte•9mo ago
no, its has scoped lifecycle
Mrguy235
Mrguy235OP•9mo ago
Yeah at this point I'll just make the new method
Neophyte
Neophyte•9mo ago
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
Mrguy235
Mrguy235OP•9mo ago
How would you make a GetInventoryItemByID method? Becuase I can't figure it out
Keswiik
Keswiik•9mo ago
What part of making the method are you stuck on?
Angius
Angius•9mo ago
Just a method with this code inside
No description
Mrguy235
Mrguy235OP•9mo ago
I cant use FirstOrDefault because it doesnt work Isnt that only for lists?
Neophyte
Neophyte•9mo ago
the issue here is not the method. but you miss what to assign it for
Mrguy235
Mrguy235OP•9mo ago
FirstOrDefault of what?
Angius
Angius•9mo ago
DbSet is a collection. .FirstOrDefault() does work with collections.
Neophyte
Neophyte•9mo ago
you are using an object initializer, but no property is listed, to which you assign the value retrieved in the method
Angius
Angius•9mo ago
Or, well, IQueryable to be precise And, yes, it fetches the data, and... does nothing with it
Mrguy235
Mrguy235OP•9mo ago
No description
Angius
Angius•9mo ago
What property of CartItem do you want to assign it to?
Neophyte
Neophyte•9mo ago
inventoryItem = new CartItem {
id = await _dbContext.....
}
inventoryItem = new CartItem {
id = await _dbContext.....
}
the Id = part is missing or whatever is the property name
Mrguy235
Mrguy235OP•9mo ago
InventoryItem
Angius
Angius•9mo ago
Then do that'
Mrguy235
Mrguy235OP•9mo ago
No description
Mrguy235
Mrguy235OP•9mo ago
Oh This will work?
Angius
Angius•9mo ago
And a semicolon With a semicolon, yes
Neophyte
Neophyte•9mo ago
semicolon after ==> this }
Mrguy235
Mrguy235OP•9mo ago
Got it Thank you I am just getting burnt out I should've realized I could use DBSet
Angius
Angius•9mo ago
Well, it wouldn't make much difference in your current code Just would provide some nice and cozy safety
Mrguy235
Mrguy235OP•9mo ago
I kept getting this one too
No description
Mrguy235
Mrguy235OP•9mo ago
And no idea why it didnt set automatically I set it manually but its still weird to me
Mrguy235
Mrguy235OP•9mo ago
No description
Neophyte
Neophyte•9mo ago
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 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 CartItemss --> 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
Mrguy235
Mrguy235OP•9mo ago
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
Want results from more Discord servers?
Add your server