How to apply different regular expression to view model property? - c#

I have the following view model in asp.net mvc app.
[Required]
public string Name { get; set; }
[Required]
public int Age { get; set; }
public DateTime DateOfBirth { get; set; }
public Address CurrentAddress { get; set; }
My Address object contains Post Code property, that has RegularExpession attribute to validate UK post codes.
public class Address
{
...
[RegularExpression(#"^[A-Z]{1,2}[0-9][0-9A-Z]? [0-9][A-Z]{2}$")]
public string PostCode { get; set; }
...
}
I want to expand the current functionality to validate PostCode using different regular expression when for example person is non-Uk resident.
Any ideas how I could achieve that? Is there any way to modify regular expression value at run-time?
If you need more information, please let me know and I'll update the question.

You can create your own Person dependand attribute:
public class MyTestAttribute : ValidationAttribute
{
private readonly Regex _regex1;
private readonly Regex _regex2;
public MyTestAttribute(string regex1, string regex2)
{
_regex1 = new Regex(regex1);
_regex2 = new Regex(regex2);
}
public override bool Match(object obj)
{
var input = (string) obj;
if (IsUk())
{
return _regex1.IsMatch(input);
}
return _regex2.IsMatch(input);
}
private bool IsUk()
{
//is person in UK
}
}

Related

Blazor trigger custom validation message

I have the following class which is being used as an input model for an EditForm in a Blazor server side application.
public class KundeInput
{
[ValidateComplexType]
public List<AnsprechpartnerInput> Ansprechpartner { get; } = new List<AnsprechpartnerInput>();
public string? Kundennummer { get; }
[Required]
[MaxLength(60)]
public string Firma { get; set; } = String.Empty;
[MaxLength(60)]
public string? Name2 { get; set; }
[MaxLength(60)]
public string? Name3 { get; set; }
}
As you can see, my model contains a list of another model called AnsprechpartnerInput. Here is this model:
public class AnsprechpartnerInput
{
public string? Kundennummer { get; set; }
public int Nummer { get; } = -1;
[MaxLength(60)]
[Required]
public string Vorname { get; set; } = String.Empty;
[MaxLength(60)]
[Required]
public string Nachname { get; set; } = String.Empty;
[MaxLength(40)]
[Required]
public string? Bereich { get; set; }
/ * More properties */
}
The validation works fine. However, once I have multiple invalid AnsprechpartnerInput models in my list, the ValidationSummary becomes a mess. Because it displays e.g. 5 times field xyz is invalid.
I know I can set a custom message with the ErrorMessage property but I am not able to use other attributes from my model in this message.
What I want to achive is this:
[Required(ErrorMessage = $"Vorname of {Kundennummer} is required")]
public string Vorname { get; set; } = String.Empty;
I already tried to change the message with reflection but accoridng to Microsoft this way is not recommend or supported
https://github.com/dotnet/aspnetcore/issues/25611
Is there any way to get it to work? I thought of string replacement but I am not sure how I can figure out the right model for my ValidationMessage.
Also is there any way to validate the items of the list by one and get a boolean result? Let's say I want to achive this:
#foreach (var ansprechpartner in Input.Ansprechpartner)
{
if (Input.SelectedAnsprechpartner is null)
Input.SelectedAnsprechpartner = ansprechpartner;
<a #onclick="() => Input.SelectedAnsprechpartner = ansprechpartner"
class="#GetNavListClass(Input.SelectedAnsprechpartner == ansprechpartner)"
id="list-ansprechpartner-tab-#(ansprechpartner.Nummer)"
data-toggle="list"
href="#list-ansprechpartner-#(ansprechpartner.Nummer)"
role="tab"
aria-controls="#(ansprechpartner.Nummer)">
#((MarkupString)(ansprechpartner.Nummer < 0 ? "<span class=\"font-weight-bold\">NEU</span>" : $"({ansprechpartner.Nummer})")) #ansprechpartner.Vorname #ansprechpartner.Nachname
</a>
// When the model ansprechpartner is invalid, I want to display an icon
}
Thanks for any help!
PS: Blazor rocks!
You should use a custom validation attribute where you can explicitly add any error message you want
public class KundennummerValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = (AnsprechpartnerInput)validationContext.ObjectInstance;
if(string.IsNullOrEmpty((string)value))
{
return new ValidationResult($"Vorname of {model.Kundennummer} is required", new[] { "Kundennummer" });
}
return ValidationResult.Success;
}
}
then use
[KundennummerValidation]
public string Vorname { get; set; } = String.Empty;
result :
Validation summary:

