Why isn't AutoFixture working with the StringLength data annotation? - data-annotations

I'm trying again to upgrade to AutoFixture 2, and I'm running into a problem with the data annotations on my objects. Here's an example object:
public class Bleh
{
[StringLength(255)]
public string Foo { get; set; }
public string Bar { get; set; }
}
I'm attempting to create an anonymous Bleh, but the property with the annotation is coming up empty rather than being populated with an anonymous string.
[Test]
public void GetAll_HasContacts()
{
var fix = new Fixture();
var bleh = fix.CreateAnonymous<Bleh>();
Assert.That(bleh.Bar, Is.Not.Empty); // pass
Assert.That(bleh.Foo, Is.Not.Empty); // fail ?!
}
According to Bonus Bits, StringLength should be supported as of 2.4.0, though even if it wasn't supported I wouldn't expect an empty string. I am using v2.7.1 from NuGet. Have I missed some sort of customization or behavior that is necessary to create data-annotated objects?

Thanks for reporting this!
This behavior is by design (the reason for that is, basically, the description of the attribute itself).
By applying [StringLength(255)] on a data field it basically means that it is allowed to have 0 up-to 255 characters.
According to the description on msdn, the StringLengthAttribute Class:
Specifies the maximum length of characters that are allowed in a data
field. [.NET Framework 3.5]
Specifies the minimum and maximum length of characters that are
allowed in a data field. [.NET Framework 4]
The current version (2.7.1) is built on .NET Framework 3.5. The StringLengthAttribute class is supported as of 2.4.0.
That being said, the created instance is valid (it's just that the second assertion statement isn't).
Here is a passing test that validates the created instance using the Validator class from the System.ComponentModel.DataAnnotations namespace:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using NUnit.Framework;
using Ploeh.AutoFixture;
public class Tests
{
[Test]
public void GetAll_HasContacts()
{
var fixture = new Fixture();
var bleh = fixture.CreateAnonymous<Bleh>();
var context = new ValidationContext(bleh, serviceProvider: null, items: null);
// A collection to hold each failed validation.
var results = new List<ValidationResult>();
// Returns true if the object validates; otherwise, false.
var succeed = Validator.TryValidateObject(bleh, context,
results, validateAllProperties: true);
Assert.That(succeed, Is.True); // pass
Assert.That(results, Is.Empty); // pass
}
public class Bleh
{
[StringLength(255)]
public string Foo { get; set; }
public string Bar { get; set; }
}
}
Update 1:
While the created instance is valid, I believe that this could be adjusted to pick a random number inside the range (0 - maximumLength) so the user never gets an empty string.
I have also created a discussion at the forum here.
Update 2:
The original test case will now pass if you upgrade to AutoFixture version 2.7.2 (or newer).
[Test]
public void GetAll_HasContacts()
{
var fix = new Fixture();
var bleh = fix.CreateAnonymous<Bleh>();
Assert.That(bleh.Bar, Is.Not.Empty); // pass
Assert.That(bleh.Foo, Is.Not.Empty); // pass (version 2.7.2 or newer)
}

Related

Load Tests, trying to generate random names but getting same names for many virtual users

