Value type field required in Razor View - asp.net-mvc-4

I have an enum type field called Title.
[Serializable]
public enum Title
{
NotSet,
Miss = 4,
Mr = 1,
Mrs = 3,
Ms = 2
}
I want to bind a property of type Title to the Razor View but I don't want it to be a required field. However, on tabbing out or OnBlur, it is showing as required, although I have not specified this as required.
Is there any way I can get around this?

create
namespace YourApplicationName.Helper
{
public class ModelValueListProvider : IEnumerable<SelectListItem>
{
List<KeyValuePair<string, string>> innerList = new List<KeyValuePair<string, string>>();
public static readonly ModelValueListProvider TitleList = new TitleListProvider();
protected void Add(string value, string text)
{
string innerValue = null, innerText = null;
if (value != null)
innerValue = value.ToString();
if (text != null)
innerText = text.ToString();
if (innerList.Exists(kvp => kvp.Key == innerValue))
throw new ArgumentException("Value must be unique", "value");
innerList.Add(new KeyValuePair<string, string>(innerValue, innerText));
}
public IEnumerator<SelectListItem> GetEnumerator()
{
return new ModelValueListProviderEnumerator(innerList.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private struct ModelValueListProviderEnumerator : IEnumerator<SelectListItem>
{
private IEnumerator<KeyValuePair<string, string>> innerEnumerator;
public ModelValueListProviderEnumerator(IEnumerator<KeyValuePair<string, string>> enumerator)
{
innerEnumerator = enumerator;
}
public SelectListItem Current
{
get
{
var current = innerEnumerator.Current;
return new SelectListItem { Value = current.Key, Text = current.Value };
}
}
public void Dispose()
{
try
{
innerEnumerator.Dispose();
}
catch (Exception)
{
}
}
object System.Collections.IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
return innerEnumerator.MoveNext();
}
public void Reset()
{
innerEnumerator.Reset();
}
}
private class TitleListProvider : ModelValueListProvider
{
public TitleListProvider (string defaultText = null)
{
if (!string.IsNullOrEmpty(defaultText))
Add(string.Empty, defaultText);
Add(Title.NotSet, "NotSet");
Add(Title.Miss , "Miss");
Add(Title.Mr , "Mr");
Add(Title.Mrs , "Mrs");
Add(Title.MS, "MS");
}
public void Add(Title value, string text)
{
Add(value.ToString("d"), text);
}
}
}
}
in your model
public Title? Titleformation { get; set; }
public string[] SelectedTitle { get; set; }
in your view, also add the name space to your view
#using YourApplicationName.Helper;
#Html.ListBoxFor(m => m.SelectedTitle , new SelectList(ModelValueListProvider.TitleList, "Value", "Text"))
hope this help you

Enums require values, and cannot be null (aka not set) despite what someone commented above. What I do for salutations is have a "none" member of the enum, and whenever I print this out, I just check in the code to see if the value of the enum is > 0 (aka, the none option) and don't print it.
public enum Salutation { none,
[Description("Mr.")] Mr,
[Description("Mrs.")] Mrs,
[Description("Ms.")]Ms,
[Description("Miss")] Miss }

Use a class rather than enum ie:
public class Title
{
NotSet;
Miss = 4;
Mr = 1;
Mrs = 3;
Ms = 2;
}

Related

Deserialize enums using the EnumMember value in AspNet [FromQuery] model binding

I have an endpoint in .NET 6 Microsoft.NET.Sdk.Web project that deserialize query strings into a .NET object by using the standard [FromQuery]
[Route("[controller]")]
public class SamplesController
: ControllerBase
{
[HttpGet]
public IActionResult Get([FromQuery]QueryModel queryModel)
{
if (!queryModel.Status.HasValue)
{
return BadRequest("Problem in deserialization");
}
return Ok(queryModel.Status.Value.GetEnumDisplayName());
}
}
The model contains an enum
public class QueryModel
{
/// <summary>
/// The foo parameter
/// </summary>
/// <example>bar</example>
public string? Foo { get; init; } = null;
/// <summary>
/// The status
/// </summary>
/// <example>on-hold</example>
public Status? Status { get; set; } = null;
}
And the enum has EnumMember attributes which value I want to use to deserialize from.
public enum Status
{
[EnumMember(Value = "open")]
Open,
[EnumMember(Value = "on-hold")]
OnHold
}
By default, .NET 6 does not take into consideration the EnumMember when deserializing.
The goal is to be able to send requests such as
http://localhost:5000/Samples?Foo=bar&Status=on-hold
and have the controller's action deserialize the QueryModel with the proper Status.OnHold value by using its EnumMember
I have tried without luck an extensions library that contains a converter, but the converter is not getting triggered when using [FromQuery]. See https://github.com/Macross-Software/core/issues/30
I have added a project to reproduce problem and as a sandbox to provide a solution**
https://gitlab.com/sunnyatticsoftware/issues/string-to-enum-mvc/-/tree/feature/1-original-problem
NOTE: I would need a solution where the Enum and the does not require any external dependency (just .NET sdk).
A custom Enum converter might be your choice. By leveraging the existing EnumConverter class what we need is to have a customized ConvertFrom method:
public class CustomEnumConverter : EnumConverter
{
public CustomEnumConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] Type type) : base(type)
{
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string strValue)
{
try
{
foreach (var name in Enum.GetNames(EnumType))
{
var field = EnumType.GetField(name);
if (field != null)
{
var enumMember = (EnumMemberAttribute)(field.GetCustomAttributes(typeof(EnumMemberAttribute), true).Single());
if (strValue.Equals(enumMember.Value, StringComparison.OrdinalIgnoreCase))
{
return Enum.Parse(EnumType, name, true);
}
}
}
}
catch (Exception e)
{
throw new FormatException((string)value, e);
}
}
return base.ConvertFrom(context, culture, value);
}
}
And then decorate the converter to your Model class:
[TypeConverter(typeof(CustomEnumConverter))]
public enum Status
{
[EnumMember(Value = "open")]
Open,
[EnumMember(Value = "on-hold")]
OnHold
}
then we can get the "on-hold" parsed. You might also want to override the ConverTo() for printing the EnumMember value to swagger. It is a bit hacky, but if you want a pure .NET solution this should be one of the minimal viable solutions.
Following the documentation guide Custom Model Binding in ASP.NET Core, you can create your own versions of Microsoft's classes EnumTypeModelBinderProvider, EnumTypeModelBinder (and base class SimpleTypeModelBinder) that replace incoming enum value names that have been renamed via EnumMemberAttribute with the original enum names before binding:
// Begin code for enum model binding
public class EnumMemberEnumTypeModelBinderProvider : IModelBinderProvider
{
public EnumMemberEnumTypeModelBinderProvider(MvcOptions options) { }
public IModelBinder? GetBinder(ModelBinderProviderContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Metadata.IsEnum)
{
var enumType = context.Metadata.UnderlyingOrModelType;
Debug.Assert(enumType.IsEnum);
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
if (EnumExtensions.TryGetEnumMemberOverridesToOriginals(enumType, out var overridesToOriginals))
return new EnumMemberEnumTypeModelBinder(suppressBindingUndefinedValueToEnumType: true, enumType, loggerFactory, overridesToOriginals);
}
return null;
}
}
public class EnumMemberEnumTypeModelBinder : ExtensibleSimpleTypeModelBinder
{
// Adapted from https://github.com/dotnet/aspnetcore/blob/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Mvc/Mvc.Core/src/ModelBinding/Binders/EnumTypeModelBinder.cs#L58
readonly Type enumType;
readonly bool isFlagged;
readonly Dictionary<ReadOnlyMemory<char>, string> overridesToOriginals;
readonly TypeConverter typeConverter;
public EnumMemberEnumTypeModelBinder(bool suppressBindingUndefinedValueToEnumType, Type modelType, ILoggerFactory loggerFactory, Dictionary<ReadOnlyMemory<char>, string> overridesToOriginals) : base(modelType, loggerFactory)
{
this.enumType = Nullable.GetUnderlyingType(modelType) ?? modelType;
if (!this.enumType.IsEnum)
throw new ArgumentException();
this.isFlagged = Attribute.IsDefined(enumType, typeof(FlagsAttribute));
this.overridesToOriginals = overridesToOriginals ?? throw new ArgumentNullException(nameof(overridesToOriginals));
this.typeConverter = TypeDescriptor.GetConverter(this.enumType);
}
protected override string? GetValueFromBindingContext(ValueProviderResult valueProviderResult) =>
EnumExtensions.ReplaceRenamedEnumValuesToOriginals(base.GetValueFromBindingContext(valueProviderResult), isFlagged, overridesToOriginals);
protected override void CheckModel(ModelBindingContext bindingContext, ValueProviderResult valueProviderResult, object? model)
{
if (model == null)
{
base.CheckModel(bindingContext, valueProviderResult, model);
}
else if (IsDefinedInEnum(model, bindingContext))
{
bindingContext.Result = ModelBindingResult.Success(model);
}
else
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(
valueProviderResult.ToString()));
}
}
private bool IsDefinedInEnum(object model, ModelBindingContext bindingContext)
{
// Adapted from https://github.com/dotnet/aspnetcore/blob/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Mvc/Mvc.Core/src/ModelBinding/Binders/EnumTypeModelBinder.cs#L58
var modelType = bindingContext.ModelMetadata.UnderlyingOrModelType;
// Check if the converted value is indeed defined on the enum as EnumTypeConverter
// converts value to the backing type (ex: integer) and does not check if the value is defined on the enum.
if (bindingContext.ModelMetadata.IsFlagsEnum)
{
var underlying = Convert.ChangeType(
model,
Enum.GetUnderlyingType(modelType),
CultureInfo.InvariantCulture).ToString();
var converted = model.ToString();
return !string.Equals(underlying, converted, StringComparison.OrdinalIgnoreCase);
}
return Enum.IsDefined(modelType, model);
}
}
public class ExtensibleSimpleTypeModelBinder : IModelBinder
{
// Adapted from https://github.com/dotnet/aspnetcore/blob/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Mvc/Mvc.Core/src/ModelBinding/Binders/SimpleTypeModelBinder.cs
private readonly TypeConverter _typeConverter;
private readonly ILogger _logger;
public ExtensibleSimpleTypeModelBinder(Type type, ILoggerFactory loggerFactory) : this(type, loggerFactory, null) { }
public ExtensibleSimpleTypeModelBinder(Type type, ILoggerFactory loggerFactory, TypeConverter? typeConverter)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (loggerFactory == null)
throw new ArgumentNullException(nameof(loggerFactory));
_typeConverter = typeConverter ?? TypeDescriptor.GetConverter(type);
_logger = loggerFactory.CreateLogger<ExtensibleSimpleTypeModelBinder>();
}
protected virtual string? GetValueFromBindingContext(ValueProviderResult valueProviderResult) => valueProviderResult.FirstValue;
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
//_logger.AttemptingToBindModel(bindingContext);
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None)
{
//_logger.FoundNoValueInRequest(bindingContext);
// no entry
//_logger.DoneAttemptingToBindModel(bindingContext);
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
try
{
var value = GetValueFromBindingContext(valueProviderResult);
object? model;
if (bindingContext.ModelType == typeof(string))
{
// Already have a string. No further conversion required but handle ConvertEmptyStringToNull.
if (bindingContext.ModelMetadata.ConvertEmptyStringToNull && string.IsNullOrWhiteSpace(value))
model = null;
else
model = value;
}
else if (string.IsNullOrWhiteSpace(value))
{
// Other than the StringConverter, converters Trim() the value then throw if the result is empty.
model = null;
}
else
{
model = _typeConverter.ConvertFrom(context: null,culture: valueProviderResult.Culture, value: value);
}
CheckModel(bindingContext, valueProviderResult, model);
//_logger.DoneAttemptingToBindModel(bindingContext);
return Task.CompletedTask;
}
catch (Exception exception)
{
var isFormatException = exception is FormatException;
if (!isFormatException && exception.InnerException != null)
{
// TypeConverter throws System.Exception wrapping the FormatException,
// so we capture the inner exception.
exception = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(exception.InnerException).SourceException;
}
bindingContext.ModelState.TryAddModelError(bindingContext.ModelName,exception, bindingContext.ModelMetadata);
// Were able to find a converter for the type but conversion failed.
return Task.CompletedTask;
}
}
/// <inheritdoc/>
protected virtual void CheckModel(
ModelBindingContext bindingContext,
ValueProviderResult valueProviderResult,
object? model)
{
// When converting newModel a null value may indicate a failed conversion for an otherwise required
// model (can't set a ValueType to null). This detects if a null model value is acceptable given the
// current bindingContext. If not, an error is logged.
if (model == null && !bindingContext.ModelMetadata.IsReferenceOrNullableType)
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
valueProviderResult.ToString()));
}
else
{
bindingContext.Result = ModelBindingResult.Success(model);
}
}
}
// End code for enum model binding
/********************************************************/
// Begin general enum parsing code
public class CharMemoryComparer : IEqualityComparer<ReadOnlyMemory<char>>
{
public static CharMemoryComparer OrdinalIgnoreCase { get; } = new CharMemoryComparer(StringComparison.OrdinalIgnoreCase);
public static CharMemoryComparer Ordinal { get; } = new CharMemoryComparer(StringComparison.Ordinal);
readonly StringComparison comparison;
CharMemoryComparer(StringComparison comparison) => this.comparison = comparison;
public bool Equals(ReadOnlyMemory<char> x, ReadOnlyMemory<char> y) => MemoryExtensions.Equals(x.Span, y.Span, comparison);
public int GetHashCode(ReadOnlyMemory<char> obj) => String.GetHashCode(obj.Span, comparison);
}
public static partial class EnumExtensions
{
public const char FlagSeparatorChar = ',';
public const string FlagSeparatorString = ", ";
public static bool TryGetEnumMemberOverridesToOriginals(Type enumType, [System.Diagnostics.CodeAnalysis.NotNullWhen(returnValue: true)] out Dictionary<ReadOnlyMemory<char>, string>? overridesToOriginals)
{
if (enumType == null)
throw new ArgumentNullException(nameof(enumType));
if (!enumType.IsEnum)
throw new ArgumentException(nameof(enumType));
overridesToOriginals = null;
foreach (var name in Enum.GetNames(enumType))
{
if (TryGetEnumAttribute<EnumMemberAttribute>(enumType, name, out var attr) && !string.IsNullOrWhiteSpace(attr.Value))
{
overridesToOriginals = overridesToOriginals ?? new(CharMemoryComparer.OrdinalIgnoreCase);
overridesToOriginals.Add(attr.Value.AsMemory(), name);
}
}
return overridesToOriginals != null;
}
public static bool TryGetEnumAttribute<TAttribute>(Type type, string name, [System.Diagnostics.CodeAnalysis.NotNullWhen(returnValue: true)] out TAttribute? attribute) where TAttribute : System.Attribute
{
var member = type.GetMember(name).SingleOrDefault();
attribute = member?.GetCustomAttribute<TAttribute>(false);
return attribute != null;
}
public static string? ReplaceRenamedEnumValuesToOriginals(string? value, bool isFlagged, Dictionary<ReadOnlyMemory<char>, string> overridesToOriginals)
{
if (string.IsNullOrWhiteSpace(value))
return value;
var trimmed = value.AsMemory().Trim();
if (overridesToOriginals.TryGetValue(trimmed, out var #override))
value = #override;
else if (isFlagged && trimmed.Length > 0)
{
var sb = new StringBuilder();
bool replaced = false;
foreach (var n in trimmed.Split(EnumExtensions.FlagSeparatorChar, StringSplitOptions.TrimEntries))
{
ReadOnlySpan<char> toAppend;
if (overridesToOriginals.TryGetValue(n, out var #thisOverride))
{
toAppend = thisOverride.AsSpan();
replaced = true;
}
else
toAppend = n.Span;
sb.Append(sb.Length == 0 ? null : EnumExtensions.FlagSeparatorString).Append(toAppend);
}
if (replaced)
value = sb.ToString();
}
return value;
}
}
public static class StringExtensions
{
public static IEnumerable<ReadOnlyMemory<char>> Split(this ReadOnlyMemory<char> chars, char separator, StringSplitOptions options = StringSplitOptions.None)
{
int index;
while ((index = chars.Span.IndexOf(separator)) >= 0)
{
var slice = chars.Slice(0, index);
if ((options & StringSplitOptions.TrimEntries) == StringSplitOptions.TrimEntries)
slice = slice.Trim();
if ((options & StringSplitOptions.RemoveEmptyEntries) == 0 || slice.Length > 0)
yield return slice;
chars = chars.Slice(index + 1);
}
if ((options & StringSplitOptions.TrimEntries) == StringSplitOptions.TrimEntries)
chars = chars.Trim();
if ((options & StringSplitOptions.RemoveEmptyEntries) == 0 || chars.Length > 0)
yield return chars;
}
}
Then add the binder in ConfigureServices() like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new EnumMemberEnumTypeModelBinderProvider(options));
});
}
Notes:
EnumTypeModelBinder and base class SimpleTypeModelBinder provide no useful extension points to customize the parsing of the incoming value string, thus it was necessary to copy some of their logic.
Precisely emulating the logic of SimpleTypeModelBinder is somewhat difficult because it supports both numeric and textual enum values -- including mixtures of both for flags enums. The binder above retains that capability, but at a cost of also allowing original enum names to be bound successfully. Thus the values on-hold and onhold will be bound to Status.OnHold.
Conversely, if you do not want to support binding of numeric values for enums, you could adapt the code of JsonEnumMemberStringEnumConverter from this answer to System.Text.Json: How do I specify a custom name for an enum value?. Demo fiddle here. This approach also avoids binding to the original, unrenamed enum names.
Matching of override names with original enum names is case-insensitive, so override names that differ only in case are not supported.