Dynamic method return type in C#

I need your help. I've got the following situation that I have a method with has to determine some conditions and depending on these conditions, returning an object of a specific type.
Now, I do not want to say public object methodXY() with object as return type but I have the approach which does not seem to work yet.
public T methodXY<T>()
{
if (condition A)
return (T)Convert.ChangeType(myValue, typeof(myType));
else if (condition B)
return (T)Convert.ChangeType(myValue, typeof(myOtherType));
else
throw new exception("xyz")
}
But with this, it seems that I have to set the return type already when calling the method. That's what I don't want and don't can.
//myType looks like this
public class myType
{
public string name;
public string firstname;
public string address;
}
and
//myOtherType looks like
public class myOtherType
{
public string name;
public string firstname;
}
Do you need more or more detailed information? Let me know.
Thanks in advance :-)
EDIT:
Here is the complete code sample of the method with object
public object myMethod(MyDto myDto)
{
userHasRoles = GetUserRoles();
if (userHasRoles .Contains("Admin"))
return (mapper.Map<myType>(myDto));
else if (userHasRoles.Contains("User"))
return (mapper.Map<myOtherType>(myDto));
throw new Exception("No elements!");
}
As far as I understand the problem, you need to return a more complete data when the retriever is the admin, and a not-so-complete one when not.
If that is the objective, then you can retrieve the appropriate data from the database and fill in an object of one of the following classes:
public class PersonData {
public string Name { get; private set; }
public string Surname { get; private set; }
}
public class ExtendedPersonData: PersonData {
public string Name { get; private set; }
public string Surname { get; private set; }
public string Address { get; private set; }
}
Since the latter class inherits from the former, you can just create a List<PersonData> and that will cover both cases.
Another, different approach: the data class takes into account the user in order to return or not certain data:
class Person {
public Person(User usr, string address)
{
this.User = usr;
this.address = address;
}
public string User { get; private set; }
public string Name { get; private set; }
public string Surname { get; private set; }
public string Address {
get {
string toret = "N/A";
if ( this.User.IsAdmin() ) {
toret = this.address;
}
return toret;
}
}
private string address;
}
Neither of both solutions is perfect, and both have their own issues, but the problem, at least how you stated it, cannot be solved.
Hope this helps.

Assigning data to attributes and static attributes directly in c#