I'm using Visual Studio Performance Tests. I want to generate a random name before each of my requests. I'm using this WebTestRequestPlugin for that:
using System;
using System.ComponentModel;
using System.Linq;
using Microsoft.VisualStudio.TestTools.WebTesting;
namespace TransCEND.Tests.Performance.Plugins
{
public class RandomStringContextParameterWebRequestPlugin : WebTestRequestPlugin
{
[Description("Name of the Context Paramter that will sotre the random string.")]
[DefaultValue("RandomString")]
public string ContextParameter { get; set; }
[Description("Length of the random string.")]
[DefaultValue(10)]
public int Length { get; set; }
[Description("Prefix for the random string.")]
[DefaultValue("")]
public string Prefix { get; set; }
private readonly string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private Random _random = new Random();
public RandomStringContextParameterWebRequestPlugin()
{
ContextParameter = "RandomString";
Prefix = "";
Length = 10;
}
public override void PreRequestDataBinding(object sender, PreRequestDataBindingEventArgs e)
{
e.WebTest.Context[ContextParameter] = CreateNewRandomString();
base.PreRequestDataBinding(sender, e);
}
private string CreateNewRandomString()
{
var randomString = new string(Enumerable.Repeat(_chars, Length).Select(s => s[_random.Next(s.Length)]).ToArray()).ToLower();
return $"{Prefix}{randomString}";
}
}
}
My problem is that when I start a load test with multiple virtual users, the preRequest code runs for the first few users immediately, rewriting the RandomName context parameter on every run. So when my requests are actually running, they are using the same random name, causing a conflict in my back-end code.
My question is how can I generate random names for each of my requests even when the user load is high?
I think the problem is that the standard random number routines are not thread safe. Thus each virtual user (VU) gets the same random seed value and hence the same random numbers. See here and here for fuller explanations.
The code for CreateNewRandomString is not shown in the question but it probably uses the basic C# random number code which has the problem described above. The solution is to use a safer random number. This question provides some ideas on better random number generators.
I have used code based on the following in several performance tests:
public static class RandomNumber
{
private static Random rand = new Random(DateTime.Now.Millisecond);
private static object randLock = new object();
/// <summary>
/// Generate a random number.
/// </summary>
/// <param name="maxPlus1">1 more than the maximum value wanted.</param>
/// <returns>Value between 0 and maxPlus1-1 inclusive. Ie 0 .le. returned value .lt. maxPlus1</returns>
public static int Next(int maxPlus1)
{
int result;
lock (randLock)
{
result = rand.Next(maxPlus1);
}
return result;
}
}
It should be simple to add a string creation method to the above code, something that generates the wanted string within a lock{ ... } statement.
The part of the question that states "rewriting the RandomName context parameter on every run. So when my requests are actually running, they are using the same random name" is misunderstanding what is happening. Each VU gets its own set of CPs, it is just that the random numbers are the same.

Want to save selected (i.e., more than 1) enums as string with NHibernate