Xunit Parameterize Selenium By Type

I have asked several basic questions related to this in the past and got great answers that explained several issues. I think i'm now in a position to ask the correct question now that I'm more aware of how Xunit works!
I am trying to parametrize several tests in C# using visual studio. I need each parameter to be displayed as an individual test that can be ran in isolation if required (I know there is a test collection runner and a separate test runner). The test collection runner is my issue.
I know that Xunit requires the parameters to be serialized in order for them to be picked up by the test collection runner. I also know that it by default can easily serialize basic data types like string, bool, int etc.
I have tried various approaches to do this with mixed results. My issue is trying to parameterize the Selenium type 'By'. I can't seem to be able to serialize this. I've tried to trick Xunit for example by using a dictionary List<string, By> and trying to serialize the in the dictionary (no luck!)
Here is the cleanest code I have come across that is simple and elegant for what i'm trying to do, but again I can't serialize the 'By' type. I have played around with changing the static property from bool to By and it returns only 1 test for all params, so it's not being serialized
public class ParamTest1
{
static string test3 = "TestXYZ";
public static TheoryData<int, bool, string, string> DataForTest1 = new TheoryData<int, bool, string, string>
{
{ 1, true, "First", test3 },
{ 2, false, "Second", test3},
{ 3, true, "Third", test3}
};
[Theory(DisplayName = "My First Test"), MemberData(nameof(DataForTest1))]
public void Test1(int valA, bool valB, string valC, string valD)
{
Assert.True(valB);
}
}
Which gives me
I am aware this particular code isn't invoking the IXunitSerializable
So here is an another working example of what I need but I just can't get it to work with the 'By' Type
public class ValidateTestCase : IXunitSerializable
{
public Guid Coupon { get; set; }
public bool IsValid { get; set; }
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(Coupon), Coupon.ToString());
}
public void Deserialize(IXunitSerializationInfo info) { }
}
public class Testing
{
public static IEnumerable<object[]> ValidateTestCases
{
get
{
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("73e4d185-70cf-4ce4-bc3f-187b7a40e167"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("93b983fb-5b6a-4845-a769-db41900b7df9"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("99c03283-33cb-4e56-a010-c2bc0758ad27"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("16a7fe80-3111-44b0-9ebf-c7159bea637d"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("8b38b4aa-d70f-4ce7-8992-8a60936c5c58"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("abc60aa0-a33b-4057-8f99-5cdceda35c70"), IsValid = true } };
}
}
[Theory(DisplayName = "CouponService should validate coupons")]
[MemberData(nameof(ValidateTestCases))]
public void MyCouponService_Validates(ValidateTestCase vtc)
{
Assert.Equal(vtc.IsValid, true);
}
}
And finally for anyone wondering what the 'By' type is I am referring to it's :
[![enter image description here][2]][2]
Here it is in the debugger so you can see what's going on inside:
[![enter image description here][3]][3]
I know there's a lot going on in there but if anyone has any ideas or suggestions it would be great!
To summarize, I can't parameterize the Selenium 'data type' By.
[2]: https://i.stack.imgur.com/XcLcn.png
[3]: https://i.stack.imgur.com/T9so1.png
Serializing Class
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using OpenQA.Selenium;
using Xunit.Abstractions;
namespace XUnitTestProject1
{
public class ParameterizedHook
{
public By p1 { get; set; }
public By p2 { get; set; }
public string assertion { get; set; }
}
public class TheoryWrapper<T> : IXunitSerializable
{
public TheoryWrapper(string label, T #object)
{
Name = label;
Object = #object;
}
public TheoryWrapper()
{
}
public string Name { get; set; }
public T Object { get; set; }
public void Deserialize(IXunitSerializationInfo info)
{
Name = info.GetValue<string>("Label");
Object = JsonConvert.DeserializeObject<T>(info.GetValue<string>("objValue"));
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue("Label", Name, typeof(string));
var json = JsonConvert.SerializeObject(Object);
info.AddValue("objValue", json);
}
public override string ToString()
{
return Name;
}
}
}
Working Test
public static IEnumerable<object[]> ComplexTheoryData
{
get
{
return new List<object[]>
{
new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { p1 = nav_hold.pip_builder.dash_expand_pip_builder_menu, p2 = nav_hold.pip_builder.dash_project_stages, assertion = "WORK STAGE" })},
new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { p1 = nav_hold.pip_builder.dash_expand_pip_builder_menu, p2 = nav_hold.pip_builder.dash_project_stages, assertion = "WORK STAGES" })},
//new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { TestData = b.login })},
//new object[] {1, new TheoryWrapper<ParameterizedHook>("Pass1", new ParameterizedHook { TestData = b.password })}
};
}
}
[SkippableTheory]
[Trait("xUnit", "ForTestRunner")]
[MemberData(nameof(ComplexTheoryData))]
public void Test_Navigation(int id, TheoryWrapper<ParameterizedHook> test)
{
nav_met.NavMethodTest(test.Object.p1, test.Object.p2);
By page_title = By.Id("ctl00_lblPageTitle");
Assert.True(nav_met.VerifyText(page_title, test.Object.assertion));
}

