Editing Partial Object in MVC without Actually Saving - c#

My title is probably terrible because I'm having trouble wording what I am trying to do.
I have an object that can potentially contain a huge number of records that looks something like this:
public class AssignmentGenerator : BaseGenerator
{
public bool IsLibrary { get; set; } = false;
public List<LineItem> LineItems { get; set; } = new List<LineItem>();
}
public class LineItem
{
public string Name { get; set; }
public string Value { get; set; }
}
I have a form created that allows editing of the values of the object, but it is possible for the list of line items to become very large (one example I have is ~ 3000). This being the case, I would like to make the line item list be a paged list in my view allowing editing of say 10 to 50 items at a time.
I've read a lot of tutorials and posts about how to do paging but none of the ones I've found go into how to do editing of a large set of data. I don't want to save the changes on each page to the database until the user actually clicks on the Save button. Is there a way to store the values in the object, retrieve them as needed, and then save upon user action?

The short answer is yes, there's a way - you're the programmer, you can do what you want. It's hard to give real code examples without more details, so below is just vague guidance.
You have to store their changes somewhere, but you can choose to save them in a staging database, or keep your AssignmentGenerator in memory on the server and just update the collection when they page (assuming 1 server or pinned sessions).
You will have to post the current state of the objects as the user changes pages (instead of just a Get endpoint). You don't have to save to the real database; you just update your temporary copy. The Save button should trigger a different controller action which moves your temporary copy to the real data store.

Related

What is the best way to write data via MVC into database?