I cannot for the life of me get this to work with my existing code, but I am trying to save my enum selections as strings in NHibernate. Basically, I have a UI check box and if the user selects multiple check boxes I want to store those selections. Now, I can get NHibernate to store ONE selection (e.g., from a drop down or radio button list, where the user is limited to one choice only).
This is the jist of what I have for an enum:
public enum IncomeType
{
[Display(Name = "Full-Time Employment")]
FullTime,
[Display(Name = "Part-Time Employment")]
PartTime,
[Display(Name = "Self-Employment")]
SelfEmployed,
[Display(Name = "Rental")]
Rental,
[Display(Name = "Social Security Payments")]
SocialSecurity,
[Display(Name = "Retirement / Pension Payments")]
Retirement,
[Display(Name = "Child Support Payments")]
ChildSupport,
[Display(Name = "Spousal Maintenance")]
Maintenance,
[Display(Name = "Other")]
Other
}
I use a method to "select" whether a checkbox list is shown (if my BulkItemThreshold equals the number of options, a checkbox list is displayed). Here is that method:
public static IEnumerable<SelectListItem> GetItemsFromEnumString<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
(Note: some items in there are helpers, but I don't believe they are relevant; also, the selected input is displayed using a template .cshtml file - again, not sure if that's relevant)
Now, I call this thusly:
public class IncomeTypeSelectorAttribute : SelectorAttribute
{
public override IEnumerable<SelectListItem> GetItems()
{
return Selector.GetItemsFromEnumString<IncomeType>();
}
}
And finally, we get to the virtual property (using a proxy) but this is where NHibernate throws a wrench (Note: this was working fine for me before NHibernate, and now I am trying to get many lines of code working with it WITHOUT having to re-do everything; if I re-do everything I will probably triple the code I already have to get it to work):
Property (record):
[IncomeTypeSelector(BulkSelectionThreshold = 9)]
public virtual List<string> IndividualIncomeTypeCheckBox { get; set; }
proxy (part):
public List<string> IndividualIncomeTypeCheckBox
{
get { return Record.IndividualIncomeTypeCheckBox; }
set { Record.IndividualIncomeTypeCheckBox = value; }
}
Again, this is how I was doing things and it was working great before NHibernate. But now I have to use NHibernate. No getting around it.
I am using a service class that it tying the two together in a Create method to save in the DB with NHibernate, and for the above it would ordinarily look like this:
part.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
This would work if it were just one selection.
Well, I've spent a good two (2) months trying to get this to work. It's tough because I have lots of code where the user can make only one selection (such as with a radiobutton list) and it works GREAT - even with NHibernate. Let me give you an example:
public virtual IncomeType? IndividualIncomeTypeCheckBox { get; set; }
If I do the above, it will display a drop down list, and NHibernate will store the ONE allowable option selected by the user in the DB no problem. But more than one option with List<string> does not work.
Now, I have tried everything I could find here or elsewhere and nothing works. Yes, I know it should be IList<IncomeType> or some other variant. But if I use this then NHibernate requires that IncomeType be another table in the DB. This is too much code to write for such a simple thing I believe. We are not talking a many-to-many relation in the sense that this is not a User with Multiple addresses (wherein addresses would have street, city, state, zip, etc.).
I have tried different types of proxy get and set code, but nothing works. I have tried [Flags] and other things working with string only, but to no avail. Those last solutions would "work" but ONLY to save the first item selected out of multiple (i.e., in my scenario, if the user selected "FullTime" and "Rental" as Income Types, then only "FullTime" (string) would be saved or "1" ([Flags]/int), not both items selected.
I have a situation where I re-display the choices using a ReadOnly attribute like this:
[IncomeTypeSelector]
[ReadOnly(true)]
public List<string> IndividualIncomeTypeCheckBoxPost
{
get { return IndividualIncomeTypeCheckBox; }
}
This would display on the UI, but I tried doing something like this with NHibernate and it wouldn't work.
Could anyone please show me, using the above, how I can go about getting NHibernate to store more than one enum in this checkbox list scenario?
UPDATE:
More poking around here and on the web, and I came up with the following (which still does not work).
Property (record):
[IncomeTypeSelector(BulkSelectionThreshold = 9)]
public virtual IList<IncomeTypeRecord> IndividualIncomeTypeCheckBox
{
get { return incomeType; }
set { incomeType= value; }
}
private IList<IncomeTypeRecord> incomeType =
new List<IncomeTypeRecord>();
Proxy (part):
public IList<IncomeTypeRecord> IndividualIncomeTypeCheckBox
{
get { return Record.IndividualIncomeTypeCheckBox; }
set { Record.IndividualIncomeTypeCheckBox= value; }
}
And a change to the enum:
public enum IncomeType : int // removing int & value still gives validate error
{
[Display(Name = "Full-Time Employment")]
FullTime = 1,
[Display(Name = "Part-Time Employment")]
PartTime,
....
}
And I added this class to support IncomeTypeRecord
public class IncomeTypeRecord
{
public virtual int Id { get; set; }
public virtual IncomeType Value { get; set; }
}
HOWEVER, when I get to the UI screen and pick one or more options I get a validation error (value not valid). For example, say I pick FullTime alone, or pick FullTime and Retirement, then the UI will display the following error:
The value 'FullTime' is invalid.
The value 'FullTime,Retirement' is invalid.
(respectively)
Even if I remove the int declaration for the enum and get rid of the value I started with "1", I still get this validation error. I tried messing around with and adding different model binders (which now has me stumped as to whether my original problem still exists and now I have a different problem - but you still get bounty points :) ).
Pulling my hair out. If I could offer more bounty I would. I need a definitive solution. I appreciate any help.
UPDATE
This is what I have so far:
Record:
public virtual string IndividualIncomeTypeCheckBox{ get; set; }
Part:
//If I do IEnumberable<string> my .Select throws a cast error
public IEnumerable<IncomeType> IndividualIncomeTypeCheckBox
{
get
{
return Record
.IndividualIncomeTypeCheckBox
.Split(',')
.Select(r => (IncomeType)Enum.Parse(typeof(IncomeType), r));
}
set { Record.IndividualIncomeTypeCheckBox= value
== null ? null : String.Join(",", value); }
}
Service Class:
public SimplePart CreateSimple(SimplePartRecord record)
{
SimplePart simple = Services.ContentManager.Create<SimplePart>("Simple");
...
//How I would save a FirstName property (example Part / PartRecord below)
//public virtual string FirstName { get; set; } - PartRecord
//public string FirstName - Part
//{
// get { return Record.FirstName ; }
// set { Record.FirstName= value; }
//}
simple.FirstName = record.FristName;
...
//I obviously cannot do the following with the above IncomeType
//Getting cannot convert string to IEnumerable error
//How would I write this:
simple.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
...
}
And this is how it's called in a controller (this persists to DB): (Updating Controller code)
public ActionResult Confirm(string backButton, string nextButton)
{
if (backButton != null)
return RedirectToAction("WrapUp");
else if ((nextButton != null) && ModelState.IsValid)
{
_myService.CreateSimple(myData.SimplePartRecord);
return RedirectToAction("Submitted");
}
else
return View(myData);
}
Updating with additional code (serialization and view model):
"myData" is defined in the controller (using Serialization) as:
private MyViewModel myData;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var serialized = Request.Form["myData"];
if (serialized != null)
{
myData = (MyViewModel)new MvcSerializer().Deserialize
(serialized, SerializationMode.Signed);
TryUpdateModel(myData);
}
else
myData = (MyViewModel)TempData["myData"] ?? new MyViewModel();
TempData.Keep();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Result is RedirectToRouteResult)
TempData["myData"] = myData;
}
I use Serialization because I set up a multi-step wizard (as seen in the controller action "backButton" "nextButton) on the front-end. I am not using a driver (which can only display Admin or on the front-end but then only on .cshtml files directly under the ~/Views folder (not in a structured folder list like I am using)). No driver = no update view model type code = no mechanism to "create" the data in the DB. If I do not use some "create" type method, the form will submit but all the data will be "NULL".
When you say that the data should be persisted automatically, I am sorry but I do not see how. All the stuff I read or code I review has SOME method of updating the DB with whatever is entered in a form. If I am missing something, my apologies.
"MyViewModel" is pretty straightforward:
[Serializabel]
public class MyViewModel
{
public SimplePartRecord SimplePartRecord { get; set; }
}
And, just in case, here is the relevant portion of the migration (return 1 is a completely separate and unrelated table):
public int UpdateFrom1()
{
SchemaBuilder.CreateTable("SimplePartRecord",
table => table
.ContentPartRecord()
...
.Column("IndividualIncomeTypeCheckBox", DbType.String)
...
);
ContentDefinitionManager.AlterPartDefinition("SimplePart",
part => part
.Attachable(false));
return 2;
}
The error I am getting is
Cannot implicitly convert type 'string' to 'System.Collections.Generic.IEnumerable'"
when I do the following in the "Create" method of my service class:
simple.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
One additional thought: I tried using the n-n Relation sample to handle this scenario. Aside from it being a lot of extra code for what I thought should be straightforward and simple, because of the way I am using Serialization I had a lot of object reference errors and could not figure out how to properly code my controller to handle it.
There's a lot of info to wade through here so hopefully I haven't missed the point. It appears to me that the goals are:
The business class has a collection property of IList<IncomeType> without requiring an additional table
The values in that collection should be persisted as a delimited string of the enum names
The best approach is to use a custom user type (an implementation of NHibernate.UserTypes.IUserType) to map the property. Below is a generic IUserType that will map an enum of type T from an IList<T> property to a comma delimited string in the database and back again. There's no easy way to restrict T to an enum but the code will only work with enums.
Mapping a property using the custom type is simple with Fluent NHibernate:
public class Person
{
public Person()
{
IncomeTypes = new List<IncomeType>();
}
public virtual int PersonId { get; protected set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<IncomeType> IncomeTypes { get; protected set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.PersonId).GeneratedBy.Identity();
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.IncomeTypes).CustomType<EnumAsDelimitedStringType<IncomeType>>();
}
}
And here's the code for the user type:
public class EnumAsDelimitedStringType<T> : IUserType
{
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
var xList = x as IList<T>;
var yList = y as IList<T>;
if (xList == null || yList == null)
{
return false;
}
// compare set contents
return xList.OrderBy(xValue => xValue).SequenceEqual(yList.OrderBy(yValue => yValue));
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var outValue = NHibernateUtil.AnsiString.NullSafeGet(rs, names[0]) as string;
if (string.IsNullOrEmpty(outValue))
{
return new List<T>();
}
var getValueArray = outValue.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
return Array.ConvertAll(getValueArray, s => (T)Enum.Parse(typeof(T), s)).ToList();
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var inValue = value as IList<T>;
// set to string.Empty if you prefer to store that instead of null when the collection is null or empty
object setValue = null;
if (inValue != null && inValue.Any())
{
var setValueArray = Array.ConvertAll(inValue.ToArray(), v => Enum.GetName(typeof(T), v));
setValue = string.Join(",", setValueArray);
}
NHibernateUtil.AnsiString.NullSafeSet(cmd, setValue, index);
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
public SqlType[] SqlTypes
{
get { return new[] {new SqlType(DbType.AnsiString)}; }
}
public Type ReturnedType
{
get { return typeof(IList<T>); }
}
public bool IsMutable
{
get { return false; }
}
}
I think you're on the right track pursuing a [Flags] enum. You may have done this, but just in case -- making an enum flags-worthy is more than adding the attribute. You also have to specify the value for the items in a binary-friendly manner. I've found the easiest way to do this is as follows:
[Flags]
public enum IncomeType : long // you'll need the room with several options
{
FullTime = 1,
PartTime = 1 << 1,
SelfEmployed = 1 << 2
// And so on
}
If you don't do this, then you'll get sequential integer values, which breaks the bitwise comparison that allows you to do multiple values in a single integer.
Your code to create the SelectList looks fine. Your options should construct form values that get posted back with the same name. If you want to use the default modelbinder, that means the associated property on your view model would need to be List<int>. If you're not using a view model (you probably should) you can pull it out of the forms collection.
Once you have this set up, then translating from your view model to your NHibernate entity is simple if a little annoying. You basically have to cycle through the values in the list and |= them onto your NHibernate entity's single enum property.
So let's assume you have a view model like this:
public class MyEditViewModel
{
public string Name { get; set; }
public List<int> IncomeSelections { get; set; }
// You'll probably have this to populate the initial view rendering
public SelectList AllIncomeOptions { get; set; }
}
You'll build your view using your helpers and all that, then build the checkboxes using the SelectList but making sure the input name is IncomeSelections, then when it's posted back you will push the view model data into your NHibernate entity something like this:
var myNHEntity = new NHEntity();
// If you're editing an existing entity, then be sure to reset the enum
// value to 0 before going into the following foreach loop...
foreach (var incomeSelection in viewModel.IncomeSelections)
{
myNHEntity.IncomeSelection |= incomeSelection;
}
There's probably a more clever way to do this, and you might have to cast the int to your enum type, but you'll figure that out (I'd do it for you, but it is Friday and I already have a beer open).
NHibernate should persist it without you having to do anything funky on the NH side.
In summary...
It seems like this is more a problem of how you handle the posted data than the NHibernate side. If you implement something like this, then be sure to use Fiddler or FireBug to inspect the posted values to make sure 1) they're integers and 2) the names are the same so they'll be added to the list.
Good luck!
The problem is simply that it won't be able to map a List without building a full relationship with an intermediate association table. It is way simpler to have the record store the values as a comma-separated string (so your record property is a string, not a list of string) and your part can map back and forth between string and List.
You can find an example of something very close here:
https://bitbucket.org/bleroy/nwazet.commerce/src/d722cbebea525203b22c445905c9f28d2af7db46/Models/ProductAttributesPartRecord.cs?at=default
https://bitbucket.org/bleroy/nwazet.commerce/src/d722cbebea525203b22c445905c9f28d2af7db46/Models/ProductAttributesPart.cs?at=default
It's not using enum values, instead it's a list of ids, but that should give you a good idea about how to make this work fairly simply: parsing enums you already know how to do.
Let me know if you need more details, but I think that's what you needed to get unblocked.