How might I construct a non-default constructor model from a query string in ASP.Net Core?

I would like to construct an object that has a non-default constructor from query string parameters in ASP.Net Core. In essence, I have two models that have a common ancestor class with different parameterizations. Based on some conditions in an API endpoint, one model will be constructed with parameters from the query string.
[HttpGet("{model}")]
public ModelBase Get(string model)
{
switch (model)
{
case "foo":
ModelFoo foo = GetModelFromQueryString<ModelFoo>();
return foo;
case "bar":
ModelBar bar = GetModelFromQueryString<ModelBar>();
return bar;
}
return null;
}
GetModelFromQueryString<TModel> is obviously the magical function that I wish I knew existed. If it already exists or someone could help provide implementation details, that would answer my question.
The example model classes would be like the following:
class ModelFoo : ModelBase
{
public ModelFoo(int param1=1, int param2=2)
{
// ...
}
}
class ModelBar : ModelBase
{
public ModelBar(int paramBaz=3)
{
// ...
}
}
This would ideally make the following HTTP calls yield the desired results:
GET api/foo?param1=7&param2=9 yields new ModelFoo(param1:7, param2:9).
GET api/bar?paramBaz=42 yields new ModelBar(paramBaz:42).
GET api/foo yields new ModelFoo(param1:1, param2:2).
GET api/foo?param2=11 yields new ModelFoo(param1:1, param2:11).
How might I go about this? Should I restructure entirely?
I realize that this may be a bit of a complicated, multi-faceted question so any and all help is much appreciated!
You can try to use my working demo.
Class:
public class ModelBase
{
}
class ModelFoo : ModelBase
{
public int param1 { get; set; }
public int param2 { get; set; }
public ModelFoo(int param1 = 1, int param2 = 2)
{
this.param1 = param1;
this.param2 = param2;
}
}
class ModelBar : ModelBase
{
public int paramBaz { get; set; }
public ModelBar(int paramBaz = 3)
{
this.paramBaz = paramBaz;
}
}
Action:
[HttpGet("{model}")]
public ModelBase Get(string model,int param1,int param2,int paramBaz)
{
switch (model)
{
case "foo":
if(param1!=0 ^ param2!=0)
{
if(param1 != 0)
{
ModelFoo foo1 = new ModelFoo
{
param1 = param1,
};
return foo1;
}
if (param2 != 0)
{
ModelFoo foo2 = new ModelFoo
{
param2 = param2,
};
return foo2;
}
}
if (param1 == 0 && param2 == 0)
{
ModelFoo foo3 = new ModelFoo();
return foo3;
}
ModelFoo foo4 = new ModelFoo
{
param1 = param1,
param2 = param2,
};
return foo4;
case "bar":
if (paramBaz != 0)
{
ModelBar bar = new ModelBar
{
paramBaz = paramBaz,
};
return bar;
}
ModelBar bar1 = new ModelBar();
return bar1;
}
return null;
}
Not really a direct answer to your question, but the needed building blocks are already available.
Newtonsoft JSON.net supports creating types if the constructor parameter matches the property names and also supports creating instances of an interface if the concrete type is set within a $type property:
public static class Program
{
public static async Task<int> Main(string[] args)
{
var sourceList = new List<IModel> { new ModelFoo(3, 7), new ModelBar(11) };
var jsonList = JsonConvert.SerializeObject(sourceList, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var list = JsonConvert.DeserializeObject<List<IModel>>(jsonList, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
return 0;
}
}
public interface IModel { }
public class ModelFoo : IModel
{
public ModelFoo(int first, int second)
{
First = first;
Second = second;
}
public int First { get; }
public int Second { get; }
}
public class ModelBar : IModel
{
public ModelBar(int third)
{
Third = third;
}
public int Third { get; }
}

Phone number tabbing in Text field using Jface

I am stuck in one of the issue. Suppose i have a phone number field 010-9999-9999, this number should split in 3 text field. I tried to do this but i get only prefix number i.e 010 in all three text field. I am using jface databinding.
I created Model as
class A{
String phoneNo;
}
Jface Databinding:
IObservableValue ssn1TextTextObserveWidget = SWTObservables.observeText(text_ph11, SWT.Modify);
IObservableValue ssn2TextTextObserveWidget = SWTObservables.observeText(text_ph2, SWT.Modify);
IObservableValue ssn2TextTextObserveWidget1 = SWTObservables.observeText(text_ph3, SWT.Modify);
IObservableValue simpleTableViewerSSN1ObserveDetailValue = BeansObservables.observeDetailValue(simpleTableViewerSelectionObserveSelection_employee, "phoneNo", String.class);
IObservableValue simpleTableViewerSSN2ObserveDetailValue = BeansObservables.observeDetailValue(simpleTableViewerSelectionObserveSelection_employee, "phoneNo", String.class);
IObservableValue simpleTableViewerSSN2ObserveDetailValue = BeansObservables.observeDetailValue(simpleTableViewerSelectionObserveSelection_employee, "phoneNo", String.class);
Databinding
bindingContext.bindValue(simpleTableViewerSSN1ObserveDetailValue, ssn1TextTextObserveWidget, null, null);
bindingContext.bindValue(simpleTableViewerSSN2ObserveDetailValue, ssn2TextTextObserveWidget, null, null);
bindingContext.bindValue(simpleTableViewerSSN2ObserveDetailValue, ssn2TextTextObserveWidget1, null, null);
When i try to do this value only prefix get populated in all three fields i.e 010-010-010. Please help me out to resolve this issue.
If you want your phone number to be divided into three different widgets you will probably need to save the phone number in three different attributes, each assigned a different part of the phone number.
There might be some way to bind a 'part' of a string value to a widget, maybe through getter methods, but for that wait and let someone more knowledgeable in this area give an answer for you.
Here is sample code that can be modified to work with your case. IConverter is used with UpdateValueStrategy to modify the value that will be set/get model to/from target text feild.
//GUI class
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(3, false));
Label lbl = new Label(shell, SWT.NONE);
lbl.setText("Name");
final Text firstName = new Text(shell, SWT.BORDER);
final Text lastName = new Text(shell, SWT.BORDER);
final Name name = new Name();
SWTObservables.getRealm(display).exec(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Realm realm = SWTObservables.getRealm(display);
DataBindingContext cxt = new DataBindingContext(realm);
ISWTObservableValue firstNameObservable = SWTObservables.observeText(firstName, new int[] { SWT.Modify });
ISWTObservableValue lastNameObservable = SWTObservables.observeText(lastName, new int[] { SWT.Modify });
final IObservableValue nameObservable = BeansObservables.observeValue(realm, name, "name");
UpdateValueStrategy firstNameTxtToName = new UpdateValueStrategy();
firstNameTxtToName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
String val = fromObject.toString();
Object beanVal = nameObservable.getValue();
if (beanVal != null) {
String beanString = beanVal.toString();
int i = beanString.indexOf(',');
if (i != -1) {
return val + beanString.substring(i);
}
}
return val;
}
});
UpdateValueStrategy nameToFirstName = new UpdateValueStrategy();
nameToFirstName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
if (fromObject != null) {
Object objVal = nameObservable.getValue();
if (objVal != null) {
String val = objVal.toString();
int i = val.indexOf(',');
if (i != -1) {
return val.substring(0, i);
} else {
return val;
}
}
}
return "";
}
});
UpdateValueStrategy lastNameTxtToName = new UpdateValueStrategy();
lastNameTxtToName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
String val = fromObject.toString();
Object beanVal = nameObservable.getValue();
if (beanVal != null) {
String beanString = beanVal.toString();
int i = beanString.indexOf(',');
String fName = beanString;
if (i != -1) {
fName = beanString.substring(0, i + 1);
} else {
fName = fName + ",";
}
val = fName + val;
}
return val;
}
});
UpdateValueStrategy nameToLastName = new UpdateValueStrategy();
nameToLastName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
if (fromObject != null) {
String val = fromObject.toString();
int i = val.indexOf(',');
if (i != -1) {
return val.substring(i + 1);
}
}
return "";
}
});
cxt.bindValue(firstNameObservable, nameObservable, firstNameTxtToName, nameToFirstName);
cxt.bindValue(lastNameObservable, nameObservable, lastNameTxtToName, nameToLastName);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
//POJO
public class Name {
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(propertyName, listener);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void firePropertyChangeEvent(PropertyChangeEvent evt) {
changeSupport.firePropertyChange(evt);
}
String name;
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
System.out.println(name);
}
}

