I read data from a SQL database into a list of objects. The data types of the properties of the entities are all strings. I want to validate these properties and then present the result to the viewer of a website. So for example i have a class like this:
public class Customer
{
public string CustomerNumber { get; set; }
}
The CustomerNumber is read from the database as string and i want to validate that it is numeric. There are multiple properties i want to perform custom validations for. How do i approach this the best way? Do i validate it in the backend and send an error list to the front end or do i validate this in the frontend before presenting the data to the user? In the frontend i want the specific property that is not valid to be displayed with a red border for example and an error message for this specific property of the list of objects.
A good place to start would be to use the following. https://docs.fluentvalidation.net/en/latest/start.html
The validator will build up a list of validation errors based on what you define is valid. After you have that list of errors, iterate through that list to build up custom ui controls for the end user
Using C# and MongoDb im saving a class similar to the following.
public class Zone
{
public string ZoneName { get; set; }
public List<string> IncludedCountries { get; set; } = new List<string>();
}
This is filled by user and saved in my DB, currently I am checking that the zone name isn't duplicated when inserting. Like so.
if (All().Any(x => x.Name.ToLower() == zone.Name.ToLower())) { throw new System.Exception($"Zone \"{zone.ZoneName}\" is already in database, please edit the zone"); };
But if user currently tries to add the exact same values (So exact same list of included countries) with different name, I wouldn't catch it.
I want to be able to, as dont want to be duplicating same classes in DB (My actual class will have more properties, this is an example). I am aware I can check it the same way im checking for name, but having in mind I have a lot of properties, i'd like to know what the best way is..
Ideally you wouldn't perform a search, then use that to decide whether to add or not. In a collaborative system with potentially multiple users you could find another user in another transaction runs the same code at the same time, and ends up adding the record just after your check, but just before your insert.
It's better, assuming your datastore supports it, to use a uniqueness constraint on some value of the data you're inserting. Here's the docs for Mongo: https://docs.mongodb.com/manual/core/index-unique/
This means the transaction will be failed by the database if you attempt to insert a duplicate. To be fair, there's nothing wrong with doing the "ask-then-tell" as well I suppose, in order to avoid ugly exceptions being shown to users, but if you're able to interrogate the exception details you can probably catch it and show the user some helpful information rather than letting them see an error page.
To support your requirement for "has the same list of things" in this way, I'd suggest creating a SHA256 hash value (here's a link: https://stackoverflow.com/a/6839784/26414) for the list, and storing that as a property in it's own right. Just make sure it's recalculated if the list changes.
One additional thing - technically "class" defines the schema, or shape of a bit of data. When you create an instance of a class at runtime, which has actual values and takes up memory, that's technically an "object". So an "object" is an "instance" of a "class".
I have an application that gets and stores data to a database using Linq to SQL. I have a field for a phone number. I am trying to store this consistently as only digits and nothing else. To do this, I have two extension methods I've added to strings. ToPhoneFormat() formats the phone digits as the user wants such as "(xxx) xxx-xxxx" or "xxx.xxx.xxxx". ToPhoneStorage() removes all formatting and only saves the digits from the number for use with the database.
Here's a sample of code used with the Linq to SQL:
private string homePhone;
[Column(CanBeNull = true, DbType = "nvarchar(25)", Name = "HOME_PHONE_NUM", Storage = "homePhone")]
[Display(Name = "Home Phone")]
public string HomePhone {
get { return homePhone.ToPhoneFormat(); }
set { homePhone = value.ToPhoneStorage(); }
}
When I submit changes using the DataContext, Linq does in fact call the getter here, and the formatted phone gets sent back to the database. Am I not understanding the Storage attribute properly? Seems like it should work correctly like this.
Of course I can create two fields "PhoneDigits" and "PhoneFormatted" and only provide the column atribute to the "Phone" field. So I know there's a work around. But what does the Storage attribute do if it doesn't do this?
The ColumnAttribute's Storage property tells Linq-to_Sql to use the private field homePhone to get and set values of the property. So the actual getter and setter are bypassed.
You could remove the Storage property assignment. The code doesn't look like a part of a generated dbml, so this won't get overwritten. On the other hand I don't think linq-to-sql will be able to track changes to the property without the regular plumbing code in the setter (SendPropertyChanged etc.). And if you add this code manually it will also be triggered when the object gets materialized from the database, so it will always start its life in a modified state. (That's what's prevented by setting the private member). A rock and a hard place...
I am trying to create something to hold global site-wide settings in our ASP.NET website - things such as site name, google analytics account number, facebook url etc... The site can have multiple ‘brands’ or sub-sites associated with it, hence the sitguid column, we would also like to have the option to put them into groups, hence the group column – e.g. TEST and PRODUCTION (set via web.config appsetting).
I do not want any of the KEY’s to be hardcoded anywhere, but I would like to be able to reference them as simply as possible in code, e.g. SiteSetting.getSetting(“SiteName”) (these may be used in templates (masterpages and such) that more junior devs will create)
I would also like to be able to administer the existing settings in our admin console and to be able to create new settings.
The datatype column is for the edit form so that the correct input element can be used, e.g. checkbox for bit types, text box for varchar etc...
SiteSettings database table currently:
[sts_sitGuid] [uniqueidentifier] NOT NULL, -- tells us which site the setting is for
[sts_group] [nvarchar](50) NOT NULL, -- used to group settings e.g. test/live
[sts_name] [nvarchar](max) NULL, -- the display name of the setting, for edit forms
[sts_alias] [nvarchar](50) NOT NULL, -- the name for the setting
[sts_value] [nvarchar](max) NOT NULL, -- the settings value
[sts_dataType] [nvarchar](50) NULL, -- indicates the control to render on edit form
[sts_ord] [tinyint] NULL, -- The order they will appear in on the admin form
I am part way through having this working at the moment, but I am not happy with the way I have done it and would like any advice people here have that might help find the ‘right’ solution! I'm sure people have done this before me. (I would share what I have so far, but do not want to skew the answers in any particular way) All i'm looking for is an overview of how this might be best done, not looking for anyone to write it for me ;)
I’ve done quite a bit of searching both here and Google and have not really found what I’m looking for, especially the ability to add new setting ‘definitions’ as well as editing the settings that exist.
The system runs on ASP.NET using webforms, it's all written in c# and uses MSSQL 2008.
As always, any help is very much appreciated!
EDIT: To clarify I am going to explain what I have built so far. I am dead set on storing all of this in SQL as we don't want web.config or other xml files or another database floating around since it'll give us more to do when we rollout the app to other customers.
So far I have a SiteSettings class, this has a method GetSetting which i can call with GetSetting("SettingAlias") to get the value for "SettingAlias" in the DB. This class's constructor fetches all the settings for the current site from the database and stores those in a dictionary, GetSetting reads from that dictionary. All of that part I am happy with so far.
The part I am struggling with is generating the edit form. The previous version of this used a webservice to get/set the settings and I am trying to continue using something similar to save work, but they were all defined in the code, such as GoogleAnalyticsID, Sitename etc... and each had a column in the database, the change I am making is to store these settings as ROWS instead (since then it's easier to add more, no need to change the schema & all of the sitesettings class) Currently my SiteSettings class has a SiteSettingsEditForm method which grabs all the info from the db, creates a bunch of controls for the form elements, puts that in a temporary page and executes that, then passes the HTML generated to our management system via ajax. This feels wrong and is a bit clunky, and is the reason for posting it here, I am having trouble figuring out how to save this stuff back via the webservice, but more importantly generating a bunch of HTML by executing a page containing a load of form controls just feels like the wrong way to do it.
So in summary I (think i) want to write a class to be able to cache & read a handful of rows from a database table, and also give me an edit form (or give data to something else to generate the form) that is dynamic based on the contents of the same database table (e.g. where my type column is 'bit' I want a checkbox, where it is 'text' I want a text input)
Sometimes this kind of problem is easier to visualize if you start off with the data model. If you want a setting per row, then two tables would probably be the best way to store this:
Site:
SiteId SiteKey SiteName
1 XYGZ4345 Client Site 1
2 AZT43752 Client Site 2
This would define the list of sites you have config for. I'd use a SiteKey as you'd put this in your web.config and it's better to abstract this away into a random string or GUID (to make it harder to accidentally load someone else's config), the client can change their name and you don't get confused in the future as you didn't use their old name as a key etc etc.
The config table itself is also simple, if we treat every setting as a string:
SiteSetting:
SettingId SiteId SettingName SettingValue
1 1 Brand KrustyBrand
2 1 GoogleId MSFTSUX0R
3 2 Brand ConfigMasters(TM)
You can then load all the config quite simply:
SELECT * FROM SiteSetting INNER JOIN Site ON (SiteSetting.SiteId = Site.SiteId) WHERE Site.SiteKey = 'XYGZ4345'
Now we have a list of key value pairs you could then store in a class like:
public class SiteSetting
{
public Site Site {
get; set; //Site would just be a simple class consisiting of Id, Key and Name to match the database table
}
protected Dictionary<String, String> Settings { get; set; } //Simple key value pairs
}
So this is a very simple solution. However, we can take it further - things to consider:
1) Can we add an environment to this somewhere?
We could either add a site per environment
OR
Add an environment to the SiteSetting table. The advantage of this is that you could define enironment = 'ALL' to avoid duplication.
OR
The database the configuration is loaded from defines the environment; so you change the config connection string in the app config. Of course, to connect to a different environment you have to change app.config, but you would potentially have to do that anyway to change the client key and/or environment.
2) Add the concept of user defineable settings - some settings you are going to want to change, some you are going to want to lock. A bit column containing "UserDefinable" would allow you to sort this out
3) Typing of settings.
This is slightly more difficult. You might have something like:
PropertyId PropertyName PropertyType Format UserDefined
1 Brand String NULL 1
2 DatePresentation DateTime "yyyy-MM-dd" 1
The Settings table then only defines a value, and a PropertyId. The advantage of this is that you can then start to increase the information about each setting you are storing, and reuse this information as the design is more normalized. The Settings class then changes like so:
public List<PropertyValue> { get; set; } //replacing the dictionary
PropertyValue then looks something like:
public class PropertyValue
{
int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public string PVType { get; set; } //Could be an enum
public string DisplayFormat { get; set;
private string _RawValue;
public string Value{
get{
switch(PVType){
case "DateTime":
return Convert.ToDateTime(_RawValue).ToString(DisplayFormat);
break;
case "Double":
return Convert.ToDouble(_RawValue).ToString(DisplayFormat);
break;
default:
return _RawValue;
}
}
set{
_RawValue = value;
}
}
}
Things like the Value method need to be improved to support robust error handling (you could also investigate using Convert.ChangeType to simplify the switch block)
This topic is as simple or as complicated as you choose to make it ;-)
Editing
As regards maintaining them; a very simple GUI would allow the user to see all of their properties in a tabular format. You might consider having rows where UserDefinable = 0 as readonly, otherwise the user can edit and add rows. You'd need to validate, especially for duplicate Setting Names for example.
The easiest way to do this is to use the DataGrid; a simple mockup might look something like this:
And a more sophisticated approach might look something like this
Generating the form is therefore as simple as databinding a collection of PropertyValue objects to your chosen grid solution.
As you probably found, there are a variety of ways that you can do this, ranging from Microsoft-recommended to 100% roll-your-own. You're currently on the roll-your-own end of the spectrum, which I think is usually better anyway.
Anyway, basically what you're looking for is a StateBag. Something loosely typed and highly configurable, which doesn't really lend itself very well to an RDBMS, but if you already have the SQL Server in place, it's probably going to be easiest to just use that.
Option 1: Redis
However, another option is to run something like Redis for site configurations. You can store it as a collection of key/value pairs in a Redis object, pull it in a JSON object, and then either convert that to a Hashtable, where lookups will be very fast, or some other hashable collection. A simple wrapper class around the collection would suffice.
Redis is very light and in your case wouldn't require a lot of configuration or maintenance.
Option 2: SQL Server
The way you've mentioned is actually a pretty good way of doing it, but rather than query the database every time, put a strongly typed collection or Hashtable in the cache and let it expire every few hours.
If you go with essentially what you have now, you could store it like this:
Setting
SettingId Int PK,
Key NVarchar(50),
Name NVarchar(50),
Description NVarchar(1000),
Type NVarchar(50),
Priority Int
Configuration
SiteId Int,
SettingId Int FK(Setting),
SettingValue NVarchar(1000)
Don't store things like live/test/dev in the database. You need to have completely separate databases and servers to distinguish between live/test/dev/stage/QA etc.
im currenctly doing this by creating a class which have all the settings as propertiese like
class Setting
{
GUID siteGuid{get; set;}
//other settings
}
then i created a static class SettingManager like this
public static class SettingManager
{
private ConcurrentDictionary<GUID,Setting> settings= new ConcurrentDictionary<GUID,Setting>;
GetSetting(Guid siteGUID)
{
settings.TryGet(siteGuid);
Lastrefreshed = DateTime.Now;
//other code
}
Private DateTime LastRefreshedOn = DateTime.Now;
public void PopulateSetingsDic()
{
//populate the settings dictionary by getting the values from the database
}
}
now anywhere in your code just include the namespace and use the settings.
u can populate the settings once or on every interval in application_start using lastRefreshedOn variable
it will be fast because u have all the settings inside the memory.
also if u want that the u should be able to add the settings dynamically then u can use an ExpandoObject and add settings dynamically using the column names from the database or just a mapping
then ull be able to use the settings by casting the settings ExpandoObject to IDictionary<string,object> here string can be the string converted GUID
Edit:- http://zippedtech.blogspot.in/2012/04/dynaminism-in-net-40.html check the link.. i have added a new post for solution to problems like this.
I would use xml, make a class that can take xpath as your "key"
Ex.
MySett.get("//level1/mysetting")
or even
MySett.get("//mysetting")
where each one can return a collection, just the first one, or what ever you want.
You could even overload.
I like xml because of it's great flexability, and to reduce code elsewher, just write a class.
Downside, You need to load your document at application startup and save at shutdown.
Here is an example class in vb code. (c code would still be very similar, I just used vb because it was up at thetime
Imports System.Xml
Public Class XSett
Public xml As XmlDocument
Public Overloads Function gett(ByVal xp As String)
Return CType(xml.SelectSingleNode(xp), XmlElement).InnerXml
'by using inner xml, you can have either text setting
'or more markup that you might need for another function
'your choice. you could even cast it into another instance
'of this class
End Function
Public Overloads Function gett(ByVal xp As String, ByVal sel As Integer)
Return CType(xml.SelectNodes(xp)(sel), XmlElement).InnerXml
'here, you can have multiple and choose the one you want
End Function
Public Overloads Sub gett(ByVal xp As String, ByRef col As Collection)
Dim i As Integer
Dim nds = xml.SelectNodes(xp)
For i = 0 To nds.Count - 1
col.Add(CType(nds(i), XmlElement).InnerXml)
Next
'Creted an entire collection of elemens.
'i used vb's "collection" object, but any collection would do
End Sub
Public Overloads Sub sett(ByVal ap As String, ByVal name As String, ByVal data As String)
'assume add here.
'ap asks for existing parent element. eg: //guids
'name ask for name of setting element
Dim ts = xml.CreateElement(name)
ts.InnerXml = data
If ap = "" Then 'we assume document element
xml.DocumentElement.AppendChild(ts)
Else
Dim p = CType(xml.SelectSingleNode(ap), XmlElement)
p.AppendChild(ts)
End If
End Sub
Public Overloads Sub sett(ByVal xp As String, ByVal sel As Integer, ByVal data As String)
'just change existing setting
CType(xml.SelectNodes(xp)(sel), XmlElement).InnerXml = data
End Sub
'naturally you can expand infinitely if needed
End Class
If I understand your question correctly you are looking for a centralized configuration framework. For configuration & server management I would normally recommend Chef or Puppet however for ASP.NET I did some quick googling and it seems like the WCF based Configuration Service might do the trick for you. The document I linked to is a step by step tutorial for the configuration service used in the .NET StockTrader 5 Sample Application.
First off, I am new to programming (especially with C#) and thanks for your help.
I have a static web form with about 150 form objects (most checkboxes). I decided to go 1 record per form submission in the sql db. So, for example, question X has a choice of 5 checkboxes. Each of these 5 checkboxes has a column in the db.
I have the post page complete(working) and am building an edit page where I load the record and then populate the form.
How I am doing this is by passing a stored proc the id and then putting all the returned column values into the according object properties, then setting the asp control object to them.
An example of setting the asp controls to the selected value:
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
As you see, this is very tiresome since there are over 150 of these to do. Also, I just found out if the response is NULL then I get the error that it cant be converted to a string. So, I have added this line of code to get past it:
This is the part where I am populating the returned column values into the object properties (entity is the object):
if (!String.IsNullOrEmpty((string)reader["questionX0"].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX0"];}
So, instead of having to add this if then statement 150+ times. There must be a way to do this more efficiently.
First of all, it seems that you are using string.IsNullOrEmpty(value), but this won’t check for the special DBNull value that is returned from databases when the data is null. You should use something more akin to value is DBNull.
The rest of your problem sounds complex, so please don’t be put off if my answer is complex too. Personally I would use custom attributes:
Declare a custom attribute
The following is a skeleton to give you the idea. You may want to use the “Attribute” code snippet in Visual Studio to find out more about how to declare these.
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class QuestionColumnAttribute : Attribute
{
public string ColumnName { get; private set; }
public QuestionColumnAttribute(string columnName)
{
ColumnName = columnName;
}
}
Use the custom attribute in the entity class
Where you declare your entity class, add this custom attribute to every field, for example where patientUnderMdTreatment is declared:
[QuestionColumn("questionX0")]
public string patientUnderMdTreatment;
Iterate over the fields
Instead of iterating over the columns in the reader, iterate over the fields. For each field that has a QuestionColumnAttribute on it, get the relevant column from the reader:
foreach (var field in entity.GetType().GetFields())
{
var attributes = field.GetCustomAttributes(typeof(QuestionColumnAttribute), true);
if (attributes.Length == 0)
continue;
object value = reader[attributes[0].ColumnName];
if (!(value is DBNull))
field.SetValue(entity, value.ToString());
}
For the first part of your question where you set the ASP controls, you can use a similar strategy iterating over the fields of selectedForm, and this is probably simpler because you don’t need a custom attribute — just take only the fields whose name starts with “questionX”.
this is a quick & easy way of doing it.. there are some suggestions to investigate LINQ, and I'd go with those first.
for (int i = 0; i < 150; i++)
{
if (!String.IsNullOrEmpty((string)reader["questionX" + i.ToString()].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX" + i.ToString()];}
}
... though this wouldn't be any good with the
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
lines
so I hear two questions:
- how to deal with null coming from IDataReader?
- how to deal with multiple fields?
Lets start with simple one. Define yourself a helper method:
public static T IsDbNull<T>(object value, T defaultValue)
{
return (T)(value is DBNull ? defaultValue : value);
}
then use it:
entity.patientUnderMdTreatment = IsDbNull<string>(reader["question"], null);
Now how to map entity fields to the form? Well that really is up to you. You can either hardcode it or use reflection. The difference of runtime mapping vs compile-time is likely to be completely irrelevant for your case.
It helps if your form fields have identical names to ones in the DB, so you don't have to do name mapping on top of that (as in Timwi's post), but in the end you'll likely find out that you have to do validation/normalization on many of them anyway at which point hardcoding is really what you need, since there isn't a way to dynamically generate logic according to the changing spec. It doesn't matter if you'll have to rename 150 db fields or attach 150 attributes - in the end it is always a O(n) solution where n is number of fields.
I am still a little unsure why do you need to read data back. If you need to preserve user's input on form reload (due to validation error?) wouldn't it be easier/better to reload them from the request? Also are entity and selectedForm the same object type? I assume its not a db entity (otherwise why use reader at all?).
Its possible that there are some shortcuts you may take, but I am having hard time following what are you reading and writing and when.
I recommend using the NullableDataReader. It eliminates the issue.