Custom dynamic dataannotation for field visibility c#

I am trying to create a dataannotations attribute that controls field visiblity based on settings in a database. The attribute will be used within a system that will be used by multiple clients. Further, the visibility of the field needs to be able to change on the fly. I know I could do an if statement around each field in the view, but I am trying to avoid that and keep the visibility control within the view model as follows:
[Visible(FirstName)]
public string FirstName { get; set; }
I have tried creating this custom attribute that gets the value from a method from a resource class called ResourceType (which is generated using T4 and contains the necessary code to hit the database):
public class VisibleAttribute : Attribute, IMetadataAware
{
/// <summary>
/// Whether this field is visible
/// </summary>
public bool Hidden { get; set; }
public VisibleAttribute(string theFieldName)
{
ResourceType resources = new ResourceType();
Type _resourceType = typeof(ResourceType);
MethodInfo getHidden = _resourceType.GetMethod("IsHidden");
object[] requiredParams = new object[] { theFieldName };
Hidden = (bool)getHidden.Invoke(resources, requiredParams);
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.ShowForEdit = !Hidden;
metadata.HideSurroundingHtml = Hidden;
}
}
Here is an excerpt of the ResourceType class:
public class ResourceType
{
public const string Creditors_SecondaryCreditorsPayOffYesNo_Require = "Prop_Creditors_SecondaryCreditorsPayOffYesNo_Require";
public static string Prop_FieldName_Require
{
get { return GetHiddenOption(FieldName) ? "true" : "false"; }
}
internal static bool GetHiddenOption(string fieldName)
{
< < Logic here to get the option from the database > >
}
I have also tried the same attribute but with the following constructor:
public VisibleAttribute(string theFieldName)
{
ResourceType resources = new ResourceType();
Type _resourceType = typeof(ResourceType);
PropertyInfo getHidden = _resourceType.GetProperty(theFieldName);
Hidden = (bool)getHidden.GetValue
}
The problem I have with these two attempts is that, since the code is in the constructor, it only runs the first time I load the page after an IIS reset. So, any further changes I make to the visibility settings are not reflected without amother IIS reset.
I also tried creating a custom DataAnnotationsModelMetadataProvider that attempts to only load the setting once per page request:
public class EGTDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType,
Func<object> modelAccessor, Type modelType, string propertyName)
{
var data = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var visibleAttributeMetadata = attributes.SingleOrDefault(a => typeof(VisibleAttribute) == a.GetType());
if (visibleAttributeMetadata != null)
{
VisibleAttribute visibleAttribte = (VisibleAttribute)visibleAttributeMetadata;
if (!visibleAttribte.VisibleIsSet)
{
PropertyInfo getHidden = visibleAttribte.ResourceType.GetProperty("Prop_" + WebUtils.RemoveSectionNameSpace(visibleAttribte.SectionName) + "_" + visibleAttribte.FieldName + "_Hide");
visibleAttribte.IsHidden = bool.Parse(getHidden.GetValue(null, null).ToString());
data.HideSurroundingHtml = visibleAttribte.IsHidden;
data.ShowForEdit = !visibleAttribte.IsHidden;
visibleAttribte.VisibleIsSet = true;
}
else
{
data.HideSurroundingHtml = visibleAttribte.IsHidden;
data.ShowForEdit = !visibleAttribte.IsHidden;
}
}
return data;
}
}
One issue I have with the ModelMetadataProvider is that the CreateMetadata method runs many times for a single field during a single request. It is very inefficient code, and a huge decrease in performace, to call the database 5-10+ times per request to get a setting that has not changed since the beginning of the request. If I try to set a flag indicating I've already loaded the setting, I'm back to the same scenario as above where I don't see the setting change until after an IIS reset.
I'm hoping someone can give me some pointers as to what methods I can employ to see the database changes real time. Or am I trying to do the impossible? Thanks in advance.
You could combine the metadata provider approach with caching the value just that single request.
For this you could use the Items dictionary in the current HttpContext. Be careful with this as a redirect will cause the items to be cleared:
string cacheKey = String.Format("IsVisible-{0}", propertyName)
if(!HttpContext.Current.Items.Contains(cacheKey))
HttpContext.Current.Items[cacheKey] = //get setting from db
bool isVisible = (bool)HttpContext.Current.Items[cacheKey];
You can also consider using the ASP .Net Cache in case that you prefer caching the value not just for the current request (Although you mentioned that within your metadata provider you were trying to load the setting once per request)

