I have an issue when I am trying to delete an entity that also has a modified enum property. The error i get is Invalid cast from 'System.String' to 'BV.Entities.CarType'. A simple example follows:
public class Car {
public int Id { get; set; }
public CarType Type { get; set; } // CarType is an enum
}
var car = // load a car entity
car.Type('Sedan');
car.entityAspect.setDeleted();
manager.saveChanges();
This can happen, for example, when a user starts editing a record, but then decides to just delete it.
I have also used the DocCode sample to test this. I edited the saveTodoTests.js and used the 'can save add, update, and delete in one batch' test similar to what was suggested here Exception in client breeze.js when using enum property on model. If I alter the enum type of the deleteTodo item it throws the error, if I remove the setDeleted() it will save it correctly.
System.InvalidCastException was unhandled by user code
HResult=-2147467262
Message=Invalid cast from 'System.String' to 'BV.Entities.CarType'.
Source=mscorlib
StackTrace:
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at Breeze.ContextProvider.EF6.EFContextProvider`1.ConvertValue(Object val, Type toType)
at Breeze.ContextProvider.EF6.EFContextProvider`1.SetPropertyValue(Object entity, String propertyName, Object value)
at Breeze.ContextProvider.EF6.EFContextProvider`1.<>c__DisplayClass10.<RestoreOriginal>b__f(KeyValuePair`2 kvp)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at Breeze.ContextProvider.EF6.EFContextProvider`1.RestoreOriginal(EntityInfo entityInfo)
at Breeze.ContextProvider.EF6.EFContextProvider`1.<ProcessAllDeleted>b__9(EFEntityInfo entityInfo)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at Breeze.ContextProvider.EF6.EFContextProvider`1.ProcessAllDeleted(List`1 deletedEntities)
at Breeze.ContextProvider.EF6.EFContextProvider`1.SaveChangesCore(SaveWorkState saveWorkState)
at Breeze.ContextProvider.ContextProvider.OpenAndSave(SaveWorkState saveWorkState)
at Breeze.ContextProvider.ContextProvider.SaveChanges(JObject saveBundle, TransactionSettings transactionSettings)
at BV.Web.Controllers.DefaultController.SaveChanges(JObject saveBundle) in c:\Work\Code\BV\BV.Web\Controllers\DefaultController.cs:line 59
Ok, this was a bug and is fixed in the combination of the current breeze.server.net and breeze.js repos on GitHub. It will also go out as part of the next release (1.4.14) sometime next week.
Related
How to create a unit test to an Entity Framework object to verify an integer attribute validation annotated with the [required] key like this:
[Required]
public Int32 MyIntProperty { get; set; }
The unit test code should be something like this:
EntityObject entityObject = new EntityObject();
entityObject.MyIntProperty = null;
EntityObjectContext.EntityObject.Attach(entityObject);
EntityObjectContext.ObjectStateManager.ChangeObjectState(entityObject, EntityState.Added);
var dbContext = new DbContext(EntityObjectContext, true);
int errors = dbContext.GetValidationErrors().Count();
Assert.AreEqual(1, errors);
The problem is that I can not indicate the null value to an integer property. Is it possible to tests this validation another way?
Tks.
If the validation fails only on null then there is nothing to test here, as Int32 will never be null. If you want to make it possible to be null change it's type to Int32? (or better: int?). If the validation fails for some other reasons, then don't put null into your property but some other erroneous value.
Int32 cannot be null unless it Int32?.
I don't know if the following suits your requirement but Int32 would be 0 when not initialised.
Assert.AreNotEqual(0, error);
I resolve my problem by testing if the property was annotated by the [Required] key word:
var propertyInfo = typeof(EntityObject).GetProperty("MyIntProperty");
var attribute = (EdmScalarPropertyAttribute)
propertyInfo.GetCustomAttributes(
typeof(EdmScalarPropertyAttribute), true)
.FirstOrDefault();
Assert.IsFalse(attribute.IsNullable);
If anyone have other solution please tell me.
Tks.
I've got one method, which take a model [AccountLinkRequest] as a parameter with url-encoded data. It's uses Json.NET by default, and also, I can't use the setting UseDataContractJsonSerializer = true cause I have generic output response model (in other methods)
[HttpPost]
public SomeResponse Link(AccountLinkRequest request)
{
if (request.CustomerId == null)
throw new Exception("Deserialization error here, pls help!");
// other actions
}
Here is my model class:
[DataContract]
[JsonObject]
public class AlertAccountLinkRequest
{
[DataMember(Name = "id")]
public string id { get; set; }
[DataMember(Name = "customer_id")]
[JsonProperty("customer_id")]
public string CustomerId { get; set; }
}
The problem: request.CustomerId is allways null. The request is pretty simple:
web_service_URL/link?customer_id=customer_id&id=id (url-encoded)
if I use Customer_Id instead of CustomerId, everything will be fine, but I'm on a jedy-way. Thank you!
There is not a simple answer how to achieve that. For more details please read this:
How to bind to custom objects in action signatures in MVC/WebAPI
Summary:
Manually call the parse function inside of your Action
Use a TypeConverter to make the complex type be simple
Use a custom model binder
So, if you for instance create your 'SmartBinder' which is able to consume some attributes, you can get what you want. Out fo the box there is no functionality for that, just the naming conventions...
I am using Entity Framework 4.0, and making use of POCO objects. When I populate POCO objects from the DB, I translate property values to my own Domain objects, which we can call my Model.
Necessarily, whether or not the fields of my Model are Nullable depends on whether the value it maps to in the database comes from a NULL or NOT NULL column. I won't go into detail, but the values must be nullable in the DB, because a user can partially save a draft of the object before publishing it to the public. That being the case, I have several fields that are nullable. So let's say my model looks like:
public class MyModel
{
public int? Field1 {get; set; }
public DateTime? Field2 {get; set; }
public int Field3 {get; set; }
}
If I use this Model in my View, complete with nullable fields, I begin receiving errors that tell me I cannot use nullable properties as values in various places, like HTML helpers, etc. I could say something like if (Model.MyBoolField.HasValue && Model.MyBoolField.Value) { // etc }, but that feels bulky for a view.
I considered creating a ViewModel object that inherits from my original domain object and has new, non-nullable versions of my nullable fields that return an appropriate value if the base version is null. So something like:
public class MyViewModel : MyModel
{
public new int Field1
{
get { return base.Field1 ?? 7; }
}
public new DateTime Field2
{
get { return base.Field2 ?? DateTime.Now; }
}
}
My problem with this is that I don't always know a good "default" value to display. What if I threw an exception in the View Model's getter when the base value is null? Is that poor practice?
I'm basically looking for a best practice on how to handle nullable fields in a model, particularly when displaying in a View.
If you just need to display these fields in a View, you don't need to specify or check whether is has a value or not.
Using Model.Field1 in your View file is enough. It will simple not display anything, and it won't throw an exception. You can always use ?? to set a default when it makes sense.
#(Model.Field1 ?? "There is nothing to see here")
In most of the cases I use the "For" helpers, which seem OK with Nullable values (PublishedCount is a nullable property):
#Html.TextBoxFor(m => m.BillPull.PublishedCount, new { id="txtPublishedCount" })
#Html.ValidationMessageFor(m => m.BillPull.PublishedCount)
When I need to use just TextBox, I use the GetValueOrDefault method, with whatever default value the framework provides:
#Html.TextBox("BillPull.AutoPublishDate", Model.BillPull.AutoPublishDate.GetValueOrDefault().ToString(dateFormat), new { id = "dtpAutoPublishDate" })
#Html.ValidationMessageFor(m => m.BillPull.AutoPublishDate)
I wish to give a Person as defined below, the ability to print a vCard out of my system. To provide the user with privacy options, the user can select whether to show/hide certain properties. In it's simplest form, I need to have a separate table that would hold the user's choices.
I was wondering if it was possible to build this configurator table using reflection. As shown in the Person model below, I could decorate properties with a custom attribute, and then using those properties, construct and persist a model that would have a bool property for every decorated Person property.
public class Person
{
public string UserName { get; set; }
public string FirstName { get; set; }
[DisplayOnVCard]
public string LastName { get; set; }
[DisplayOnVCard]
public string Email { get; set; }
[DisplayOnVCard]
public string MobilePhone { get; set; }
}
** where [DisplayOnVCard] is a custom attribute.*
At the end of this, I expect a table in the db that would correspond to this:
public class VCardConfigurator
{
public bool LastName { get; set; }
public bool Email { get; set; }
public bool MobilePhone { get; set; }
}
This is just a sample representation of what is actually a huge entity. Which is why I hope to avoid manually mapping a bool field to each optional property.
I believe this problem domain is quite similar to how, for instance, privacy settings work on social networking sites, yes?
While I was typing this, I did ponder upon the possibility that if down the line I was to remove the attribute from one of the properties, what implications that might have. Needs some thought!
Further reading for self:
Programmatically adding properties to an MVC model at runtime
There is a huge possibility that I am galloping down a totally wrong path! If that is the case, please advice so!
#1 Update
I am not sure its possible to add or remove attributes for an instance since attributes are at the class level, but their property values can be changed (Since they are instances).
My suggested solusion
I am not sure what you mean in "I expect a table in the db that would correspond to this",
since you can't have a table in the database that contains only the columns of the non-privacy properties for each user.
You will need a dedicated table for this mapping (Lets say 'PrivacyMappings' table), with these columns:
UserId, PropertyName, IsPrivate.
When a user is added, all the properties will be added to this table with a default privacy settings (for instance, all properties are non-private by default).
You can add the properties by iterating over them and insert them as you said.
You can use the following class in entity framework:
public class PrivacyMapping
{
public int UserId {get;set;}
public string PropertyName {get;set;}
public bool IsPrivate {get;set;}
}
Adding the default privacy settings when a user being added:
// retrieve user model properties.
foreach (property in properties)
{
//iterrate over the user Properties.
context.PrivacyMapping.Add(new PrivacyMapping(user.userId, propertyName, isPrivate);
}
context.SaveChanges()
Now you can take all the user non-private properties by
context.PrivacyMapping.Where(p=>p.UserId == user.id && !IsPrivate).Select(p=>p.PropertyName);
And now you can deal with information any way you want.
For example, you can have a VCardItems class, that receive an user id/object in its c'tor and stores a dictionary of the allowed properties by their names.
public class VCardItems{
private Dictionary<string, object> properties{get;set;}
public VCardItems(User user)
{
// initiate values..
}
public object this[string name] {
get
{
if (properties.ContainsKey(name))
{
return properties[name];
}
// A private property.
return null;
}
set
{
properties[name] = value;
}
}
}
There is other options of how to use the data, for example with ActionFilter that in this case sets the private properties to null or storing the non-private data in the HttpContext.Items dictionary,
but it really up to you.
First message
Before we get into details, I wonder how you expect to use this class.
If a view (or whatever going to handle it), going to receive have a runtime-generated class for example, how you gonna handle it?
How you gonna know what properties this model has?
I have created a custom CompareLessThan validation attribute by copying the ASP.NET MVC 3 CompareAttribute and instead of checking for equality, I check to see that one property is less than another. If there is a client side error, the message '{0} must be less than {1}' is displayed to the user.
My model is setup as follows with the Display attributes referencing a resource file.
[CompareLessThan("AmountAvailable", ErrorMessageResourceName="CompareLessThan", ErrorMessageResourceType = typeof(Resources.ValidationMessages))]
[Display(Name = "Amount", ResourceType = typeof(Resources.Labels))]
public decimal Amount { get; set; }
[Display(Name = "AmountAvailable", ResourceType = typeof(Resources.Labels))]
public decimal AmountAvailable { get; set; }
Then the custom validation GetClientValidationRules method is exactly the same as in the CompareAttribute
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(OtherProperty), this.AllowEquality);
}
Here we are generating the error message that will be displayed to the user if there is a problem. I can get the display name from the resource file for the property that is decorated with my custom CompareLessThan attribute, but my question is how do I go about getting the display name of the 'other' property we are comparing against? In the IsValid method we have a reference to the validationContext from which I can generate a PropertyInfo object for the 'other' property and I think get the display name. But, in the GetClientValidationRules I don't have access to that.
I could always just pass in another value for the display name of the other property but I was hoping there would be a way to derive it as I'm already specifying it with data annotations.
Any ideas?
As of ASP.NET MVC 4 this is how I managed to get the other property:
PropertyInfo otherPropertyInfo =
this.Metadata.ContainerType.GetProperty(attribute.DependentProperty);
Then I got the Display attribute from the property:
var displayAttribute =
otherPropertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true).
FirstOrDefault() as DisplayProperty;
In your case:
// GetName() is important to get the translated name if you're using a resource file...
this.otherPropertyDisplayName = displayAttribute.GetName();
GetName() reference:
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.name%28v=vs.95%29.aspx
The answer provided by nemesv didn't work as the metadata.Model property has a value of 0. But, through the metadata we do have the full name of the model so it is possible to create a new instance of that model and then create a new DataAnnonationsModelMetadataProvider from that create instance. From there we can get the display name of the other property.
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
Type type = Type.GetType(metadata.ContainerType.FullName);
var model = Activator.CreateInstance(type);
var provider = new DataAnnotationsModelMetadataProvider();
var otherMetaData = provider.GetMetadataForProperty(() => model, type, this.OtherProperty);
this.otherPropertyDisplayName = otherMetaData.DisplayName;
yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(this.OtherProperty), this.AllowEquality);
}
I really don't like this solution (even though it works) as it seems there should be a better way. Does anyone else have any other ideas?
I haven't tried it out but you can get the model properties with the metadata.Properties property
metadata.Properties.Single(p => p.PropertyName == "OtherPropName").DisplayName;
EDIT: Because Properties is empty what you can always do (although it's very elegant). You can generate the metadata for yourself.
var provider = new DataAnnotationsModelMetadataProvider();
var otherMetaData = provider.GetMetadataForProperty(() => metaData.Model, metaData.ModelType, "OtherPropertyName");