Can there be multiple instances of a dependency in Xamarin.Forms? - xamarin.forms

I have mirrored some code from XLabs to get Network State Tracking on my device from here: Xlabs INetwork Android Implementation
I've pretty much aligned it with coding standards here and changed some method names - everything is working fine, except for some reason on the android implementation i am unable to get my event handlers to respond, the same way they do on iOS.
This is my Adaptation for Android:
[assembly: Dependency(typeof(STM.Droid.DependencyInjection.Network))]
[assembly: UsesPermission("android.permission.ACCESS_NETWORK_STATE")]
[assembly: UsesPermission("android.permission.ACCESS_WIFI_STATE")]
[assembly: UsesPermission("android.permission.CHANGE_NETWORK_STATE")]
[assembly: UsesPermission("android.permission.CHANGE_WIFI_STATE")]
namespace Namespace.Droid
{
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new [] { Android.Net.ConnectivityManager.ConnectivityAction })]
public class Network : BroadcastReceiver, INetwork
{
/// <summary>
/// Internets the connection status.
/// </summary>
/// <returns>NetworkStatus.</returns>
public NetworkStatus GetConnectionStatus()
{
var status = NetworkStatus.NotReachable;
using (var cm = (ConnectivityManager)Application.Context.GetSystemService(Context.ConnectivityService))
using (var ni = cm.ActiveNetworkInfo)
{
if (ni != null && ni.IsConnectedOrConnecting)
{
var name = ni.TypeName.ToUpper();
if (name.Contains("WIFI"))
{
status = NetworkStatus.ReachableViaWiFiNetwork;
}
else if (name.Contains("MOBILE"))
{
status = NetworkStatus.ReachableViaCarrierDataNetwork;
}
else
{
status = NetworkStatus.ReachableViaUnknownNetwork;
}
}
}
return status;
}
private readonly object lockObject = new object();
private EventHandler<NetworkStatus> _reachabilityChanged;
public event EventHandler<NetworkStatus> ReachabilityChanged
{
add
{
lock (this.lockObject)
{
this._reachabilityChanged += value;
}
}
remove
{
lock (this.lockObject)
{
this._reachabilityChanged -= value;
}
}
}
/// <summary>
/// Occurs when [reachability changed].
/// </summary>
// public event EventHandler<NetworkStatus> ReachabilityChanged;
/// <summary>
/// Determines whether the specified host is reachable.
/// </summary>
/// <param name="host">The host.</param>
/// <param name="timeout">The timeout.</param>
public Task<bool> IsReachableAsync(string host, TimeSpan timeout)
{
return Task.Run(
() =>
{
try
{
var address = InetAddress.GetByName(host);
return address != null; // && (address.IsReachable((int)timeout.TotalMilliseconds) || );
}
catch (UnknownHostException)
{
return false;
}
});
}
/// <summary>
/// Determines whether [is reachable by wifi] [the specified host].
/// </summary>
/// <param name="host">The host.</param>
/// <param name="timeout">The timeout.</param>
public async Task<bool> IsReachableByWifiAsync(string host, TimeSpan timeout)
{
return GetConnectionStatus() == NetworkStatus.ReachableViaWiFiNetwork && await IsReachableAsync(host, timeout);
}
/// <summary>
/// This gets called by OS when the <see cref="ConnectivityManager.ConnectivityAction"/> <see cref="Intent"/> fires.
/// </summary>
/// <param name="context">Context for the intent.</param>
/// <param name="intent">Intent information.</param>
public override void OnReceive(Context context, Intent intent)
{
// this is a workaround - which oddly enough forwards events the way i would expect it to.
MessagingCenter.Send(this as INetwork, string.Empty, GetConnectionStatus());
// THIS IS ALWAYS NULL!
var handler = _reachabilityChanged;
if (handler != null)
{
var connectionStatus = this.GetConnectionStatus();
handler(this, connectionStatus);
}
}
}
}
The error in question occurs in OnReceive.
While in debugging i do notice that the handlers are attached correctly on the PCL side - there will just be null on that _reachabilityChanged field oddly enough.
As debugging indicates i am apparently getting
{md50e2cce2f1202796e628729fe0540389b.Network#b422684}
on the droid OnReceive method for "this"
while on the PCL side i am getting
{md50e2cce2f1202796e628729fe0540389b.Network#b562641}
when calling DependencyService.Get
To me this looks like the reason the handler list is empty, is because i am subscribing my handler on another instance and therefore do not get any handlers when a different instance of Network calls OnReceive.
Is it even possible for Xamarin.Forms DependencyService to provide 2 or more instances of an instance? Because that would surprise me a lot...
UPDATE:
In case someone needs a workaround - this works as well:
public event EventHandler<NetworkStatus> ReachabilityChanged
{
add { MessagingCenter.Subscribe(value.Target, string.Empty, (INetwork d, NetworkStatus a) => value(d, a)); }
remove { MessagingCenter.Unsubscribe<INetwork>(value.Target, string.Empty); }
}

for new instance
_client = DependencyService.Get<YOUINTERFACE>(DependencyFetchTarget.NewInstance);
by default DependencyService is keep single

Related

Can ASP.NET Core 7 with Blazor use NavigateToLogin with a domain hint?

When using the new .NavigateToLogin in a Blazor app in .NET 7 as recommended here, how do I pass a domain hint when calling NavigateToLogin (or NavigateToLogout)? Or is there a way via setup to make the domain hint be added automatically?
Without the domain hint, my users now have an extra step for both log in and out. (I am using MSAL for Open ID Connect with Azure AD.)
From this page, it appears as though I can new-up a InteractiveRequestOptions object, run options.TryAddAdditionalParameter("domain_hint", "mydomain.com");, and pass that into Navigation.NavigateToLogin--but it doesn't work at all; it is simply ineffective.
I think this issue is still applicable: https://github.com/dotnet/aspnetcore/issues/40046#issuecomment-1042575825 - at least that's how I solved it. Not sure if there's a better way to do this.
So, step 1: You add class AuthExtensions:
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace Your.Namespace;
/// <summary>
/// https://github.com/dotnet/aspnetcore/issues/40046
/// </summary>
public static class AuthExtensions
{
/// <summary>
/// Adds support for Auth0 authentication for SPA applications using <see cref="Auth0OidcProviderOptions"/> and the <see cref="RemoteAuthenticationState"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="configure">An action that will configure the <see cref="RemoteAuthenticationOptions{TProviderOptions}"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> where the services were registered.</returns>
public static IRemoteAuthenticationBuilder<RemoteAuthenticationState, RemoteUserAccount> AddAuth0OidcAuthentication(this IServiceCollection services, Action<RemoteAuthenticationOptions<Auth0OidcProviderOptions>> configure)
{
services.TryAddEnumerable(ServiceDescriptor.Scoped<IPostConfigureOptions<RemoteAuthenticationOptions<Auth0OidcProviderOptions>>, DefaultAuth0OidcOptionsConfiguration>());
return services.AddRemoteAuthentication<RemoteAuthenticationState, RemoteUserAccount, Auth0OidcProviderOptions>(configure);
}
}
public class Auth0OidcProviderOptions : OidcProviderOptions
{
public MetadataSeed MetadataSeed { get; set; } = new();
}
public class MetadataSeed
{
[JsonPropertyName("end_session_endpoint")]
public string EndSessionEndpoint { get; set; } = null!;
}
// Copy/paste from Microsoft.AspNetCore.Components.WebAssembly.Authentication with the option type changed.
public class DefaultAuth0OidcOptionsConfiguration : IPostConfigureOptions<RemoteAuthenticationOptions<Auth0OidcProviderOptions>>
{
private readonly NavigationManager _navigationManager;
public DefaultAuth0OidcOptionsConfiguration(NavigationManager navigationManager) => _navigationManager = navigationManager;
public void Configure(RemoteAuthenticationOptions<Auth0OidcProviderOptions> options)
{
if (options == null)
{
return;
}
options.UserOptions.AuthenticationType ??= options.ProviderOptions.ClientId;
var redirectUri = options.ProviderOptions.RedirectUri;
if (redirectUri == null || !Uri.TryCreate(redirectUri, UriKind.Absolute, out _))
{
redirectUri ??= "authentication/login-callback";
options.ProviderOptions.RedirectUri = _navigationManager.ToAbsoluteUri(redirectUri).AbsoluteUri;
}
var logoutUri = options.ProviderOptions.PostLogoutRedirectUri;
if (logoutUri == null || !Uri.TryCreate(logoutUri, UriKind.Absolute, out _))
{
logoutUri ??= "authentication/logout-callback";
options.ProviderOptions.PostLogoutRedirectUri = _navigationManager.ToAbsoluteUri(logoutUri).AbsoluteUri;
}
}
public void PostConfigure(string name, RemoteAuthenticationOptions<Auth0OidcProviderOptions> options)
{
if (string.Equals(name, Options.DefaultName, StringComparison.Ordinal))
{
Configure(options);
}
}
}
Then in your program.cs you wire it up like this:
builder.Services.AddAuth0OidcAuthentication(options =>
{
var authority = builder.Configuration["GoogleAuth:Authority"];
var clientId = builder.Configuration["GoogleAuth:ClientId"];
options.ProviderOptions.MetadataSeed.EndSessionEndpoint = $"{authority}/v2/logout?client_id={clientId}&returnTo={builder.HostEnvironment.BaseAddress}";
// Allowing only MyDomain.Com users
options.ProviderOptions.AdditionalProviderParameters.Add("hd", builder.Configuration["GoogleAuth:hd"]);
});
Note that I'm not 100% sure which exact parameter you should be adding.
"hd" is the domain hint parameter for google cloud based domains: https://developers.google.com/identity/openid-connect/openid-connect#hd-param
Based on this guide: https://learn.microsoft.com/en-us/azure/active-directory-b2c/direct-signin?pivots=b2c-user-flow - It looks like the Azure domain hint parameter is either login_hint or domain_hint
From this page, I founds that I can create a InteractiveRequestOptions object, run options.TryAddAdditionalParameter("domainHint", "mydomain.com");, and pass that into Navigation.NavigateToLogin, and it works great. Just be careful to use domainHint and not domain_hint, contrary to several pieces of documentation.

[OpenStack .NET API]: Is there a way to combine the Object creation and Container creation in one call?

Currently I do this
try
{
cloudFilesProvider.CreateObjectFromFile(inStrContainerName, inStrSrcFilePath, strDesFileName, 4096, FileMetaData);
}
catch (ItemNotFoundException ex1)
{
try
{
cloudFilesProvider.CreateContainer(inStrContainerName);
cloudFilesProvider.CreateObjectFromFile(inStrContainerName, inStrSrcFilePath, strDesFileName, 4096, FileMetaData);
}
catch(Exception ex2)
{
return false;
}
}
So essentially if the container does not exist then its 3 separate API calls.
Is there a more efficient way to do this?
You can simplify the code by reducing it to the following two lines.
cloudFilesProvider.CreateContainer(inStrContainerName);
cloudFilesProvider.CreateObjectFromFile(inStrContainerName, inStrSrcFilePath, strDesFileName, 4096, FileMetaData);
CreateContainer is safe to call for a container that already exists. It returns ContainerCreated if the container is created, or ContainerExists if the container was not created because it already exists.
PS: The return values (with information like the above) for the IObjectStorageProvider methods will be well documented for release 1.2.0.0.
Edit: To reduce the number of API calls your code makes, you could use a cache class like the following. The CreateIfNecessary method attempts to create the container only if it is not previously known to exist. The ClearCache method provides a manual method for continuing to use the cache even if you delete a container.
This code is currently untested.
public class ContainerManager
{
private readonly CloudFilesProvider _provider;
private readonly string _region;
private readonly bool _useInternalUrl;
private readonly CloudIdentity _identity;
private readonly HashSet<string> _containers = new HashSet<string>();
public ContainerManager(CloudFilesProvider provider, string region, bool useInternalUrl, CloudIdentity identity)
{
if (provider == null)
throw new ArgumentNullException("provider");
_provider = provider;
_region = region;
_useInternalUrl = useInternalUrl;
_identity = identity;
}
/// <summary>
/// Clears the cache of known containers.
/// </summary>
/// <remarks>
/// <alert class="warning">
/// If a container was deleted after this cache was in use, this method must be called or
/// <see cref="CreateIfNecessary(string)"/> could fail to create a container which does not
/// exist.
/// </alert>
/// </remarks>
public void ClearCache()
{
lock (_containers)
{
_containers.Clear();
}
}
/// <summary>
/// Ensures that a container exists in the Cloud Files store.
/// </summary>
/// <remarks>
/// <alert class="warning">
/// If a container was deleted after this cache was in use, and <see cref="ClearCache()"/>
/// has not been called, this method could fail to create a container which does not exist.
/// </alert>
/// </remarks>
/// <param name="container">The name of the container to create.</param>
/// <exception cref="ArgumentNullException">If <paramref name="container"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="container"/> is empty.</exception>
/// <returns><c>true</c> if the container was created; otherwise <c>false</c> if the container already existed.</returns>
public bool CreateIfNecessary(string container)
{
if (container == null)
throw new ArgumentNullException("container");
if (string.IsNullOrEmpty(container))
throw new ArgumentException("container cannot be empty");
// don't try to create the same container multiple times
if (_containers.Contains(container))
return false;
ObjectStore result = _provider.CreateContainer(container, _region, _useInternalUrl, _identity);
if (result == ObjectStore.ContainerCreated || result == ObjectStore.ContainerExists)
{
lock (_containers)
{
// add to _containers even if the result is ContainerExists, because that
// means it simply wasn't known to this cache.
_containers.Add(container);
}
}
// only return true if the container was actually created
return result == ObjectStore.ContainerCreated;
}
}

Revalidate Model When Using WebAPI (TryValidateModel equivalent)

Using vanilla MVC I can revalidate my model with TryValidateModel. The TryValidateModel method doesn't seem to be applicable to WebAPI. How can I revalidate my model when using WebAPI?
I know it has been a while since this has been asked, but the problem is still valid. Thus i thought i should share my solution to this problem.
I decided to implement the TryValidateModel(object model) myself, based on the implementation in the System.Web.Mvc.Controller.cs
The problem is that the mvc's TryValidateModel internally used their own HttpContext and ModelState. If you go and compaire the two, they are very similar....
The be able to use our own HttpContext there exists a HttpContextWrapper that can be used for that.
And Since we have to clear our model state, it doesn't really matter that we use a different type of ModelState , as long as we get the desired result, thus i create a new ModelState object from the correct type...
I did add the error to the ModelState of the controller and not to the model state to the newly created ModelState , This seems to work just fine for me :)
Here is my code, that i just added to the controller...
do not forget to import the library...
using System.Web.ModelBinding;
protected internal bool TryValidateModel(object model)
{
return TryValidateModel(model, null /* prefix */);
}
protected internal bool TryValidateModel(object model, string prefix)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
var t = new ModelBindingExecutionContext(new HttpContextWrapper(HttpContext.Current), new System.Web.ModelBinding.ModelStateDictionary());
foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(metadata, t).Validate(null))
{
ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
}
return ModelState.IsValid;
}
I don't know when was it added but now there is Validate method on api controller.
ApiController.Validate Method (TEntity)
https://msdn.microsoft.com/en-us/library/dn573258%28v=vs.118%29.aspx
Based from rik-vanmechelen original answer, here is my version that relies on the services container exposed by Web API.
/// <summary>
/// Tries to validate the model.
/// </summary>
/// <param name="model">The model.</param>
/// <returns>Whether the model is valid or not.</returns>
protected internal bool TryValidateModel(object model)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
var metadataProvider = Configuration.Services.GetService<System.Web.Http.Metadata.ModelMetadataProvider>();
var validatorProviders = Configuration.Services.GetServices<System.Web.Http.Validation.ModelValidatorProvider>();
var metadata = metadataProvider.GetMetadataForType(() => model, model.GetType());
ModelState.Clear();
var modelValidators = metadata.GetValidators(validatorProviders);
foreach (var validationResult in modelValidators.SelectMany(v => v.Validate(metadata, null)))
{
ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
}
return ModelState.IsValid;
}
This uses the following simple extension methods to access the services :
/// <summary>
/// Services container extension methods.
/// </summary>
public static class ServicesContainerExtensions
{
/// <summary>
/// Gets the service.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="services">The services.</param>
/// <returns>The service.</returns>
/// <exception cref="System.ArgumentNullException">services</exception>
public static TService GetService<TService>(this ServicesContainer services)
{
if (services == null)
{
throw new ArgumentNullException("services");
}
return (TService)((object)services.GetService(typeof(TService)));
}
/// <summary>
/// Gets the services.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="services">The services.</param>
/// <returns>The services.</returns>
/// <exception cref="System.ArgumentNullException">services</exception>
public static IEnumerable<TService> GetServices<TService>(this ServicesContainer services)
{
if (services == null)
{
throw new ArgumentNullException("services");
}
return services.GetServices(typeof(TService)).Cast<TService>();
}
}
The advantage of using this method is that it reuses the MetadataProvider and ValidatorProvider(s) you have configured for your Web API application while the previous answer is retrieving the one configured in ASP.NET MVC.
ASP.NET MVC and WebAPI run through different pipelines.
Turns out TryValidateModel is not supported in WebAPI. There's a feature request over on CodePlex.