MVVM Light - Unable to update parent view from child - nested edit

My situation is slightly different than from other posts and I was not able to solve it with the other trhreads. So that why I ask.
I have a class that is obtained from deserializing an XML like:
<?xml version="1.0" encoding="UTF-8"?>
<node>
<leaf>
<name>node 1</name>
<text>text 1</text>
<url>url 1</url>
</leaf>
<leaf>
<name>node 2</name>
<text>text 2</text>
<url>url 2</url>
</leaf>
</node>
so the class is:
[XmlRoot("node")]
public class csNodeList
{
public csNodeList()
{
Leaf = new csLeafCollection();
}
[XmlElement("leaf")]
public csLeafCollection Leaf
{
get;
set;
}
}
public class csLeaf
{
public csLeaf()
{
Name ="";
Description = "";
Address = "";
}
[XmlElement("name")]
public string Name
{
get;
set;
}
[XmlElement("text")]
public string Description
{
get;
set;
}
[XmlElement("url")]
public string Address
{
get;
set;
}
}
public class csLeafCollection : System.Collections.ObjectModel.ObservableCollection<csLeaf>
{
}
Then I have 2 Views, one to show all the leafs and one to edit one leaf. I've implemented commit and rollback so I use messaging back and forth to pass the new values and I store the old ones.
To do so I copy the objects a a backup variable and then I modify the ones associated via binding to the XAML view, in this way (in theory) any change to the ViewModel data should be reflected.
Also is better because if I commit the changes I just discard the backup variables (this is 90% of the times) and if I need to roll back I copy back from the backup variables.
MainView:
public const string listPropertyName = "list";
private csNodeList _list = new csNodeList();
public csNodeList list
{
get
{
return _list;
}
set
{
Set(listPropertyName, ref _list, value, false);
}
}
Using the message I send back the new values of a node and I put them in the correct position:
private void DoSomething(csMessage message)
{
csMessage rmessage;
if (message != null)
{
switch (message.destination)
{
case csMessage.e2MessageDest.updateNode:
//_editP should be fine.
list.Leaf[list.Leaf.IndexOf(_editP)].Name = ((csLeaf)message.payload).Name;
list.Leaf[list.Leaf.IndexOf(_editP)].Text= ((csLeaf)message.payload).Text;
list.Leaf[list.Leaf.IndexOf(_editP)].Address = ((csLeaf)message.payload).Address;
RaisePropertyChanged(listPropertyName , null, _list, true);
break;
}
}
}
The code is executed correctly and the item is changed.
BUT the RaisePropertyChanged is ignored. I've tried even just the one with the listPropertyName without any change.
If I save the changes exit from the app and get back I see the new value correctly stored
Can you please help me?
Thanks,
Massimo
The reason why your RaisePropertyChanged is ignored is hat yor Leaf class des not implement INotifyOropertyChanged. Commonly the model is wrapped into a view model which then implements INotifyPropertyChanged to notify the view hat something has happened.
However, you also can implement INotifyPropertyChanged on the model class directly. To implement INotifyPropertyChanged each property has to raise the propty changed event.
public string Property {
get { ... }
set {
if (_propertyField == value)
return;
_propertyField = value;
RaisePropertyChanged("Property");
}
}
The code assumes hat there is a method RaisePropertyChanged which actually taises the PropertyChangedEvent.
Thank you everyone for the help.
Investigating your suggestion I've found a slightly different solution; as you correctly said the issue is that the leaf fields are not "observable" so they do not generate a notification event.
I've noticed that if I add or Delete a profile the binding is updated.
So what I've decided to do is not to edit directly the leafs but to replace the node.
What I do not like is that I have to create a node to replace the old one and this allocates a little bit more memory... but for small data like the one I have it can work without any major impact on the app performance/memory foot print.
Here is what I do:
csLeaf _leaf = new slLeaf();
_leaf.Name = ((csLeaf)message.payload).Name;
_leaf.Text= ((csLeaf)message.payload).Text;
_leaf.URL = ((csLeaf)message.payload).Address;
list.Leaf[list.Leaf.IndexOf(_editP)] = _leaf;
To optimized readabilty of code I've enhanced it adding a constructor with 3 parameters so that the code can be:
csLeaf _leaf = new slLeaf(((csLeaf)message.payload).Name, ((csLeaf)message.payload).Text, ((csLeaf)message.payload).Address);
list.Leaf[list.Leaf.IndexOf(_editP)] = _leaf;
The constructor is:
public csLeaf(string _name, string _description, string _address)
{
Name = _name;
Description = _description;
Address = _address;
}

