I have a class that is an entity from a database that has a bunch of dates represented as strings. For example, it could be something like this:
public class Stuff
{
public string Date1 {get;set;}
public string Date2 {get;set;}
public string Date3 {get;set;}
public string Date4 {get;set;}
}
I then have a Validation method that validates other properties and also validates the date properties. Currently, I am validating each date separately for each object. This works, but I was wondering if there was a way I could make it generic so I didn't have to duplicate code across classes and within the class itself. I am currently doing something like:
public bool ValidateDate(string date)
{
string[] overrides = {"","__/__/____"};
bool success = true;
DateTime dateTime;
if(!overrides.Contains(date) && !DateTime.TryParse(date,out dateTime))
{
success = false;
}
return success;
}
//Notice in this method I am repeating the if statements.
public bool Validate(Stuff stuff, out string message)
{
message = string.Empty;
bool success = true;
if(!ValidateDate(stuff.Date1))
{
success = false;
message = "Date 1 is invalid";
}
if(!ValidateDate(stuff.Date2))
{
success = false;
message = "Date 2 is invalid";
}
if(!ValidateDate(stuff.Date3))
{
success = false;
message = "Date 3 is invalid";
}
if(!ValidateDate(stuff.Date4))
{
success = false;
message = "Date 4 is invalid";
}
return success;
}
void Main()
{
string message;
Stuff stuff = new Stuff();
stuff.Date1 = "01/01/2020";
stuff.Date2 = "__/__/____";
stuff.Date3 = "";
stuff.Date4 = "44/__/____";
bool valid = Validate(stuff, out message);
}
I thought about doing something like:
public bool Validate<T>(T value, out string message)
{
//Validation here
}
But, correct me if I am wrong, but this would require that I get the properties and use reflection to check the value of the date and my other problem with this is that the properties are strings, so there is no way for me to check if it is a DateTime?
I feel like I'm missing some information - right now the duplication that I see is that you are calling ValidateDate multiple times. I don't think there is a way around that - you have to call Validate on each Date property, unless (as you mentioned) you want to go the reflection route.
With reflection you would simply iterate through all properties and find any property who's name matches the pattern Date[number], you would then validate that it was indeed a DateTime (with Parse like you already do) and then move on.
If the number of fields is known and isn't too much I'd stick with your ValidateMethod though notice that message will currently only ever show you the last error (it gets overwritten each time).
You could get cute and write a method like:
public bool Validate(Stuff stuff, out string message)
{
message = "Invalid Date(s): ";
return ValidateDates(ref message, stuff.Date1, stuff.Date2, stuff.Date3, stuff.Date4);
}
public bool ValidateDate(ref string message, params string[] dates)
{
bool rv = true;
for (int i = 0; i < dates.Length; i++)
{
if (![validate DateTime as above])
{
message += i + " "; // add the failed index to the message
rv = false;
}
}
return rv;
}
The best you can do is probably something like this, although I don't advocate using string to represent dates.
public bool ValidateDates(params string[] dates)
{
return dates.All( d => ValidateDate(d));
}
public bool Validate(Stuff stuff, out string message)
{
ValidateDates(stuff.Date1,stuff.Date2,stuff.Date3);
}
You can obviously play around with this to know which date failed, for instance you can do something like
var item = dates.Select( d, i => new
{
Valid = ValidateDate(d),
Message = String.Format("Date {0} failed", i)
}).FirstOrDefault( x => !x.Valid);
if(item == null) //everything is valid
return true;
else
//set the out param
outMessageStr = item.Message;
return false;
You could make everything generic by using interfaces for the objects that you want to get dates from.
For instance:
//Defines an interface that provides a function to get multiple dates from an object.
public interface IGetDates
{
//You could use a Read-Only property too
string[] GetDates();
}
public class Stuff : IGetDate
{
//other stuff...
public string[] GetDates()
{
return new[]{ Date1, Date2, Date2, ect...};
}
}
Then your generic method would look like this:
//Uses generic constraints so only objects that use the
//IGetDates interface can call this method.
public bool Validate<T>(T stuff, out string message) where T : IGetDates
{
message = string.Empty;
bool success = true;
string[] dates = stuff.GetDates();
for(int i = 0; i < dates.Length; i++)
{
if(!Validate(dates[i]))
{
success = false;
message = string.Format("Date {0} is invalid.", i);
}
}
return success;
}
Related
I often want to parse a string into various bits and have a readable way to return them.
I like this approach, but it involves creating a specific class
long orderID = Utils.UnTradeIdent(tradeIdent).OrderID;
In Utils.cs:
public class TradeIdentData
{
public string AccountIdent;
public long OrderID;
public string SubID;
}
public static TradeIdentData UnTradeIdent(string tradeIdent)
{
TradeIdentData tradeIdentData = new TradeIdentData();
var parts = tradeIdent.Split('!');
tradeIdentData.AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
tradeIdentData.OrderID = long.Parse(bits[1]);
tradeIdentData.SubID = bits[1];
}
else
{
tradeIdentData.OrderID = long.Parse(parts[1]);
tradeIdentData.SubID = "";
}
return tradeIdentData;
}
A separate class with well-named properties (which you are already using) is currently the most readable way of doing this.
In C# 7 you will be able to use tuples for return values, like so:
public static (string AccountIdent, string OrderID, string SubID) UnTradeIdent(string tradeIdent)
{
string accountIdent, orderID, subID ;
... Code to initialise accountIdent, orderID and subID appropriately ...
// Now return the data as a tuple:
return (accountIdent, orderID, subID);
}
You can consume this as follows:
long orderID = Utils.UnTradeIdent(tradeIdent).OrderID;
Or if you want all the values:
var result = Utils.UnTradeIdent(tradeIdent);
// Use result.OrderId, result.SubID or result.AccountIdent
This is not going to be available until some time next year, though.
Also, even though this new tuple support makes it more convenient to WRITE the code, it doesn't let you document it using XML comments as well. Spending the time to write a simple and well-documented class will still often be better than using the new C# 7 tuple support.
See here for more details.
You can also use the out keyword to pass arguments by reference, see MSDN article out (C# Reference):
public static void UnTradeIdent(string tradeIdent, out string AccountIdent, out long OrderID, out string SubID)
{
var parts = tradeIdent.Split('!');
AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
OrderID = long.Parse(bits[1]);
SubID = bits[1];
}
else
{
OrderID = long.Parse(parts[1]);
SubID = "";
}
}
UPDATED with suggestion from comments:
public static bool UnTradeIdent(string tradeIdent, out string AccountIdent, out long OrderID, out string SubID)
{
bool result = false;
AccountIdent = "";
OrderID = 0;
SubID = "";
try
{
var parts = tradeIdent.Split('!');
AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
OrderID = long.Parse(bits[1]);
SubID = bits[1];
}
else
{
OrderID = long.Parse(parts[1]);
SubID = "";
}
}
catch(ArgumentNullException ane)
{
// Handle parsing exception
}
catch (FormatException fe)
{
// Handle parsing exception
}
catch (OverflowException oe)
{
// Handle parsing exception
}
return result;
}
Its pretty simple to do just by changing the return type to dynamic and using an anonymous class
public static dynamic UnTradeIdent(string tradeIdent)
{
var value1 = //parselogic
var value2 = //parselogic
return new { Identity = value1, Item2 = value2};
}
I would consider making additional static methods. Your current implementation is cleaner when you require all of the returned properties, but something like below might be appropriate when you only need one of them.
public static string TradeIdentToAccountIdent(string tradeIdent)
{
var parts = tradeIdent.Split('!');
return parts[0];
}
public static long TradeIdentToOrderID(string tradeIdent)
{
var parts = tradeIdent.Split('!');
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
return long.Parse(bits[1]); // Taken from your example, should probably be bits[0]?
}
else
return long.Parse(parts[1]);
}
// My own take on it this time, you could obviously use your logic as well.
public static string TradeIdentToSubID(string tradeIdent)
{
var order = tradeIdent.Split('!')[1];
if (order.Contains("."))
return order.Split('.')[1];
else
return String.Empty;
}
I have a pipe delimited line in a text file. What is the best way to validate the line. I have a definite format for how each token in the line should be i.e for eg; say 5th item should be a date.
Could anyone help me what is the best object oriented way to achive this?Is there any design pattern to achieve this?
Thanks
You're looking for a specific pattern for validation. That can be done any number of ways, but the simplest is to validate in the constructor of the object. Since you're looking for a more OO approach, you might consider creating an object to represent the file and an object to represent each record. There's no real pattern here other than associational. You could, however, utilize the iterator pattern to allow your records to be iterated in a loop. You're talking about reading a text file so that's not complicated enough of a process, but if it was, you could consider a factory pattern to create the file object. If there's a lot to validate, then you could create a separate method to validate each in your class. Here's an example of what I'm talking about...
static void Main(string[] args)
{
DataFile myFile = new DataFile(#"C:\...");
foreach (DataRecord item in myFile)
{
Console.WriteLine("ID: {0}, Name: {1}, StartDate: {2}", item.ID, item.Name, item.StartDate);
}
Console.ReadLine();
}
public class DataFile : IEnumerable<DataRecord>
{
HashSet<DataRecord> _items = new HashSet<DataRecord>();
public DataFile(string filePath)
{
// read your file and obtain record data here...
//I'm not showing that
//... then begin iterating through your string results
//... though I'm only showing one record for this example
DataRecord record = new DataRecord("1234|11-4-2015|John Doe");
_items.Add(record);
}
public IEnumerator<DataRecord> GetEnumerator()
{
foreach (DataRecord item in _items)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class DataRecord
{
private int _id;
public int ID
{
get { return _id; }
private set { _id = value; }
}
private DateTime _startDate;
public DateTime StartDate
{
get { return _startDate; }
private set { _startDate = value; }
}
private string _name;
public string Name
{
get { return _name; }
private set { _name = value; }
}
internal DataRecord(string delimitedRecord)
{
if (delimitedRecord == null)
throw new ArgumentNullException("delimitedRecord");
string[] items = delimitedRecord.Split('|');
//You could put these in separate methods if there's a lot
int id = 0;
if (!int.TryParse(items[0], out id))
throw new InvalidOperationException("Invalid type...");
this.ID = id;
DateTime startDate = DateTime.MinValue;
if (!DateTime.TryParse(items[1], out startDate))
throw new InvalidOperationException("Invalid type...");
this.StartDate = startDate;
//This one shouldn't need validation since it's already a string and
//will probably be taken as-is
string name = items[2];
if (string.IsNullOrEmpty(name))
throw new InvalidOperationException("Invalid type...");
this.Name = name;
}
}
The "clean" way for achieve this would be to use Regular Expression. Here is a basic example :
var allLines = new List<string>();
for (int i = 0; i < 5; i++)
{
allLines.Add("test" + i);
}
// if you add this line, it will fail because the last line doesn't match the reg ex
allLines.Add("test");
var myRegEx = #"\w*\d"; // <- find the regex that match your lines
Regex regex = new Regex(myRegEx);
var success = allLines.All(line => regex.Match(line).Success);
In this example, my regex is waiting for a word immediately followed by a digit. All you have to do is to find the regex that match your line.
You can also avoid the linq expression by using a more complexe reg ex.
Give us your token so we can help you with.
It is possible for a function to read/return the value of a string whose name is passed to it?
For example, I want to return true if the value of a string is not "0" by telling the function which string I want it to check.
public static bool IsEnabled(string sName)
{
if (TenderTypes.<sName> != "0")
{
return true;
}
else
{
return false;
}
}
This is mainly to make things quicker while I'm coding, I want to be able to keep the strings there but at the same time I want to be able to disable them by changing their value from a string number, to zero.
Let's say you have something like this:
class TenderTypes
{
static string myStr = "Some value";
static string anotherStr = "A different value";
// ...
};
And you want to query that string by the name of a field (for example "myStr").
Well, it can be done with reflection. But first an alternative:
Using a Dictionary
class TenderTypes
{
static Dictionary<string, string> strings =
new Dictionary<string, string>()
{
{"myStr", "Some value},
{"anotherStr", "Some value}
}
//...
}
Then you could write your method like this:
public static bool IsEnabled(string sName)
{
if (TenderTypes.strings[sName] != "0")
{
return true;
}
else
{
return false;
}
}
Using Reflection
Then you could write your method like this:
public static bool IsEnabled(string sName)
{
var type = typeof(TenderTypes);
var field = type.GetField(sName, BindingFlags.Static);
if (field.GetValue(null) != "0")
{
return true;
}
else
{
return false;
}
}
Don't know exactly what you want to achieve, your syntax looks like you are trying to query something generic <>... this is not really possible.
Maybe a simple dictionary will help you?
Create a new Dictionary<string, bool> should also help you if your "settings" are enabled or disabled...
I am trying to develop a module which will read excel sheets (possibly from other data sources too, so it should be loosely coupled) and convert them into Entities so to save.
The logic will be this:
The excel sheet can be in different format, for example column names in Excel sheet can be different so my system needs to be able to map different fields to my entities.
For now I will be assuming the format defined above will be same and hardcoded for now instead of coming from database dynamically after set on a configuration mapping UI kinda thing.
The data needs to be validated before even get mapped. So I should be able validate it beforehand against something. We're not using like XSD or something else so I should validate it against the object structure I am using as a template for importing.
The problem is, I put together some things together but I don't say I liked what I did. My Question is how I can improve the code below and make things more modular and fix the validation issues.
The code below is a mock-up and is not expected to work, just to see some structure of the design.
This is code I've come up with so far, and I've realized one thing that I need to improve my design patterns skills but for now I need your help, if you could help me:
//The Controller, a placeholder
class UploadController
{
//Somewhere here we call appropriate class and methods in order to convert
//excel sheet to dataset
}
After we uploaded file using an MVC Controller, there could be different controllers specialized to import certain behaviors, in this example I will uploading person related tables,
interface IDataImporter
{
void Import(DataSet dataset);
}
//We can use many other importers besides PersonImporter
class PersonImporter : IDataImporter
{
//We divide dataset to approprate data tables and call all the IImportActions
//related to Person data importing
//We call inserting to database functions here of the DataContext since this way
//we can do less db roundtrip.
public string PersonTableName {get;set;}
public string DemographicsTableName {get;set;}
public Import(Dataset dataset)
{
CreatePerson();
CreateDemograhics();
}
//We put different things in different methods to clear the field. High cohesion.
private void CreatePerson(DataSet dataset)
{
var personDataTable = GetDataTable(dataset,PersonTableName);
IImportAction addOrUpdatePerson = new AddOrUpdatePerson();
addOrUpdatePerson.MapEntity(personDataTable);
}
private void CreateDemograhics(DataSet dataset)
{
var demographicsDataTable = GetDataTable(dataset,DemographicsTableName);
IImportAction demoAction = new AddOrUpdateDemographic(demographicsDataTable);
demoAction.MapEntity();
}
private DataTable GetDataTable(DataSet dataset, string tableName)
{
return dataset.Tables[tableName];
}
}
I have IDataImporter and specialized concrete class PersonImporter. However, I am not sure it looks good so far since things should be SOLID so basically easy to extend later in the project cycle, this will be a foundation for future improvements, lets keep going:
IImportActions are where the magic mostly happens. Instead of designing things table based, I am developing it behavior based so one can call any of them to import things in more modular model. For example a table may have 2 different actions.
interface IImportAction
{
void MapEntity(DataTable table);
}
//A sample import action, AddOrUpdatePerson
class AddOrUpdatePerson : IImportAction
{
//Consider using default values as well?
public string FirstName {get;set;}
public string LastName {get;set;}
public string EmployeeId {get;set;}
public string Email {get;set;}
public void MapEntity(DataTable table)
{
//Each action is producing its own data context since they use
//different actions.
using(var dataContext = new DataContext())
{
foreach(DataRow row in table.Rows)
{
if(!emailValidate(row[Email]))
{
LoggingService.LogWarning(emailValidate.ValidationMessage);
}
var person = new Person(){
FirstName = row[FirstName],
LastName = row[LastName],
EmployeeId = row[EmployeeId],
Email = row[Email]
};
dataContext.SaveObject(person);
}
dataContext.SaveChangesToDatabase();
}
}
}
class AddOrUpdateDemographic: IImportAction
{
static string Name {get;set;}
static string EmployeeId {get;set;}
//So here for example, we will need to save dataContext first before passing it in
//to get the PersonId from Person (we're assuming that we need PersonId for Demograhics)
public void MapEntity(DataTable table)
{
using(var dataContext = new DataCOntext())
{
foreach(DataRow row in table.Rows)
{
var demograhic = new Demographic(){
Name = row[Name],
PersonId = dataContext.People.First(t => t.EmployeeId = int.Parse(row["EmpId"]))
};
dataContext.SaveObject(person);
}
dataContext.SaveChangesToDatabase();
}
}
}
And the validation, which mostly where I suck at unfortunately. The validation needs to be easy to extend and loosely coupled and also I need to be able to call this validation beforehand instead of adding everything.
public static class ValidationFactory
{
public static Lazy<IFieldValidation> PhoneValidation = new Lazy<IFieldValidation>(()=>new PhoneNumberValidation());
public static Lazy<IFieldValidation> EmailValidation = new Lazy<IFieldValidation>(()=>new EmailValidation());
//etc.
}
interface IFieldValidation
{
string ValidationMesage{get;set;}
bool Validate(object value);
}
class PhoneNumberValidation : IFieldValidation
{
public string ValidationMesage{get;set;}
public bool Validate(object value)
{
var validated = true; //lets say...
var innerValue = (string) value;
//validate innerValue using Regex or something
//if validation fails, then set ValidationMessage propert for logging.
return validated;
}
}
class EmailValidation : IFieldValidation
{
public string ValidationMesage{get;set;}
public bool Validate(object value)
{
var validated = true; //lets say...
var innerValue = (string) value;
//validate innerValue using Regex or something
//if validation fails, then set ValidationMessage propert for logging.
return validated;
}
}
I have done the same thing on a project. The difference is that I didn't have to import Excel sheets, but CSV files. I created a CSVValueProvider. And, therefore, the CSV data was bound to my IEnumerable model automatically.
As for validation, I figured that going through all rows, and cells, and validating them one by one is not very efficient, especially when the CSV file has thousands of records. So, what I did was that I created some validation methods that went through the CSV data column by column, instead of row by row, and did a linq query on each column and returned the row numbers of the cells with invalid data. Then, added the invalid row number/column names to ModelState.
UPDATE:
Here is what I have done...
CSVReader Class:
// A class that can read and parse the data in a CSV file.
public class CSVReader
{
// Regex expression that's used to parse the data in a line of a CSV file
private const string ESCAPE_SPLIT_REGEX = "({1}[^{1}]*{1})*(?<Separator>{0})({1}[^{1}]*{1})*";
// String array to hold the headers (column names)
private string[] _headers;
// List of string arrays to hold the data in the CSV file. Each string array in the list represents one line (row).
private List<string[]> _rows;
// The StreamReader class that's used to read the CSV file.
private StreamReader _reader;
public CSVReader(StreamReader reader)
{
_reader = reader;
Parse();
}
// Reads and parses the data from the CSV file
private void Parse()
{
_rows = new List<string[]>();
string[] row;
int rowNumber = 1;
var headerLine = "RowNumber," + _reader.ReadLine();
_headers = GetEscapedSVs(headerLine);
rowNumber++;
while (!_reader.EndOfStream)
{
var line = rowNumber + "," + _reader.ReadLine();
row = GetEscapedSVs(line);
_rows.Add(row);
rowNumber++;
}
_reader.Close();
}
private string[] GetEscapedSVs(string data)
{
if (!data.EndsWith(","))
data = data + ",";
return GetEscapedSVs(data, ",", "\"");
}
// Parses each row by using the given separator and escape characters
private string[] GetEscapedSVs(string data, string separator, string escape)
{
string[] result = null;
int priorMatchIndex = 0;
MatchCollection matches = Regex.Matches(data, string.Format(ESCAPE_SPLIT_REGEX, separator, escape));
// Skip empty rows...
if (matches.Count > 0)
{
result = new string[matches.Count];
for (int index = 0; index <= result.Length - 2; index++)
{
result[index] = data.Substring(priorMatchIndex, matches[index].Groups["Separator"].Index - priorMatchIndex);
priorMatchIndex = matches[index].Groups["Separator"].Index + separator.Length;
}
result[result.Length - 1] = data.Substring(priorMatchIndex, data.Length - priorMatchIndex - 1);
for (int index = 0; index <= result.Length - 1; index++)
{
if (Regex.IsMatch(result[index], string.Format("^{0}.*[^{0}]{0}$", escape)))
result[index] = result[index].Substring(1, result[index].Length - 2);
result[index] = result[index].Replace(escape + escape, escape);
if (result[index] == null || result[index] == escape)
result[index] = "";
}
}
return result;
}
// Returns the number of rows
public int RowCount
{
get
{
if (_rows == null)
return 0;
return _rows.Count;
}
}
// Returns the number of headers (columns)
public int HeaderCount
{
get
{
if (_headers == null)
return 0;
return _headers.Length;
}
}
// Returns the value in a given column name and row index
public object GetValue(string columnName, int rowIndex)
{
if (rowIndex >= _rows.Count)
{
return null;
}
var row = _rows[rowIndex];
int colIndex = GetColumnIndex(columnName);
if (colIndex == -1 || colIndex >= row.Length)
{
return null;
}
var value = row[colIndex];
return value;
}
// Returns the column index of the provided column name
public int GetColumnIndex(string columnName)
{
int index = -1;
for (int i = 0; i < _headers.Length; i++)
{
if (_headers[i].Replace(" ","").Equals(columnName, StringComparison.CurrentCultureIgnoreCase))
{
index = i;
return index;
}
}
return index;
}
}
CSVValueProviderFactory Class:
public class CSVValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
var uploadedFiles = controllerContext.HttpContext.Request.Files;
if (uploadedFiles.Count > 0)
{
var file = uploadedFiles[0];
var extension = file.FileName.Split('.').Last();
if (extension.Equals("csv", StringComparison.CurrentCultureIgnoreCase))
{
if (file.ContentLength > 0)
{
var stream = file.InputStream;
var csvReader = new CSVReader(new StreamReader(stream, Encoding.Default, true));
return new CSVValueProvider(controllerContext, csvReader);
}
}
}
return null;
}
}
CSVValueProvider Class:
// Represents a value provider for the data in an uploaded CSV file.
public class CSVValueProvider : IValueProvider
{
private CSVReader _csvReader;
public CSVValueProvider(ControllerContext controllerContext, CSVReader csvReader)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (csvReader == null)
{
throw new ArgumentNullException("csvReader");
}
_csvReader = csvReader;
}
public bool ContainsPrefix(string prefix)
{
if (prefix.Contains('[') && prefix.Contains(']'))
{
if (prefix.Contains('.'))
{
var header = prefix.Split('.').Last();
if (_csvReader.GetColumnIndex(header) == -1)
{
return false;
}
}
int index = int.Parse(prefix.Split('[').Last().Split(']').First());
if (index >= _csvReader.RowCount)
{
return false;
}
}
return true;
}
public ValueProviderResult GetValue(string key)
{
if (!key.Contains('[') || !key.Contains(']') || !key.Contains('.'))
{
return null;
}
object value = null;
var header = key.Split('.').Last();
int index = int.Parse(key.Split('[').Last().Split(']').First());
value = _csvReader.GetValue(header, index);
if (value == null)
{
return null;
}
return new ValueProviderResult(value, value.ToString(), CultureInfo.CurrentCulture);
}
}
For the validation, as I mentioned before, I figured that it would not be efficient to do it using DataAnnotation attributes. A row by row validation of the data would take a long time for CSV files with thousands of rows. So, I decided to validate the data in the Controller after the Model Binding is done. I should also mention that I needed to validate the data in the CSV file against some data in the database. If you just need to validate things like Email Address or Phone Number, you might as well just use DataAnnotation.
Here is a sample method for validating the Email Address column:
private void ValidateEmailAddress(IEnumerable<CSVViewModel> csvData)
{
var invalidRows = csvData.Where(d => ValidEmail(d.EmailAddress) == false).ToList();
foreach (var invalidRow in invalidRows)
{
var key = string.Format("csvData[{0}].{1}", invalidRow.RowNumber - 2, "EmailAddress");
ModelState.AddModelError(key, "Invalid Email Address");
}
}
private static bool ValidEmail(string email)
{
if(email == "")
return false;
else
return new System.Text.RegularExpressions.Regex(#"^[\w-\.]+#([\w-]+\.)+[\w-]{2,6}$").IsMatch(email);
}
UPDATE 2:
For validation using DataAnnotaion, you just use DataAnnotation attributes in your CSVViewModel like below (the CSVViewModel is the class that your CSV data will be bound to in your Controller Action):
public class CSVViewModel
{
// User proper names for your CSV columns, these are just examples...
[Required]
public int Column1 { get; set; }
[Required]
[StringLength(30)]
public string Column2 { get; set; }
}
I have a class(KeywordProperties) with this code :
public class KeywordProperties
{
[DisplayMode("0-1,0-2,0-3,1-1,1-2,1-3,1-6,1-9,1-10,1-11,1-12,2-1,2-2,2-3,2-9,2-10,2-12,3-1,3-2,3-3,3-10,3-12,4-13,5,6")]
public string Onvaan { get; set; }
[DisplayMode("0-1,0-2,0-3,1-1,1-2,1-3,1-6,1-9,1-10,1-11,1-12,2-1,2-2,2-3,2-9,2-10,2-12,3-1,3-2,3-3,3-10,3-12,4-13,5,6")]
public string MozooKolli { get; set; }
[DisplayMode("0-10,1-10,3-10,3-12,5,6")]
public string EsmeDars { get; set; }
[DisplayMode("0-1,1-1,2-1,2-2,3-1,6")]
public string Sokhanraan { get; set; }
[DisplayMode("0-10,1-2,2-1,2-10,3-10,6")]
public string Modares { get; set; }
}
And I have another for check attributes :
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DisplayModeAttribute : Attribute
{
private readonly string mode;
public DisplayModeAttribute(string mode)
{
this.mode = mode ?? "";
}
public override bool Match(object obj)
{
var other = obj as DisplayModeAttribute;
if (other == null) return false;
if (other.mode == mode) return true;
// allow for a comma-separated match, in either direction
if (mode.IndexOf(',') >= 0)
{
string[] tokens = mode.Split(',');
if (Array.IndexOf(tokens, other.mode) >= 0) return true;
}
else if (other.mode.IndexOf(',') >= 0)
{
string[] tokens = other.mode.Split(',');
if (Array.IndexOf(tokens, mode) >= 0) return true;
}
return false;
}
}
I want display properties in propertygrid with this code :
String Code = "":
KeywordProperties Kp = new KeywordProperties();
propertygrid1.SelectedObject = Kp;
propertygrid1.BrowsableAttributes = new AttributeCollection(new DisplayModeAttribute(Code));
When Code vlue is "0-1" or "5" or ...(single value), I can see my properties.
But, when use "0-1,1-2" for Code, I can't see any thing in my properygrid.
How can I see these data :
1- All properties that have code 0-1 and code 1-2 :
result is :Onvaan,MozooKolli
2- All properties that have code 0-1 or code 1-2 :
result is : Onvaan,MozooKolli,Sokhanraan,Modares
It appears that your code only matches DisplayModeAttributes when both have a single value, or one contains a single value and the other contains multiple values; it won't match them when both contain multiple values, unless the list of values are identical.
To use your code as-is, you could change the way you populate PropertyGrid.BrowsableAttributes:
propertygrid1.BrowsableAttributes = new AttributeCollection(
new DisplayModeAttribute("0-1"),
new DisplayModeAttribute("1-2")
// etc.
);
Alternatively, to fix your matching code, you could replace it with something like:
public override bool Match(object obj)
{
var other = obj as DisplayModeAttribute;
if (other == null)
return false;
if (other.mode == mode)
return true;
string[] modes = mode.Split(',');
string[] others = other.mode.Split(',');
var matches = modes.Intersect(others);
return matches.Count() > 0;
}
This uses the LINQ Intersect method, which returns the elements that two lists have in common.