How do you store dynamic site settings in the database? - c#

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..

Related

Save data in file used in multiple ViewModels

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

Most efficient way to convert a object to another (Model to ViewModel)

Suppose I have a model with 20 fields, and in my index page, I want to list all models that are stored in my database.
In index page, instead of listing all fields of the model, I only to list 3 fields.
So, I make two class:
class CompleteModel {
public int Id { get; set; }
public string Field01 { get; set; }
public string Field02 { get; set; }
public string Field03 { get; set; }
public string Field04 { get; set; }
public string Field05 { get; set; }
...
public string Field20 { get; set; }
}
now, in my Controller, I can use:
await _context.CompleteModel.ToListAsync();
but I feel that it does not seem to be the right way to do it, because I'm getting all fields and using only 3 fields.
So, I made this code:
class ViewModel {
public string Field02 { get; set; }
public string Field04 { get; set; }
public string Field08 { get; set; }
}
var result = _context.CompleteModel.Select(
x => new {
x.Field02,
x.Field04,
x.Field08
}).ToListAsync();
var listResults = new List<IndexViewModel>();
if (result != null)
{
listResults.AddRange(results.Select(x => new IndexViewModel
{
Field02 = x.Field02,
Field04 = x.Field04,
Field08 = x.Field08
}));
}
I think this is a lot of code to do this.
First, I selected all the fields that I want, then, copied everything to another object.
There's a "more directly" way to do the same thing?
Like:
_context.CompleteModel.Select(x => new IndexViewModel { Field02, Field04, Field08 });
You could use AutoMapper to reduce the boiler plate so you're not manually copying field values over.
If you include the AutoMapper NuGet package then you'd need to have the following in your startup somewhere to configure it for your classes:
Mapper.Initialize(cfg => cfg.CreateMap<CompleteModel, ViewModel>());
You could then do something like the following:
var results = await _context.CompleteModel.ToListAsync();
var viewModelResults = results.Select(Mapper.Map<ViewModel>).ToList();
There are a lot of configuration options for the package so do take a look at the documentation to see if it suits your needs and determine the best way to use it if it does.
In my view this is one of the weaknesses of over abstraction and layering. The VM contains the data that is valuable to your application within the context of use (screen, process etc). The data model contains all the data that could be stored that might be relevant. At some point you need to match the two.
Use EF Projection to fetch only the data you need from the database into projected data model classes (using the EF POCO layer to define the query, but not to store the resultant data).
Map the projected classes onto your VM, if there is a naieve mapping, using Automapper or similar. However unless you are just writing CRUD screens a simple field by field mapping is of little value; the data you fetch from your data store via EF is in its raw, probably relational form. The data required by your VM is probably not going to fit that form very neatly (again, unless you are doing a simple CRUD form), so you are going to need to add some value by coding the relationship between the data store and the View Model.
I think concentrating on the count of lines of code would lead to the wrong approach. I think you can look at that code and ask "is it adding any value". If you can delegate the task to Automapper, then great; but your VM isn't really pulling its weight other than adding some validation annotation if you can consistently delegate the task of data model to VM data copying.

How to make grid operations work with custom property in EF?

In a ASP.NET MVC 5 web site I have a GridView using the devexpress component binding using the LINQ method.
EF generated a partial class to map a table that i use to display in that gridview.
In this partial class generated by the EF i have a ID_Status property wich has a corresponding description in other table. I made another partial class to deal with this custom Property and it works ok, except when i try to make a 'Sort' operation clicking on the header of this column.
The partial class generated by the EF.
[Table("Test")]
public partial class Test
{
[Key]
public long ID_Test { get; set; }
public long ID_TestStatus { get; set; }
//other properties
}
My Custom partial class:
public partial class Test
{
private static readonly TestRepository _testRepository;
static TestRepository()
{
_testRepository= new TestRepository();
}
public string StatusDescription
{
get { return _testRepository.GetStatusDescriptionById(ID_TestStatus); }
}
}
When i try to Sort using another column it works fine, but when i try to Sort using the custom property Column all the grid cell values gets empty, without any value.
Any suggestion?
It's not a very good idea to have data access code inside an entity. One reason is that it makes it very hard to write unit test. Another reason is that it is very likely to give rise to the n + 1 anti pattern. In your case, it does: one (1) query to get the Tests, then each Test (n) sends a separate query to the database to get its StatusDescription.
The way you implemented it also raises some eyebrows, because
_testRepository is static, which meas there is probable some context instance living for the entire lifecycle of the application - unless GetStatusDescriptionById creates a new context for each call, but that wouldn't be a good idea either.
The GetStatusDescriptionById call is made each time the property is accessed. In a web application this may not be a big problem because the objects are newly created each time they are requested anyway, but in other environments this could be highly inefficient.
A better approach would be to fetch the Testss with their Status included:
context.Tests.Include(t => t.TestStatus)
and have an unmapped property like
public string StatusDescription
{
get { return TestStatus== null ? string.Empty : TestStatus.Description; }
}
better still (in my opinion) would be not to show Test objects directly, but TestDto objects like
public class TestDto
{
public string StatusDescription { get; set; }
//other properties that match Test's properties
}
and use a tool like AutoMapper to map a collection of Tests to TestDtos. If Test has a property Status and TestStatus has a property Description, AutoMapper will be able to flatten that into StatusDescription automatically.
Both this StatusDescription property and the Dto appraoch set the state of a Test(Dto) object once. I don't think any grid component can mess with that.