ASP.NET MVC Validation Help

I'm doing some simple validation inside my Controller I know this would better placed inside something like a service layer but for this I want to keep it inside the Controller
The idea is to check that a valid url is being entered into a url field to display an image, e.g. http://domain.com/myawesomeimage.png would be valid and http://domain.com/ would not be valid.
// ValidateInt has a default value of 0
int ValidateInt = 0;
// If the url entered (if one at all) does not have correct extension then increment the ValidateInt
if (!ArticleToEdit.image.Contains(".jpg"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".jpeg"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".png"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".gif"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".bmp"))
ValidateInt++;
// if ValidateInt is bigger than 0 then the url is invalid
if (ValidateInt > 0)
ModelState.AddModelError("Image", "Please enter a valid URL.");
EDITED CODE
The problem with your code
Your code is invalid, because your model state will always have at least 4 errors. Even though the URL would be correct. Your code requires that your URL must have all extensions which is of course incorrect. It can only have one. At most.
The solution
Use DataAnnotations instead and use regular expression validator. You're obviously already using some application model class called ArticleToCreate. You'll have to put data annotations attribute on the image property (one more observation: keey property names with Pascal casing so it's the same as .net):
public class ArticleToCreate
{
[RegularExpression(#"...")] // add regular expression that fulfils your requirements
public string Image { get; set; }
...
}
Then it all depends how complicated your regular expression is. The easiest one for your needs could be just that it starts with an http:// and end with the correct extension:
^http:\/\/.+\.(?:png|jpe?g|gif|bmp)$
And if you're directly providing your class instance to controller action it will get automatically validated for you without any additional code. This way you won't be able to forget to validate your objects manually.
Consider this controller action that automatically validates your model class object instance by validators defined on it (as per validator definition I've written above):
public ActionResult Create(ArticleToCreate data)
{
if (!this.ModelState.IsValid)
{
// handle invalid object
}
// handle valid object
}
This way your actions will focus on the processing part which is their main objective instead of focusing on too many aspects of your business process like validation for instance.
Shorter code = simpler code = easier to maintain = less bugs = less work = happy clients
Why not create custom ValidationAttributes (from DataAnnotations) and allow the Validation Engine do the work for you rather than worrying about where to put your logic?
I'm guessing it would look something like:
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field, AllowMultiple = false)]
public class ValidImageUrlAttribute : ValidationAttribute
{
public string ErrorMessage { get; set; }
public override bool IsValid(object value)
{
var url = value as string;
if(!url.Contains(".jpg") || !url.Contains(".jpeg")
|| !url.Contains(".gif") || !url.Contains(".bmp")
|| !url.Contains(".png"))
{
return false;
}
return true;
}
public override string FormatErrorMessage(string name)
{
return ErrorMessage ?? base.FormatErrorMessage(name);
}
}
And then you could decorate your Model:
[Required(ErrorMessage = "Image URL is required.")]
[ValidImageUrl(ErrorMessage = "Valid Image URL is required.")]
public string ImageUrl { get; set; }
changed if (validate1 > 0) and it works fine :)

Resources