I am working on a homework project using MVC with EF Core.
I am looking for the best way to write data into the database. (I am beginner)
There are two tables. Predbilježba(Enrollment) and Seminari(Seminars)
public class Predbilježba
{
[Key]
public int PredbilježbeID { get; set; }
public string Ime { get; set; }
public string Prezime { get; set; }
public string Adresa { get; set; }
public string Email { get; set; }
public string Telefon { get; set; }
public bool Status { get; set; }
[DataType(DataType.Date)]
public DateTime DatumUpisa { get; set; }
public int SeminarID { get; set; }
public Seminar Seminar { get; set; }
}
public class Seminar
{
public int SeminarID { get; set; }
public string Naziv { get; set; }
public string Opis { get; set; }
[DataType(DataType.Date)]
public DateTime Datum { get; set; }
public bool Popunjen { get; set; }
public ICollection<Predbilježba> Predbilježba { get; set; }
}
I need to insert a sort of Enrollment (Named: Predbilježba) into the database.
Enrollment is connected to a table called Seminars (Named: Seminari).
So when a person is "enrolling" into a "seminar", he/she needs to insert basic data into form (name, phone number, etc.) and they need to choose a "seminar" from a list of given seminars which are in "Seminar" table.
So when they click "Save", their basic data is written into Predbilježba / (eng. Enrollment)" along with chosen "seminar"
So I already have controllers for these 2 models, and appropriate views to create, edit, and so on..
My question is: Do I create a separate controller/model/view to insert data into tables? Can someone give me some example of how it is done?
To clarify further, I need to make user side page where user can "enroll" to "seminar" by writing name, last name, etc.. and by choosing the desired seminar. For now I have functional database, Identity (which will be used later in project), controllers of both models, and appropriate views where I can edit Prebilježbe(eng. Enrollments) and Seminars.
Images of page follow:
So when user clicks Upiši se (eng. enroll) as shown in image number 3. , that selected Seminar, along with basic info that opens after the click (image 4 ) needs to be written into database "Predbilježbe" (eng Enrollments)
This "Upis" page would be a user input page, and "Seminari" and "Predbilježbe" would be admin pages..
If I understand your question correctly, you are asking about good architectural design. Aren't you? (if not please let me know to edit the answer).
You have many architectural choices and possibilities. The easiest one for you to start with is the Service-Repository architectural pattern. I would omit the Repository word here because EF is already (in my opinion) a Repository pattern implementation (at least partially).
So to keep it simple, you would like to start with Service architectural pattern. Which is about creating a class, which injects the DbContext in its construction (let's name it PredbilježbaService). And in this class, you handle all operations of your logic (including database EF queries).
Then you inject this class to your controller and call the required functions from that service class (which deals with the database) in your controller.
The same process can be applied to the other entity Seminar.
P.S. by injecting I mean using any IoC design pattern (in ASP.Net Core, dependency injection is already built-in).
So after these brief words, to answer your question directly, yes, a good software design would be by creating a separate class which handles database operations (adding rows, editing rows, etc.)
It all depends on what your application is supposed to do.
If this is nothing more than a few views around a few tables, then it is perfectly fine to save these objects directly from the controller. The best design is usually the simplest one and there is no need to overcomplicate things with layers, architectural patterns and so on. These are relevant when the size of the project is much larger than in your case.
Good design is all about communication. If someone else is supposed to maintain your project, will it be clear to them where to find the functionality?
I would expect two controllers: one for seminars (called SeminarController) and one for enrollments (called EnrollmentController). These will have methods for viewing, inserting, modifying and deleting data. I would be able to extend your project easily because I know where (and how) to find the code. So your suggestion seems like a good fit.
Response to comment
In the list of seminars has a link pointing to the screen where someone can register for a seminar. That action needs to know which seminar has been selected. The way to do it is to pass the id of the seminar with the request, e.g. /Enrollment/Register/{seminar id}. This results in a GET-request. The form in the enrollment view will POST the inputted data back to the controller.
In the EnrollmentController you would have something like this:
private readonly MyDbContext context;
// Constructor and other methods omitted
[HttpGet]
public ActionResult Register(int seminarId)
{
var seminar = context.Seminars.Single(x => x.Id == seminarId);
return View(seminar);
}
[HttpPost]
public ActionResult Register(Enrollment enrollment)
{
context.Enrollment.Add(enrollment);
return RedirectToAction("index", "Seminar");
}
Depending on the requirements, you might need to insert some validation etc.
You need to study about software architectures a bit to clarify this. Try reading about Layered Architecture for basic structures, and I am assuming you already understand how the MVC architecture works. These will clarify where to perform which task. One of my favorites is the Onion architecture. So basically when you implement an architecture in your code, it becomes much more easy to read, control and track all activities performed within the code.
At the simplest, it is better to split the tasks as below:
1. You define your model classes
2. You create a database class/layer, where you will implement the logic to perform data base queries into your database with respect to the models and return the formatted data (This is where you perform the EF core queries).
3. You create your controllers, where you handle tasks by sending appropriate requests to the database layer and fetch the formatted data.
4. You create your views based on the expected model, and setup the controllers to send the formatted model data to the appropriate view.
A good place to start is here: Tutorial on EF core with MVC
The best way to achieve this in MVC is tu use the nuget package EntityFrameworkCore
Here is a step by step documentation: https://learn.microsoft.com/en-us/ef/core/get-started/
For any further questions, feel free to ask.

Store temp data per user Entity Framework 6

I'm creating asp.net-mvc application where user is uploading multiple files.
The data will be compared with db data, processed and exported later. Also paging.
When displaying these data, sorting and filtering is importing.
When data is uploaded, some of them will be stored in db, some will be displayed as not found in db, some will be modified and stored ... etc
My question is, what is the best way to store the uploaded data in order to be available to be process or viewed?
Load in memory
Create temp tables for every session? (even don't know if possible)
Different storage which can be queryable (access data using linq) (JSON??)
Another option.
The source files are (csv or excel)
One of the files example
Name Age Street City Country Code VIP
---------------------------------------------------------
Mike 42 AntwSt Leuven Belgium T5Df No
Peter 32 Ut123 Utricht Netherland T666 Yes
Example of class
public class User
{
public string Name { get; set; }
public Address Address { get; set; } // street, city,country
public Info Info { get; set; } // Age, and Cres
}
public class Info
{
public int Age { get; set; }
public Cres Cres { get; set; }
}
public class Cres
{
public string Code { get; set; }
public bool VIP { get; set; }
}
There are a variety of strategies for handling this (I actually just wrote an entire dissertation over the subject), and there are many different considerations you'll need to take under consideration to achieve this.
Depending on the amount of data present, and what you're doing with it, it may be simple enough to simply store information in Session Storage. Now how you actually implement the session store is up to you, and there are pros and cons to how you decide to do that.
I would personally recommend a server side session store to handle everything and there are a variety of different options for how to do that. For example: SSDB and Redis.
Then from there, you'll need a way of communicating to clients what has actually happened with their data. If multiple clients need to access the same data set and a single user uploads a change, how will you alert every user of this change? Again, there are a lot of options, you can use a Pub/Sub Framework to alert all listening clients. You could also tap into Microsoft's SignalR framework to attempt to handle this.
There's a lot of different If's, But's, Maybe's, etc to the question, and unfortunately I don't believe there is any one perfect solution to you problem without knowing exactly what you're trying to achieve.
If the data size is small and you just need them to exist temporarily, feel free to go with storing them in memory and thus cut all the overhead your would have with other solutions.
You just need to be sure to consider that the data in memory will be gone if the server or the app is switched off for whatever reason.
It might also be a good idea to consider, what happens if the same user performs the operation for the second time, while the operation on the first data is not completed yet. If this can happen to you (it usually does), make sure to use good synchronization mechanisms to prevent race conditions.

Create Entity from another Entity in MVC

Not sure if the title is clear enough. I have Products and Categories entities in an MVC-EF app. when adding a new product, I select the category from a drop down list. I would like to be able to create a new category if it doesn't exist in the drop down list from within the product/create page. maybe having a button like "Add New Category" next to the drop down list.
For example: if I want to add a book that belongs to the Cooking department (which is not in the list), I want to be able to add the cooking department at the same time I'm adding the book without going to the Department/Create view
Well, there's three primary approaches:
Handle the category creation through a modal window or similar that pulls in an iframe. This is the easiest method as the category creation is completely segrated with its own actions and views. However, you'll still need some JavaScript to update your select list choices to include the newly created item after its saved. That's relatively easy, though. You just issue an AJAX request to some endpoint that returns all the available categories and use that to rebuild the select list after the modal is closed.
Use AJAX for category creation. This is a little more complicated than #1, but still relatively straight-forward. You simply have a form with the fields for a new category embedded somewhere in your page. You can also use AJAX to request the form HTML from an action that returns a PartialViewResult. Either way, you can present this to the user through a modal or whatever you like. Then, when they submit, instead of a traditional form post, you simply issue an AJAX request with the serialized form data to some endpoint responsible for actually saving it. Like #1, you'll need additional JavaScript to update your select list accordingly after saving the category.
Include the new category with the post data of the rest of the product form. This is the most complicated of the three choices as you have to take special care in a few ways. You'll need a separate collection on your view model to hold the posted category or categories, and you'll need to manually add these new categories to your product entity before saving it. You'll also need to synchronize it with categories associated via the select list.
Giving you anything more specific is beyond the scope of StackOverflow. Whichever of the above three methods you choose, a little bit of research will turn up enough articles/tutorials to get you rolling. If you have specific issues in the process of building your solution, you can come back here to ask specific questions about those.
I have had a similar requirement on a project. Make something like this:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
Then you populate the dropdown with all your categories, and you can make more as you go along.
On your product you just make a virtual property of category to access it.
public class Product
{
public int Id { get; set; }
//Rest of your fields
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
Doing this will allow you to lazy load the related entities if you wish.

How to give access to lists of objects which are part of an aggregate root?

If I have an aggregate root which consists of say:
class Parent
{
IEnumerable<Child> Children{ get; set; }
}
Children could contain any number of possible Child objects which are stored in the database.
What would be the best way of getting a total list of all Child objects to the application, so they can be presented in a UI allowing a user to attach/remove them from the Parent object?
Having a method in Parent such as
class Parent
{
IEnumerable<Children> GetAllChildObjects { get; set; }
}
would surely corrupt the model with implementation details?
Would it be ok to have a domain service which calls the Parent repository and retrieves a full list. The facade to the application could then call the service directly, ensuring the Parent model stays "pure".
Update:
To give a bit more detail, i'm tidying up a system and trying to give it some structure.
A User can hold a number of WorkLocations. WorkLocations are pretty simple. The current system contains a webpage which displays user details including a full list of valid WorkLocations. Selecting locations from the list updates the User model with the new locations.
Currently, the UI pretty much hits the DB and pulls out the full list of WorkLocations. I need to pull this back into a more structured form.
Or, does this suggest that WorkLocation should not be in the User root as it currently is?
Am I correct in thinking that you want all the WorkLocations from the database, regardless of what User they are attached to (if any)?
If so I would definitely go for the service approach, something like:
public interface IWorkLocationsService
{
IEnumerable<WorkLocation> GetAllWorkLocations();
}
You might want WorkLocation to be immutable so that all changes to them go through User, though I suspect this isn't necessary here.
Update:
You could then add the following methods to User
// This gets filled from the db somehow.
private IList<WorkLocation> workLocations;
// IEnumerable so that all external additions and
// removals must go through dedicated methods.
public IEnumerable<WorkLocation> WorkLocations
{
get { return workLocations; }
}
public void AddWorkLocation(WorkLocation locationToAdd)
{
workLocations.Add(locationToAdd);
// Do whatever else you need to, i.e. mark the item for saving.
}
public void RemoveWorkLocation(WorkLocation locationToRemove)
{
workLocations.Remove(locationToRemove);
// Do whatever else you need to, i.e. mark the item for saving.
}
If you really must get the list of SonOfFoo for some reason, by using simple high level interfaces such as IEnumerable, you're not corrupting the model with implementation details.
Depending on what you need done, it would be better to avoid getting a list of SonOfFoo though, having Foo manage the work would be better.
Also depending on the amount of details SonOfFoo has, it would be a good idea to encapsulate it on an interface with the methods that the UI/Facade would need to use.
Edit:
From your description, the UI needs a list of the WorkLocations a user can work at (a IEnumarable would be a good choice), and then after the user selects the location and confirms it, the UI notifies the control the switch of the user with the selected location.

Stopping Filter Display in Dynamic Data Entity Web App

I'm currently experimenting with the Dynamic Data Entity Web App Project type in VS2008 SP1 and after reading many tutorials which offer helpful advice for problems I so far have no need of a solution to I have fallen at the first hurdle.
In the DB I have made my entity model from I decided to start small with a table called "Companies" just to see if I could tweak the display into a satisfactory shape for this small table. The Companies table has a column called "contactid" which leads to a record filled with various contact information in a "contacts" table.
The default created Entity Data Model has guessed that One companies could have many contact records. So it tries to be helpful and add a "Contact" filter onto the page that allows you to see all the Companies that share a particular set of contact info indexed by the "Contact Name" field.
Unfortunately the contact table is a multi-purpose one that also stores contact info for customers and there are about 1000 times more customers than there are companies. So the Dropdown makes the page load time increase exponentially and produces no benefit.
So I'd like to just stop the filter from appearing. Only problem is I don't have a clue how to switch it off. Google is so far proving recalcitrant on the matter so I wondered if anyone in here knew how to get rid of a useless filter.
I recently had the same issue. I am new to the Dynamic Data Application template and was quickly impressed by its out of the box functionality. However I noted a few set backs as well, specifically the one you mentioned. Several of my pages took forever to load, and when I disovered that it was caused by the foreign key drop down populates, it took me a while to find a solution.
The problem is this behavoir is essential to the inner workings of the web site. These foreign key dropdown lists actually drive the result set in the datagrid. (The ddlist is populated, then the default value is set, which raises an event to filter the datagrid.) So the key is to alter this behavoir instead of disabling it.
My first clue in solve this issue came from an excellent series of blogs I found on the web. Kudos to Stephen Naughton
The concept is to create a metacolumn attribute and decorate the problematic entities in your metatable partial classes. (I named mine DisableFilter)
So, you'll end up with a table and metatable partial class definition similar to:
[MetadataType(typeof(InvoiceMetadata))]
public partial class Invoice {
}
public partial class InvoiceMetadata {
[ScaffoldColumn(false)]
public string InvoiceId { get; set; }
[ScaffoldColumn(false)]
public string LiquidatorInvoiceId { get; set; }
[ScaffoldColumn(false)]
public string CreatedBy { get; set; }
[ScaffoldColumn(false)]
public System.Nullable<System.DateTime> LastModifiedDate { get; set; }
[ScaffoldColumn(false)]
public string LastModifiedBy { get; set; }
[DisableFilter]
public LeadAccount LeadAccount { get; set; }
}
My newly created attribute, DisableFilter, decorates the LeadAccount (Our Contact table) collection.
Once the column is decorated, I then altered the base behavior of the ForeignKey.ascx control by adding a check for the new attribute. If the filter is disabled, I only populate the ddlist with the default item.
if (!Page.IsPostBack) {
if (!Column.IsRequired) {
DropDownList1.Items.Add(new ListItem("[Not Set]", NullValueString));
}
if (Column.IsFilterDisabled()) {
DropDownList1.Items.Add(new ListItem(DefaultValue, DefaultValue));
}
else {
PopulateListControl(DropDownList1);
}
// Set the initial value if there is one
string initialValue = DefaultValue;
if (!String.IsNullOrEmpty(initialValue)) {
DropDownList1.SelectedValue = initialValue;
}
}
}
Its not quite what I call a full solution, mainly the value of the filter will not contain a user friendly name. I haven't bothered to resolve this yet, but this should get you one step closer to solving your problem.

Categories

Resources