ASP.NET MVC - Session is null

I have an MVC3 application on .net4 that its session working in the dev Environment, but not in the production.
In the production I logged the sessionID and the it is the same in the moment I Set and Get from the session.
When I try to get the session I am getting Null Exception.
This is how I access the session:
public static class HandlersHttpStorage
{
public static string TestSession
{
get
{
return HttpContext.Current.Session["time"];//This is null
}
set
{
HttpContext.Current.Session.Add("time", value);//DateTime.Now.ToString()
}
}
}
What's makes me worried is that the behavior in the production is different than the development, even though the web.config is the same.
Solution 1:
Link: HttpContext.Current.Session is null when routing requests
Got it. Quite stupid, actually. It worked after I removed & added the SessionStateModule like so:
<configuration>
...
<system.webServer>
...
<modules>
<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
...
</modules>
</system.webServer>
</configuration>
Simply adding it won't work since "Session" should have already been defined in the machine.config.
Now, I wonder if that is the usual thing to do. It surely doesn't seem so since it seems so crude...
Solution 2:
Link: HttpContext.Current.Session null item
sessionKey may be changing, you probably only need to do:
HttpContext.Current.Session["CurrentUser"]
Or the session may be expiring, check the timeout:
http://msdn.microsoft.com/en-us/library/h6bb9cz9(VS.71).aspx
Or you may be setting the session value from somewhere else, normally i control access to Session/Context object through one property
static readonly string SESSION_CurrentUser = "CurrentUser";
public static SiteUser Create() {
SiteUser.Current = new SiteUser();
return SiteUser.Current;
}
public static SiteUser Current {
get {
if (HttpContext.Current.Session == null || HttpContext.Current.Session[SESSION_CurrentUser] == null) {
throw new SiteUserAutorizationExeption();
}
return HttpContext.Current.Session[SESSION_CurrentUser] as SiteUser;
}
set {
if (!HttpContext.Current.Session == null) {
HttpContext.Current.Session[SESSION_CurrentUser] = value;
}
}
}
Another possible cause/solution is that IE doesn't save cookies if the domain name has an underscore (because strictly speaking domain names can't have underscores, so you'll probably only encounter this in development), e.g. http://my_dev_server/DoesntWork. Chrome or Firefox should work in this scenario, and if you change the domain name you're using to not have an underscore problem solved.
Ref:
http://blog.smartbear.com/software-quality/internet-explorer-eats-cookies-with-underscores-in-the-hostname/
http://social.msdn.microsoft.com/Forums/ie/en-US/8e876e9e-b223-4f84-a5d1-1eda2c2bbdf4/ie7-cookie-issue-when-domain-name-has-underscore-character-in-it?forum=iewebdevelopment
For me, I found that HttpContext.Current was null, so I created it:
System.Web.HttpContext c = System.Web.HttpContext.Current;
And I passed that into my function that was in my other class, like this:
string myString = "Something to save";
SessionExtensions.SetDataToSession<string>(c, "MyKey1", myString);
I had actually wanted my function to be a real extension method off of Session like the one below, but what I found was this HttpSessionStateBase session was null, it would give the NullReferenceException when I tried to add anything to Session using it. So this:
public static class SessionExtensions
{
/// <summary>
/// Get value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="session"></param>
/// <param name="key"></param>
/// <returns></returns>
public static T GetDataFromSession<T>(this HttpSessionStateBase session, string key)
{
return (T)session[key];
}
/// <summary>
/// Set value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="session"></param>
/// <param name="key"></param>
/// <param name="value"></param>
public static void SetDataToSession<T>(this HttpSessionStateBase session, string key, object value)
{
session[key] = value;
}
}
That Microsoft had here: https://code.msdn.microsoft.com/How-to-create-and-access-447ada98 became this, instead:
public static class SessionExtensions
{
/// <summary>
/// Get value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="session"></param>
/// <param name="key"></param>
/// <returns></returns>
public static T GetDataFromSession<T>(HttpContext context, string key)
{
if (context != null && context.Session != null)
{
context.Session.Abandon();
}
return (T)context.Session[key];
}
/// <summary>
/// Set value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="session"></param>
/// <param name="key"></param>
/// <param name="value"></param>
public static void SetDataToSession<T>(HttpContext context, string key, object value)
{
context.Session[key] = value;
}
}
And I was able to retrieve my data like this:
System.Web.HttpContext c = System.Web.HttpContext.Current;
string myString = SessionExtensions.GetDataFromSession<string>(c, "MyKey1");
And, of course, since HttpContext.Current and Session now exists, I was able to even simplify that to be:
string myString = Session["MyKey1"].ToString();
If this had been object, you would put the object's type in place of <string> in the SetDataToSession()
function:
List<string> myStringList = new List<string>();
myStringList.Add("Something to save");
SessionExtensions.SetDataToSession<List<string>>(c, "MyKey1", myStringList);
And to retrieve it:
System.Web.HttpContext c = System.Web.HttpContext.Current;
List<string> myStringList = SessionExtensions.GetDataFromSession<List<string>>(c, "MyKey1");
or simply:
List<string> myStringList = (List<string>)Session["MyKey1"];

