Save data in file used in multiple ViewModels - c#

So I'm not sure if it is correct for me to ask this, but I've been self learning WPF and I can't figure out a method to save the data the user enters in my application.
Let's say a project requires the user to input a IList<int> of values. So I have a class storing that information. This information can be loaded from a json filed if the user has already input it and saved within the application.
public class Vault : BindableBase
{
public Vault(string savedFilePath = null)
{
if (string.IsNullOrEmpty(savedFilePath))
{
Measures = new List<int> { 1, 2, 3, 4 };
}
else
{
Measures = (List<int>)JsonConverter.DeserializeObject<List<int>>(savedFilePath);
}
}
public IList<int> Measures { get; set; }
}
Now, when I create the application view, I want to load all the ViewModels the user will use. In each ViewModel, an element of the Measures List must go.
public MainWindowViewModel()
{
vault = new Vault(savedFilePath);
Collection = new ObservableCollection<object>
{
new FirstViewViewModel(vault.Measures[0]),
new SecondViewViewModel(vault.Measures[1])
};
}
So that when I press Save, the Vault class can be serialized.
public void Save()
{
File.WriteAllText(fileLocation, JsonConvert.SerializeObject(vault));
}
As I want to modify the values in Vault with the user input, I need a direct reference to it, therefore in the ViewModels what I do is
public class FirstViewViewModel : BindableBase
{
private int _measure;
public FirstViewViewModel(int measure)
{
_measure = measure;
}
public int Measure
{
get => _measure;
set => SetProperty(ref _measure, value);
}
}
Nevertheless this seems an awful way to connect the user input with the data i want to save in a file.
This is a simplified case of what I want to achieve. However I am sure there are a better way that would allow me to change the values in Vault when Raising a property on the ViewModel. Ideally one that would make UnitTest easy (I haven't started with that yet).
If anyone could offer me a clue to find a better method to deal with this kind of situation, I would really appreciate it.

This will probably get flagged for being too broad in scope, but in general you should serialize the data to a database. This article is a great place to start:
https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/workflows/new-database
If your data structures are very lite then you might want to use something like SQLite, which stores the database in a local file and doesn't require installing any 3rd-party applications along with your application. Plenty of info here on how to get that working with Entity Framework:
Entity Framework 6 with SQLite 3 Code First - Won't create tables

Related

How do you store dynamic site settings in the database?

How do you store dynamic site settings/design in the database?
Previously, I have stored it in a table looking something like this:
public class SiteSettings
{
public int Id { get; set; }
public string FooterText { get; set; }
public string CopyrightInformation { get; set; }
public string TextColor { get; set; }
}
When writing data I checked if a record exists. If not, create one. If it exists, update it. In other words, never have more than one record in that table.
When reading the data, I did this:
SiteSettings settings = await db.SiteSettings.FirstOrDefaultAsync();
Is there another, better way of doing it?
You can always store it in JSON format in one table, one row, one column. But it will be close to field name changes a little bit, so new settings you will add later, they will have to be nullable types in your class definition. And if you wanna play around with names and class structure later, you might have to manually fix the changes on DB too.. The advantage of this would be, you could design your settings class in nested objects and wouldn't have to deal with making different tables for each nested objects:
{
SiteTitle : "My Web Site",
Theme: "Dark",
DbSettings : {
ConnectionString : "",
ConnnTimeout : 30
},
HomePage : {
WelcomeMessage : "Hello!"
MainMenu : {
Position : "left",
ShowIcons : true
}
}
}
If this doesn't fit you and you still want to store fields separately on your DB table, then I would suggest storing your settings in Key, Value structure in multiple rows and find the setting you want with its key from the list. Otherwise you will have to deal with adding new properties to your class every time you need one, add the column to database, re-build, re-deploy, etc.. Besides, if this is a long-term project, your table column number will increase in time with new requirements and it will be pretty ugly after a time. But if you store Key/Value it's a bit more work at the start, but less work in the future..

How to create collection without database in .net core

How can I add items to my list SearchedVideos?
I would like to have these items on the list until the end of my application.
Now I have error like this:
NullReferenceException: Object reference not set to an instance of an object.
I create context with prop as Singleton like this:
public List<QueryViewModel> SearchedVideos { get; set; }
In startup
services.AddSingleton<YtContext>();
My model
public class ExecutedQuery
{
public Query Query { get; }
public string Title { get; set; }
public IReadOnlyList<Video> Videos { get; set; }
public ExecutedQuery(Query query, string title, IReadOnlyList<Video> videos)
{
Query = query;
Title = title;
Videos = videos;
}
}
My service
public async Task<ExecutedQuery> ExecuteQueryAsync(Query query)
{
// Search
if (query.Type == QueryType.Search)
{
var videos = await _youtubeClient.SearchVideosAsync(query.Value);
var title = $"Search: {query.Value}";
var executedQueries = new ExecutedQuery(query, title, videos);
var qw = new QueryViewModel
{
ExecutedQueries = executedQueries,
};
_ytcontext.SearchedVideos.Add(qw);
return executedQueries;
}
}
My QueryViewModel
public ExecutedQuery ExecutedQueries { get; set; }
My Controller
[HttpGet("Search/all")]
public async Task<IActionResult> ListAllQueriesAsync(string query)
{
var req = _queryService.ParseQuery(query);
var res = await _queryService.ExecuteQueryAsync(req);
return View(res);
}
If you are wanting to edit this list from one instance to another then you'll need to use some kind of datasource. If a database is not an option then a text file will have to do. Use a Json string and serialize/deserialize to your object. https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-serialize-and-deserialize-json-data. I've used this method to mockup an application but if you are going to be doing alot of writing to the file you may run into issues.
If you can hard code the list in the application then a Singleton will work. Read up here. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2
Each request is its own thing, unaffected by anything that's happened before or since. As such, you pretty much start from a blank slate. The typical means for persisting state between one or more additional requests is the session. Sessions are essentially fake state, through a combination of server-side (some persistent store) and client-side components (cookies), something that appears like persistence of state can be achieved. However, particularly on the server-side, you still need some sort of store, which is generally a database of some sort, be it relational (SQL Server, etc.) or NoSQL (Redis, etc.). The default session store will be in-memory, which may suffice for your needs, but as memory is volatile, any sort of app restart will take anything stored there along with it.
Alternatively, there's statics and objects with singleton lifetimes. In either case, they're virtually the same as in-memory storage - they'll persist the life of the application and no more.
Statics are just members with a static keyword on them. It's probably the simplest and most straight-forward approach, but also the most fragile. It's virtually impossible to test statics, so you're basically creating black-holes in your code where anything could happen.
A better approach is to simply use an object with a singleton lifetime. These can be create via the AddSingleton<T> method on the service collection. For example, you could create a class like:
public class MySingleton
{
public ICollection<IReadOnlyList<Video>> SearchedVideo { get; set; }
}
And then register it as a singleton in ConfigureServices:
services.AddSingleton<MySingleton>();
Then, in your controllers, views, and such, you can inject MySingleton to access the SearchedVideos property. As a singleton, the data there will persist for the life of the application.
The chief difference between sessions, particularly in-memory sessions, and either statics or singletons is one of breadth. Sessions will always be tied to a particular client, whereas statics and singletons will be scoped to the application. That means that if you use statics or singletons, all clients will see the same data and will potentially manipulate the same data. If you need something that is client-specific, you must use sessions, instead.
#natsukiss i guess you are trying to call Add() method from null property. Even you create a list you should set an initial instance for SearchedVideo Property. Because if you dont create an instance, it means that property will not have address in memory. Because of that sometimes we are using string TestVal = "". That means we sets initial value on Common Language Runtime(CLR) to locate Address in Memory.
public List<QueryViewModel> SearchedVideos { get; set; } = new List<QueryViewModel>(); //<==
or if you are working with EntityFramework you should use
public ICollection<QueryViewModel> SearchedVideos { get; set; } = new HashSet<QueryViewModel>(); //<===

Which class should be responsible for certain actions?

I am making a Storage System Application. Each storage system contains up to 10 Warehouses capable of storing Stock Items.
I want to create a method for updating a Warehouse properties (Name, Description etc). There is a business rule that each Warehouse within a storage system must have a unique name, and im unsure which class should be responsible for this. Im trying to stick to the principle that each class should only be responsible for itself.
Here is a simple mock up of the code (C#)
public class StorageSystem
{
public List<Warehouse> Warehouses{Get;}
}
public class Warehouse
{
public string Name{Get; private Set;}
public int StorageSystemId{Get; Set;}
public StorageSystem Sotrage System{Get; Set;}
}
I think i should do the following,
Step1. Create a method in StorageSystem class.
public bool WarehouseNameAvailable(string name)
{
//Check List of Warehouses for the name
//if found return false
//else return true
}
Step2. Create a method in the Warehouse
public void UpdateWarehouseName(string name)
{
if(StorageSystem.WarehouseNameAvailable(name))
{
this.name = name;
}
else
{
//Throw Exception
{
}
Is this the "proper and correct" way to do this?
Is this the "proper and correct" way to do this?
This highly depends on your overall application architecture, imho. Ask 5 developers whether your solution is "proper and correct" and you will get 6 opinions.
To get to the point: When purely relating on DDD concepts, your solution seems at least possible. But to be honest, i've never come across an enterprise application of notable size that was able to project its business logic entirely in the domain model in a structured (!) and maintainable (!) way and did not need some kind of service layer upon it. I personally like this approach and would recommend pojecting this requirements in separate classes, that represent business/use cases:
public class RenameAction {
//Some Kind of DbSet, Database Connection, external service,...
//I'll go with an EF - DbSet<Warehouse> in this example
private readonly DbSet<Warehouse>_warehouses;
private readonly DbSet<StorageSystem> _storageSystem;
public void Execute(int storageSystemId, int warehouseId, string name) {
var storageSystem = _storageSystems.Single(system => system.Id == storageSystemId);
if (_storageSystem.Warehouses.Any(wh => wh.Name == name))
throw new BusinessLogicException("Warehouse names must be unique within storage systems!");
var warehouse = storageSystem.Warehouses.Single(wh => wh.Id == warehouseId);
warehouse.Name = name;
//Write back the updated warehouse to whereever, this won't work with an DbSet<Warehouse>.
_warehouses.Update(warehouse);
}
}
But again, this is just how I would deal with that.
Make T_WAREHOUSE.NAME unique (just assumed that your table/columns would have names like this) - database wise and handle the exception if a second entry gets written with the same name.
You could rise that error/exception up to user level too, to show users what went wrong: enter a different name please, "Super Dooper Warehouse" is already taken
Your questions text suggests that this will be a bigger project with many classes that interact with each other.
If you are not using a ORM, think about using one! It does a lot what you are trying to achive via custom methods loke your WarehouseNameAvailable workaround.
If you are using Entity Framework you can do this on your class properties via [Unique] attribute.

How to store data or persist it across forms

I have created a program with Windows Forms in C# and the architecture is something like this
* BaseClass.cs
* EntityClass.cs
* ControllerClass.cs
* DataAccessClass.cs
* Viewer.cs
So basically the Entity class inherits from the base class, and the data access class goes and retrieves data from a MySql database.
What I want to do is to be able to use the data pulled without having to call the data access class if I already pulled it once.. where can I put this data so I can access it anywhere?..
I was reading about serializing into memory.. but I want to get a good advise here on where to put it and if possible how. I would really appreciate any pointers.
Regards
Seems like you want to continue with the MVC pattern, and introduce a "Model" component that is shared between your 2 views. The Model would be initialized/refreshed using your DataAccessClass. Each form (or View) should have a reference to the Model. I would not suggest using serialization as you are just sharing an in-memory C# object in the same process between 2 different Forms.
For example:
public class MyFirstView : Form
{
private ModelClass m_model;
public MyFirstView(ModelClass model)
{
m_model = model;
m_model.OnDataRefresh += this.Model_OnDataRefresh;
}
}
public class MySecondView : Form
{
private ModelClass m_model;
public MySecondView(ModelClass model)
{
m_model = model;
m_model.OnDataRefresh += this.Model_OnDataRefresh;
}
}
public class ModelClass
{
private DataAccessClass m_dataAccess;
public event EventHandler OnDataRefresh = {}; // fired when data is refreshed
public void EnsureDataIsLoaded(); // queries the db if we haven't already
public void RefreshData(); // refreshes the data from the db
public IList<Entity> GetDataList(); // access to data items
}
For each Form/View, you can use the Form.Load event to refresh the view with the model data. Perhaps you could have a method on the ModelClass.EnsureDataIsLoaded() that will use the DataAccessClass to query the database if the you haven't already.
And lastly, if the model changes, you need some way to push the changes to the view(s). One way to do this is have the model fire an event when the data is refreshed, and each view subscribes to that event.
I would suggest using a static class to hold the data. If you will have multiple forms opened simultaneously, you could have a Dictionary within the static class keyed by some form identifier.
You shouldn't need to serialize anything, you can just hold on the to the references in some sort of cache layer.
Assuming that your data access classes take some sort of query as a parameter, you can store the results in a dictionary keyed by the query. Then you check the cache first for query results, if not there go to the database, retrieve it, and cache it. If you are using lambda expressions for your queries, you can call the .GetHashCode() method to get a unique hash for that lambda.
EX:
IDictionary<string, IEnumerable<T>> Cache;
IEnumerable<T> GetData<T>(string query)
{
var key = typeof(T).Name + query;
if (!this.Cache.ContainsKey(key))
{
// get from database
var data = SomeRepository.GetData(query);
this.Cache[key] = value;
}
return this.Cache[key]
}
I suggest you to take a look at the System.Runtime.Caching.MemoryCache class. It might serve your need.

What is a best practice way to define select/dropdown options for view creation or db store

I'm still not yet sure on the best way to store selectlist options for front end display or db storage.
I've been using Enums at the moment, and also using description decorators (How do you create a dropdownlist from an enum in ASP.NET MVC?)
I'm now thinking that I might as well just create a full class for this stuff, so I can store the following information properly with full control:
Item Name
Full description
int for storage in db
order
Any methods to get information in anyway from the list.
Is it right I should be thinking about implementing all this myself by hand? I want a really solid way of doing this, and an enum doesn't really feel like it's going to cut it.
Is it right I should be thinking about implementing all this myself by
hand?
Yes. Enums are often leaky and insufficient abstractions that aren't always suitable for the complex domain model you actually wish to represent.
Rather than roll your own, you may want to consider Headspring's Enumeration class (via github, nuget). We use it all the time instead of enums because it's nearly as simple and is much more flexible.
An example of a "State" enumeration and using it as a select list:
public class State : Enumeration<State>
{
public static State Alabama = new State(1, "AL", "Alabama");
public static State Alaska = new State(2, "AK", "Alaska");
// .. many more
public static State Wyoming = new State(3, "WY", "Wyoming");
public State(int value, string displayName, string description) : base(value, displayName)
{
Description = description;
}
public string Description { get; private set; }
}
public IEnumerable<SelectListItem> Creating_a_select_list(State selected)
{
return State.GetAll().Select(
x => new SelectListItem
{
Selected = x == selected,
Text = x.Description,
Value = x.Value.ToString()
});
}
I'm not trying to sell you on this particular implementation, you could certainly hand code your own (the Enumeration class is only about 100 lines of code). But I definitely think you'd benefit from moving beyond basic enums. It is the right approach given the scenario you described in your question.
The first place where such information shoiuld be is the database...or any "virtual store" such as a web service that offers an interface to you db. In fact if there are other db entiies that use these values THEY MUST be represented in the database, otherwise you will run in big troubles. In fact, suppose one of such values is a string....if you don't define a table containing all possible values+a key and simply write the string as it is in other tables...it will be impossible for you to change the format of the string since it will be "spread" all over your db...On the contrary, if you just use an external key to refer to such strings...you can easily change them since the string is stored in just ONE place in your db.
Also the enumeration solution suffers of the problem that you cannot add or deleted values...so if such operations "conceptually" might make sense you cannot use an enumeration. You can use enumeration when all options "conceptually span" all possibilities, so you are sure you will never add/delete other options, such as in the case of the enumeration (yes, no, unknown).
That said, once you have your options in the db the remainder is easy...you will have DTO entities or Business entities representing them in exactly the same way you do for all other DB entities.
For visualization purposes you may have a ViewModel version of this options that might just contain key and description, and a "Repository method" that your controllers can call to have the list of all options.
Once retrieved you controllers put them in the overall page ViewViewModel...together with all other information to be shown on the page. From the ViewModel...you can access them to put them in a dropdown.
Summing up:
1) You need a DB representation of your options
2) Then you will have DTO, business layer, and View versions of this entities...as needed, exactly as for all other DB entities.
Are you looking for a one-size-fits-all solution for all your select list options? I personally advocate choosing the option that best fits the specific issue.
In a recent project I was introduced to a hybrid of a Smart Enum. Here's an example (I apologize for typos, I'm typing this cold):
public class Priority
{
public enum Types
{
High,
Medium,
Low
}
public Types Type { get; private set; }
public string Name { get { return this.Type.ToString(); } } // ToString() with no arguments is not deprecated
public string Description { get; private set; }
public static High = new Priority{ Type = Types.High, Description = "..."};
public static Medium = new Priority{ Type = Types.Medium, Description = "..."};
public static Low = new Priority{ Type = Types.Low, Description = "..."};
public static IEnumerable<Priority> All = new[]{High, Medium, Low};
public static Priority For(Types priorityType)
{
return All.Single(x => x.Type == priorityType);
}
}
So, in implementation, you could store the Enum value, but you would reference the object itself (Priority.For(entity.priority)) for the additional metadata when rendering your views.
Is that closer to what you're looking for?
Of course, one of the gotchas is if you need to write a query against the database that relies on the metadata on the lookup, this solution is going to create a few tears along the way.
You can use "repository pattern" for data access and use viewmodels between your controllers and views. Example:
//Model
public class CustomerViewModel
{
public Customer customer { get;set; }
public IEnumerable<Village> Villages { get; set; }
}
//Controller
public ActionResult Index()
{
var customerViewModel = new CustomerViewModel
{
Customer = new Customer(),
Villages = _villageService.GetAll()
};
return View(customerViewModel);
}
//View
#model ViewModel.RegisterViewModel
#Html.DropDownListFor(q => q.Customer.VillageId, new SelectList(Model.Villages, "Id", "Title"), "Please Select")
I have written a blog post about repository pattern, you may have a look.
I store my options in the View Models themselves:
public class ViewModel {
[Required]
public int SelectListValue { get; set; }
public IDictionary<String,String> SelectListOptions {
get {
return new Dictionary<String, String>{
{ "0", Resources.Option1},
{ "1", Resources.Option2},
{ "2", Resources.Option3}
};
}
}
}
Then I can just drop the following line into my view to render the select list:
<%= Html.DropDownListFor(m => m.SelectListValue, new SelectList(this.Model.SelectListOptions, "Key", "Value", "")) %>

Categories