Serializing object graph using MongoDB Bson serializer

I've been playing a little with the MongoDB Bson serializer, using the following piece of code:
class Program
{
public class myValue
{
public int Id = 0;
public string Label = "";
}
public class myValueMap : Dictionary<string, myValue>
{
}
public class myProdData
{
public myValueMap Mapping { get; set; }
}
public class mySystemPosition
{
public string Text { get; set; }
public myProdData ProdData { get; set; }
}
static void Main(string[] args)
{
BsonClassMap.RegisterClassMap<mySystemPosition>();
BsonClassMap.RegisterClassMap<myProdData>();
BsonClassMap.RegisterClassMap<myValueMap>();
BsonClassMap.RegisterClassMap<myValue>();
var o = new mySystemPosition()
{
ProdData = new myProdData()
{
Mapping = new myValueMap()
{
{"123", new myValue() {Id = 1, Label = "Item1"}},
{"345", new myValue() {Id = 2, Label = "Item2"}},
}
}
};
var bson = o.ToBson();
var text = Encoding.ASCII.GetString(bson);
}
}
however I don't seem to be able to get the myProdData.Mapping serialized....
Do I need to configure the MongoDB Bson serializer in a special way, to make this work?
You no need to use BsonClassMap.RegisterClassMap if you no need custom serializtion(documentation).
All your classes will be desirialzied according to default rules.
Also i am changed your example a little bit to get it work(i've replaces myValueMap class with Dictionary):
public class myProdData
{
public Dictionary<string, myValue> Mapping { get; set; }
}
static void Main(string[] args)
{
var o = new mySystemPosition()
{
ProdData = new myProdData()
{
Mapping = new Dictionary<string, myValue>()
{
{"123", new myValue() {Id = 1, Label = "Item1"}},
{"345", new myValue() {Id = 2, Label = "Item2"}},
}
}
};
var json = o.ToJson();
Console.WriteLine(json);
Console.ReadKey();
}
Here is console output(just well formatted):
{
"Text":null,
"ProdData":{
"Mapping":{
"123":{
"_id":1,
"Label":"Item1"
},
"345":{
"_id":2,
"Label":"Item2"
}
}
}
}
You can test your serializtion using ToJson() extention method, in order to view that all correct and after that use ToBson() if need.
The problem is that myValueMap derives from Dictionary. That results in a class that the AutoMap method can't handle.
I recommend you just use the Dictionary directly, as Andrew did in his reply.
Ufortunately the myValueMap is an object that I can't easily change, however it turns out, that's pretty easy to create your own (de)serializer....
public class myValueMapSerializer : IBsonSerializer
{
public object Deserialize(Bson.IO.BsonReader bsonReader, System.Type nominalType, System.Type actualType, IBsonSerializationOptions options)
{
if (nominalType != typeof(myValueMap)) throw new ArgumentException("Cannot serialize anything but myValueMap");
var res = new myValueMap();
var ser = new DictionarySerializer<string, myValue>();
var dic = (Dictionary<string, myValue>)ser.Deserialize(bsonReader, typeof(Dictionary<string, myValue>), options);
foreach (var item in dic)
{
res.Add(item.Key, item.Value);
}
return res;
}
public object Deserialize(Bson.IO.BsonReader bsonReader, System.Type nominalType, IBsonSerializationOptions options)
{
throw new Exception("Not implemented");
}
public bool GetDocumentId(object document, out object id, out IIdGenerator idGenerator)
{
id = null;
idGenerator = null;
return false;
}
public void Serialize(Bson.IO.BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
if (nominalType != typeof(myValueMap)) throw new ArgumentException("Cannot serialize anything but myValueMap");
var ser = new DictionarySerializer<string, myValue>();
ser.Serialize(bsonWriter, typeof(DictionarySerializer<string, myValue>), value, options);
}
public void SetDocumentId(object document, object id)
{
return;
}
}