what is the best way to implement a client side validation to check that the Max value is greater than the Min value inside my asp.net MVC

i have an object which contains Max value and Min value,, so i want inside all the edit and create views to display a client side validation message incase the user insert the Min value greater then the Min value.
BR
You will probably want both client-side and server-side validation. It might be good to implement your own Validation. I did a similar thing in which I wanted to make sure that one field was NOT the same value as another field. I didn't start from scratch though. I used some code from Simon J Ince of Microsoft. He has it here on his blog. Basically he has a foundation of Conditional Validation (where you validate based on the value of another field). I inherited from his ConditionalAttributeBase and implemented the IClientValidatable interface (this is how you send to the browser the dependent field the java script will check with.)
/// <summary>
/// A validator for ensuring that the value of this field does NOT equal the value of another field.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class NotEqual:ConditionalAttributeBase, IClientValidatable
{
public string DependentProperty { get; set; }
/// <summary>
/// Returns client validation rules for NotEqual
/// </summary>
/// <param name="metadata">The model metadata.</param>
/// <param name="context">The controller context.</param>
/// <returns>
/// The client validation rules for NotEqual.
/// </returns>
IEnumerable<ModelClientValidationRule> IClientValidatable.GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "notequal",
};
string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
rule.ValidationParameters.Add("dependentproperty", depProp);
yield return rule;
}
/// <summary>
/// Builds the dependent property id.
/// </summary>
/// <param name="metadata">The metadata.</param>
/// <param name="viewContext">The view context.</param>
/// <returns></returns>
protected string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
return QualifyFieldId(metadata, DependentProperty, viewContext);
}
/// <summary>
/// Validates that the value does not equal the value of the dependent value if the dependent value is not null
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">The context information about the validation operation.</param>
/// <returns>
/// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult"/> class.
/// </returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// check if the current value matches the target value
if (value != null && GetDependentFieldValue(DependentProperty, validationContext).ToString() == value.ToString())
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
/// <summary>
/// Initializes a new instance of the <see cref="NotEqual"/> class.
/// </summary>
/// <param name="dependentProperty">The dependent property.</param>
public NotEqual(string dependentProperty)
{
DependentProperty = dependentProperty;
}
}$
Next I tweaked the javascript:
(function ($) {
$.validator.addMethod('notequal',
function (value, element, parameters) {
var id = '#' + parameters['dependentproperty'];
var depControl = $(id);
var control = $(element);
if (control.val() === depControl.val())
return "";
return true;
}
);
$.validator.unobtrusive.adapters.add(
'notequal',
['dependentproperty'],
function (options) {
options.rules['notequal'] = {
dependentproperty: options.params['dependentproperty']
};
options.messages['notequal'] = options.message;
}
);
$.validator.addMethod('notequaltwo',
function (value, element, parameters) {
var id = '#' + parameters['dependentproperty'];
var depControl = $(id);
var control = $(element);
if (control.val() === depControl.val())
return "";
return true;
}
);
$.validator.unobtrusive.adapters.add(
'notequaltwo',
['dependentproperty'],
function (options) {
options.rules['notequaltwo'] = {
dependentproperty: options.params['dependentproperty']
};
options.messages['notequaltwo'] = options.message;
}
);
})(jQuery);$
Hopefully you can see how you can adapt my code to do what you want it do, basically you would have to convert the types (if the types don't convert, don't do anything and other validators that you have should pick up on it.) and then do a compare.

Resources