How can one efficiently modify a collection within a ViewModel?

I am currently typing this from my phone, so I don't have any code at hand. But I'll try to explain the situation.
Taking the example of a Recipe and Variations to this Recipe. The Recipe in this case holds a collection of Variations.
I have a Create view that takes the Recipe as the model.
I want this view to contain a list of Variations to the Recipe already. So there is an 'Add variation' button that opens a dialog with a partial view to add this variation.
Given that this is a yet non existing Recipe, I can't save the variation to the database yet, because it requires the RecipeId. So I think I am looking for a temporary place to store the new variations until I am saving the Recipe, and then also save the variations with it.
As MVC is stateless, I can't really save the variations anywhere temporarily, except for possibly the viewdata or tempdata, or I could add a bunch of hidden fields to the page via JavaScript, so that the Variation entries will be taken into the post triggered by saving the Recipe. All of which don't sound particularly tidy solutions.
What would be the most clean, ideal solution here?
A couple of possible solutions:
Reconsider to use a wizard flow for creation instead - create recipe screen (no variations), then a chain of create variation screens. After that when you edit you can do it in the same page since the recipe is saved already.
As dreza mentionned, the variation partial view could return html to the creation page that adds the fields needed in the form for a full submit of the recipe + variations.
Pre-generate the RecipeId on the create form and reference to it for every actions, so you could save Variation (Id, RecipeId, ...) in the database before saving Recipe if you have different table/document. Then you lose the autogenerate id from SQL/Entity tho, and it's harder to maintain.
If you'd use a document database, you could have had the Recipe document have a list of VariationId and create the Variation on it's own without any direct reference to Recipe directly (No RecipeId in Variation). In SQL/Entity tho you would need 3 tables - Recipe (RecipeId), Variation (VariationId), RecipeVariation (RecipeId,VariationId) - it's uglier.
However, with complex, error prone UI like a parent + childs and such, another approach, and one I used extensively, is to create InputContexts associated to users and have InputModels are well as your normal models. These are semi-temporary models that exists until the user finishes to do his entry, they are usually used as ViewModels directly. For example, you could have:
public class InputContext
{
public int Id { get; set; }
public int UserId { get; set; }
public string RouteName { get; set; }
public DateTime TimeStamp { get; set; }
//...
}
public class RecipeInput
{
public int Id { get; set; }
public int ContextId { get; set; }
// ...
}
public class VariationInput
{
public int Id { get; set; }
public int ContextId { get; set; }
// ...
}
Your partial view that creates variation could simply save them to VariationInput with the current ContextId and that's it.
Then in the controller:
[HttpGet]
public ActionResult Create()
{
// We should check if one exists and reuse it instead, but omitted for clarity
var context = new InputContext(GetUserId(), GetRouteName());
db.Contexts.Add(context);
db.Save(context);
var model = new RecipeInput(context.Id);
return View(model);
}
[HttpPost]
public ActionResult Create(RecipeInput model)
{
// Save the current input to DB
db.RecipeInputs.Update(model);
db.Save();
// Do validation and return Create view on error...
// load the context and related variations created
var context = db.Contexts.Find(model.ContextId);
var children = db.VariationInputs.Where(x => x.ContextId == context.Id).ToList();
// Create the actual models from the input.
var recipe = new Recipe();
// set values from model
foreach (var child in children )
{
var variation = new Variation();
// set values from child
recipe.Variations.Add(variation);
}
db.Recipes.Add(recipe);
db.Save();
// Cleanup if it worked
db.RecipeInputs.Delete(model.Id);
foreach (var child in children )
{
db.VariationInputs.Delete(child.Id);
}
// you could keep the Contexts as logs or delete them
//...
}
This is quite more complicated, but it can be used to save user data even between browser restart, session timeouts (they leave for lunch and come back and have to input everything again...), etc, and do some other neat tricks.
I usually store them in another database/partition/schema, and when I use SQL as a backend (because that's what the client wants), I often try use a Document Database (RavenDB or MongoDB for example) to store the context and input models as it is much nicer to play with in terms of MVC. This is not client data so it doesn't matter much.
If you need to see the list of variations in the Create Recipe page, you simply need to do have a partial view/ajax that gets a list of VariationInput in the current ContextId and display these.

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