I have a method as follows which gets data and stores them to specific variables. I also have two static variables that preserves their value if a condition is met. My question is how can I store this data in attributes in a specific class ?
Like for example, I have a class called UserDetails with attributes :
UserDetails class
public class UserDetails {
public static string RateCountry { get; set; }
public static string RateWeek { get; set; }
public int Start { get; set; }
public int Length { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
Second Class
For now, its working like this. But I want to enhance it by making use of objects.
public static string RateCountry { get; private set; }
public static string RateWeek { get; private set; }
public ActionResult ShowList()
{
int start = Convert.ToInt32(Request["start"]);
int length = Convert.ToInt32(Request["length"]);
string name = Request["search[value]"];
string address = Request[("columns[3][search][value]")];
string rateType = Request[("columns[7][search][value]")];
if (string.IsNullOrEmpty(rateType)) // if null, reset the static variables to null
{
RateCountry = "";
RateWeek = "";
}
else
{
if (CheckDate(rateType)) // if contains date, assign to RateWeek
{
RateWeek = rateType;
}
else
{
RateCountry = rateType; // else if contains a string word, assing to ratecountry
}
}
var items = AssignDetails(start, length, name, address, RateWeek, RateCountry);
return items;
}
Then instead of passing several parameters like start, length, name etc. in the method AssignDetails, I can pass an object of the UserDetails class directly taking into consideration the static variables.
Can someone please help ?
Note: In C#, they are called properties not attributes. Attributes are a totally different thing.
What you want to do is straight forward:
Firstly, you need to change your method so it accepts your class UserDetails as an argument:
public void AssignDetails(UserDetails userDetails)
{
// Use userDetails here to do whatever you want
}
Secondly, when you call the above method, you need to pass the argument to it. You can create an instance of UserDetails and pass it to the AssignDetails method:
var userDetails = new UserDetails
{
Start = start,
Length = length,
Name = name
Address = address
}
I am not sure why RateWeek, and RateCountry properties are static in your class, but to set those you can do them as below (Please note it is using the class and not the instance of the class):
UserDetails.RateWeek = RateWeek;
You could make use of the instance's properties as an indirection to the class' static properties, although all this thing is really ugly in terms of design.
public class UserDetails
{
public static string PersistedRateCountry { get; set; }
public static string PersistedRateWeek { get; set; }
public static string RateCountry
{
get { return string.IsNullOrEmpty(rateType) ? "" : PersistedRateCountry; }
set { PersistedRateCountry = value; }
}
public static string RateWeek
{
get { return string.IsNullOrEmpty(rateType) ? "" : PersistedRateWeek; }
set { PersistedRateWeek= value; }
}
public static string RateWeek { get; set; }
public int Start { get; set; }
public int Length { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I strongly suggest you to move these static properties out to another class, which would be responsible for persisting them.
E.g. try to separate your Data Object (which just holds data) from your Business Object (which contains business logic, and is constructed by receiving a Data Object as parameter). Put all that crazy persistence logic in the Business Object, and use the Business Object everywhere in your code (instead of using the Data Object).
Keep your classes short and clean. If you are coding a lot in the same class, it's probably because you got a bad object-oriented design.

AutoMapper isn't recognizing profile-specific prefixes

I'm trying to use AutoMapper to take data from a class that has prefixes before property names and map it to a second class that doesn't have those prefixes. However, I don't necessarily want it to always strip out that prefix: I just want it to do it for this particular mapping.
My source class looks like this:
public class AdvancedSearchFilterDataModel
{
// ....
public string ServiceMeterNumber { get; set; }
// ....
}
My destination class looks like this:
[DataContract]
public class ServicesAdvancedSearchFilterData : AdvancedSearchFilterData
{
// ....
[DataMember]
public string MeterNumber { get; set; }
// ....
}
When I try to map values like this, it works:
Mapper.Configuration.RecognizePrefixes("Service");
Mapper.CreateMap<AdvancedSearchFilterDataModel, ServicesAdvancedSearchFilterData>();
ServicesAdvancedSearchFilterData servciesFilterData =
Mapper.Map<ServicesAdvancedSearchFilterData>(model);
But I only want "Service" to be recognized as a prefix for certain mappings, since it's also used as a normal part of property names in other mappings. I tried to handle this with a profile, but this didn't work -- no data was mapped:
Mapper.CreateProfile("ServicePrefix").RecognizePrefixes("Service");
Mapper.CreateMap<AdvancedSearchFilterDataModel, ServicesAdvancedSearchFilterData>()
.WithProfile("ServicePrefix");
ServicesAdvancedSearchFilterData servciesFilterData =
Mapper.Map<ServicesAdvancedSearchFilterData>(model);
How can I make it recognize the prefix only when I want it to, either using profiles or some other technique? (I also have other prefixes that I'm going to need it to recognize for other mappings in the same way.)
I achieved this functionality by creating following structure:
I have Person model for my view which is flattened from PersonCombined
public class PersonCombined
{
public Person Person { get; set; }
public Address DefaultAddress { get; set; }
public Contact EmailContact { get; set; }
public Contact PhoneContact { get; set; }
public Contact WebsiteContact { get; set; }
}
public class Person : IWebServiceModel
{
public int ID { get; set; }
public string PersonFirstName { get; set; }
public string PersonSurname { get; set; }
public string PersonDescription { get; set; }
public Nullable<bool> PersonIsActive { get; set; }
}
Then I have separate class for this mapping only that looks like this:
public class PersonCustomMapping : ICustomMapping
{
const string separator = " ";
private static IMappingEngine _MappingEngine;
public IMappingEngine MappingEngine
{
get
{
if (_MappingEngine == null)
{
var configuration = new ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
configuration.RecognizePrefixes("Person");
configuration.RecognizeDestinationPrefixes("Person");
configuration.CreateMap<Person, MCIACRM.Model.Combine.PersonCombined>();
configuration.CreateMap<MCIACRM.Model.Combine.PersonCombined, Person>();
_MappingEngine = new MappingEngine(configuration);
}
return _MappingEngine;
}
}
}
In my generic view I have mappingEngine property like this:
private IMappingEngine mappingEngine
{
get
{
if (_mappingEngine == null)
{
_mappingEngine = AutoMapper.Mapper.Engine;
}
return _mappingEngine;
}
}
Finally in my generic view constructor i have:
public GenericEntityController(IGenericLogic<S> logic, ICustomMapping customMapping)
: base()
{
this._mappingEngine = customMapping.MappingEngine;
this.logic = logic;
}
And that's how I do mapping:
result = items.Project(mappingEngine).To<R>();
or
logic.Update(mappingEngine.Map<S>(wsItem));
Because I use 1 entity per view I can define custom mapping configuration per entity.
Hope this helps

binding a GridView to a List in asp.net c#

This is the first time that I'm doing this, so I need a little bit of help,
I have this code behind:
List<Trucks> FinalListOfTrucks = new List<Trucks>();
public class Trucks
{
public string Placa;
public string Lock;
public string Event;
public DateTime Date;
public string TipoCamion;
public string Person;
public string MissedDate;
}
protected void btnProcess_Click(object sender, EventArgs e)
{
Trucks item = new Trucks();
item.Placa = "MA2323";
item.Lock = "lock1";
item.Event = "Event1";
item.Date = DateTime.Now;
item.TipoCamion = "TRUCK1";
item.Person = "JULIAN";
item.MissedDate = "";
FinalListOfTrucks.Add(item);
gvOriginal.DataSource = FinalListOfTrucks;
gvOriginal.DataBind();
}
in design:
<asp:Button ID="btnProcess" runat="server" Text="Process"
onclick="btnProcess_Click" />
<asp:GridView ID="gvOriginal" runat="server"></asp:GridView>
But trying to run the web app, I'm getting the following error:
The data source for GridView with id 'gvOriginal' did not have any properties or attributes from which to generate columns. Ensure that your data source has content.
Do I have to do anything else, to make this work?
Databinding relies on using properties rather than fields, as the error message you got indicates. You can easily change your code so that Trucks uses properties instead:
public class Trucks
{
public string Placa { get; set; }
public string Lock { get; set; }
public string Event { get; set; }
public DateTime Date { get; set; }
public string TipoCamion { get; set; }
public string Person { get; set; }
public string MissedDate { get; set; }
}
If you make that change everything should work.
Note that there are a number of subtle differences between properties and public fields. A property is effectively syntactic sugar around methods, so public string Placa {get;set;} would be transformed into something similar to:
private string _placa;
public string GetPlaca() { return _placa; }
public void SetPlaca(string value) { _placa = value; }
As for the differences between methods and fields, that's probably beyond the scope of this question.
You can bind to lists gridviews, but your class has to use PROPERTIES, not variables.
public class Trucks
{
public string Placa{get;set;}
public string Lock{get;set;}
public string Event{get;set;}
public DateTime Date{get;set;}
public string TipoCamion{get;set;}
public string Person{get;set;}
public string MissedDate{get;set;}
}

Categories

Resources