In my app, I fetch two attributes IsLoopCar and LoopStatusSet from a DB and Store them to a model class:
private static ChecklistModel MapChecklistModel(DataRow row)
{
var checklist = new ChecklistModel
{
IsLoopCar = Convert.IsDBNull(row["IsLoopCar"]) ? null : Convert.ToBoolean(row["IsLoopCar"]),
LoopStatusSet = Convert.IsDBNull(row["LoopStatusSet"]) ? null : Convert.ToBoolean(row["LoopStatusSet"])
}
}
In my model class and the viewModel I map my model class to, the data type of these two attributes is bool?
public class ChecklistModel
{
public bool? IsLoopCar { get; set; }
public bool? LoopStatusSet { get; set; }
}
public class GetPaymentInformationViewModel
{
public bool? IsLoopCar { get; set; }
public bool? LoopStatusSet { get; set; }
}
The value of the attribute in my DB is null. When I map the model to the viewmodel using AutoMapper (7.0.1), I get an error message which I don't understand:
GetPaymentInformationViewModel gpivm = _mapper.Map<ChecklistModel, GetPaymentInformationViewModel>(checklist);
The mapping configuration is very basic:
CreateMap<ChecklistModel, GetPaymentInformationViewModel>();
The error message is:
InvalidOperationException: Nullable object must have a value.
I don't understand what's the point in mapping nullable bools, when they have to have a value. I need to have this value to be null when i pass it to the view because it works as kind of a checkpoint where the user must select a yes/no value with no initial default value set.
Based on the recommendation of Lucian, I tried to reproduce the problem with a fresh console app but it didn't fail there. As it turned out, the problem was in my view model where I had written a custom setter that makes no sense:
[Required]
[Display(Name = "IsLoopCar")]
public bool? IsLoopCar
{
get
{
return _IsLoopCar;
}
set
{
if (value.Value == true)
_IsLoopCar = value.Value;
else
{
_IsLoopCar = value.Value;
LoopStatusSet = null;
}
}
}
The field value.Value cannot be read because it does not exists. It's supposed to be like that:
[Required]
[Display(Name = "IsLoopCar")]
public bool? IsLoopCar
{
get
{
return _IsLoopCar;
}
set
{
if (value == true)
_IsLoopCar = value;
else
{
_IsLoopCar = value;
LoopStatusSet = null;
}
}
}
Related
I would need RequiredIfNull attribute for model validation.
How can I add conditional Required attribute. Condition would depend on another property. If that property value is null, then this shouldn't be.
Something like that:
public class MyModel
{
public int? prop1 { get; set; }
[ConditionalRequired(prop1)] //if prop1 == null, then prop2 is required, otherwise MyModel is invalid
public int? prop2 { get; set; }
}
You need a custom validation attribute. For example:
using System.ComponentModel.DataAnnotations;
using System.Reflection;
[AttributeUsage(AttributeTargets.Property)]
public class RequiredIfNullAttribute : ValidationAttribute
{
private const string DefaultErrorMessageFormat = "The {0} field is required.";
public RequiredIfNullAttribute(string otherProperty)
{
if (otherProperty == null)
{
throw new ArgumentNullException(nameof(otherProperty));
}
OtherProperty = otherProperty;
ErrorMessage = DefaultErrorMessageFormat;
}
public string OtherProperty { get; }
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value == null)
{
var otherProperty = validationContext.ObjectInstance.
GetType().GetProperty(OtherProperty);
object otherPropertyValue = otherProperty.GetValue(
validationContext.ObjectInstance, null);
if (otherPropertyValue == null)
{
return new ValidationResult(
string.Format(ErrorMessageString, validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
}
Then in your model:
public class MyModel
{
public int? prop1 { get; set; }
[RequiredIfNull(nameof(prop1))]
public int? prop2 { get; set; }
}
Things will get more complex if you also need to add client side validation.
I have two data contracts of same contents in two different namespaces. i have copied one datacontract to another and
passed to a particular method. but it is giving the below error and throwing exception. it is not going into that method.
Please let me know any ideas /suggestions on how to resolve this.Appreciate your help:
Exception errror:
{"Type 'System.Collections.Generic.List`1[[GlobalWcfServiceLib.TopicDetailsInfo, GlobalWcfContracts, Version=1.2.2.0, Culture=neutral,
PublicKeyToken=17c64733a9775004]]' with data contract name 'ArrayOfTopicDetailsInfo:http://CName.GlobalService/11.1/2010/11'
is not expected. Consider using a DataContractResolver or add any types not known statically to the list of
known types - for example, by using the KnownTypeAttribute attribute or by
adding them to the list of known types passed to DataContractSerializer."}
Message = "There was an error while trying to serialize parameter
http://CName.SharedServices.DBServiceLib:subscriptionDataContract.
The InnerException message was 'Type 'System.Collections.Generic.List`1
[[GlobalWcfServiceLib.TopicDetailsInfo,
GlobalWcfContra...
//////
Here is my scenario : i am copying the data from dc to new data contract as below. after copying , when i am executing the createsubscriptions method i am getting the above mentioned error. i have given the details of data contract and error attached to this question. please refer to that as well.
Method1(SubscriptionDataContracts dc)
{
SubscriptionDataContract subscriptionDataContract = new SubscriptionDataContract();
List<SubscriptionTopicInfo> topicsInfo = dc.TopicList;
List<SubscriptionTopic> newTopicsList = new List<SubscriptionTopic>();
subscriptionDataContract.ExtensionData = dc.ExtensionData;
subscriptionDataContract.UserID = dc.UserID;
for (int i = 0; i < topicsInfo.Count; i++)
{
SubscriptionTopic topic = new SubscriptionTopic();
topic.DBHandle = topicsInfo[i].DBHandle;
topic.Topic = topicsInfo[i].Topic;
topic.Target = topicsInfo[i].Target;
newTopicsList.Add(topic);
}
subscriptionDataContract.TopicList = newTopicsList;
CreateSubscriptions(subscriptionDataContract); //getting the above mentioned error in another dll after going into this method
}
////////////////////////////////
//My data contract
[DataContract(Name = "TopicDetailsInfo", Namespace = "http://CName.GlobalService")]
[Serializable]
public class TopicDetailsInfo
{
protected object topic;
protected object baseObjectType;
[DataMember]
public object BaseObjectType
{
get
{
return baseObjectType;
}
set
{
baseObjectType = value;
}
}
[DataMember]
public object TopicID
{
get
{
return topic;
}
set
{
topic = value;
}
}
static public TopicDetailsInfo CreateTopic<T, mT>(IComparable<T> objectType, IComparable<mT> objectID)
{
var topicDetails = new TopicDetailsInfo();
topicDetails.BaseObjectType = objectType;
topicDetails.TopicID = objectID;
return topicDetails;
}
}
[DataContract(Name = "SubscriptionTopicInfo", Namespace = "http://CName.GlobalService")]
[KnownType(typeof(List<TopicDetailsInfo>))]
[Serializable]
public class SubscriptionTopicInfo
{
private object topic;
private object target;
private object creator;
[DataMember]
public object Topic
{
get
{
return topic;
}
set
{
topic = value;
}
}
[DataMember]
public object Target
{
get
{
return target;
}
set
{
target = value;
}
}
[DataMember]
public object DBHandle
{
get
{
return creator;
}
set
{
creator = value;
}
}
static public SubscriptionTopicInfo CreateSubscriptions<T, mT, nT>(IList<TopicDetailsInfo> topic, IComparable<mT> target, IComparable<nT> handle)
{
var subscriptionTopic = new SubscriptionTopicInfo();
subscriptionTopic.Target = target;
subscriptionTopic.Topic = topic;
subscriptionTopic.DBHandle = handle;
return subscriptionTopic;
}
}
[DataContract(Name = "SubscriptionData", Namespace = "http://CName.GlobalService")]
[KnownType(typeof(List<SubscriptionTopicInfo>))]
[Serializable]
public class SubscriptionDataContracts : IExtensibleDataObject
{
private ExtensionDataObject extensionDataObjectValue;
[DataMember]
public string UserID
{
get;
set;
}
[DataMember]
public string ProjectID
{
get;
set;
}
[DataMember]
public string FromDiscipline
{
get;
set;
}
[DataMember]
public string ModuleID
{
get;
set;
}
[DataMember]
public string SessionID
{
get;
set;
}
[DataMember]
public List<SubscriptionTopicInfo> TopicList
{
get;
set;
}
public ExtensionDataObject ExtensionData
{
get
{
return extensionDataObjectValue;
}
set
{
extensionDataObjectValue = value;
}
}
}
I am implementing a RequiredIf validation attribute and the value being passed to the IsValid method is always null.
RequiredIfAttribute Class
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
ViewModel
[Required]
[Display(Name = "Are You A Student?")]
public bool? IsStudent { get; set; }
[RequiredIf("IsStudent", true, ErrorMessage = "You must upload a photo of your student ID if you wish to register as a student.")]
[Display(Name = "Student ID")]
[FileExtensions("jpg|jpeg|png|pdf", ErrorMessage = "File is not in the correct format.")]
[MaxFileSize(2 * 1024 * 1024, ErrorMessage = "You may not upload files larger than 2 MB.")]
public HttpPostedFileBase StudentId { get; set; }
EditorTemplate
#model bool?
#using System.Web.Mvc;
#{
var options = new List<SelectListItem>
{
new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value },
new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && Model.Value }
};
string defaultOption = null;
if (ViewData.ModelMetadata.IsNullableValueType)
{
defaultOption = "(Select)";
}
}
#Html.DropDownListFor(m => m, options, defaultOption)
Every time the form is submitted, the RequiredIf error message is thrown and I have a feeling it has to do with the null value I described initially. What am I doing wrong? Thanks!
NOTE: The HTML appears to be rendering properly, so I don't think that's the problem.
<select data-val="true" data-val-required="The Are You A Student? field is required." id="IsStudent" name="IsStudent"><option value="">(Select)</option>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
Because this is your code -
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
You are using a RequriedAtrribute. So to simulate that it behaves life a RequiredIf you have to implement some logic that will check whether the target property value is true or false. But you are not doing that and returning just from the innerattribute. So it is just a mere Required not RequiredIf -
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
modify this function to do some checking like -
public override bool IsValid(object value)
{
//if the referred property is true then
return innerAttribute.IsValid(value);
//else
return True
}
I use the following code:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public abstract class StefAttribute : ValidationAttribute
{
public WDCIAttribute()
: base()
{
this.ErrorMessageResourceType = typeof(GlobalResources);
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class StefRequiredIfAttribute : StefAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public WDCIRequiredIfAttribute()
{
}
public WDCIRequiredIfAttribute(string dependentProperty, object targetValue)
: base()
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
public class RequiredIfValidator : DataAnnotationsModelValidator<StefRequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, StefRequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
if (field != null)
{
// get the value of the dependent property
object value = field.GetValue(container, null);
// compare the value against the target value
if (IsEqual(value) || (value == null && Attribute.TargetValue == null))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
{
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
private bool IsEqual(object dependentPropertyValue)
{
bool isEqual = false;
if (Attribute.TargetValue != null && Attribute.TargetValue.GetType().IsArray)
{
foreach (object o in (Array)Attribute.TargetValue)
{
isEqual = o.Equals(dependentPropertyValue);
if (isEqual)
{
break;
}
}
}
else
{
if (Attribute.TargetValue != null)
{
isEqual = Attribute.TargetValue.Equals(dependentPropertyValue);
}
}
return isEqual;
}
}
Which can be used in the model as follows:
public class PersonnelVM : EntityVM
{
// . . .
[DisplayName("Name")]
[StefRequiredIf("IndividualOrBulk", PersonnelType.Bulk, ErrorMessageResourceName = GlobalResourceLiterals.Name_Required)]
public string Name { get; set; }
[DisplayName("PersonnelType")]
public PersonnelType IndividualOrBulk { get; set; }
// . . .
}
I have the following class:
public class Widget {
public virtual int Id { get; set; }
[Required]
public virtual WidgetType Type { get; set; }
public virtual string SerializedParameters {
get {
return new XmlSerializer(Parameters.GetType()).Serialize(Parameters);
} set {
Parameters = new XmlSerializer(Assembly
.LoadFrom(Server.MapPath(Type.ModelAssembly))
.GetType(Type.ModelClass)
).Deserialize(value);
}
}
private object _parameters;
public virtual object Parameters {
get {
if (_parameters == null)
_parameters = Activator.CreateInstance(Assembly
.LoadFrom(Server.MapPath(Type.ModelAssembly))
.GetType(Type.ModelClass)
);
return _parameters;
} set { _parameters = value; }
}
}
The Parameters property is not mapped to the database but the SerializedParameters property is. However when it tries to get the information from the database and set the SerializedParameters (which subsequently sets the Parameters) the Type property is null and therefore an exception is thrown. I guess this depends on the order in which NHibernate sets the properties for the Widget but i can't get it to work.
I was wondering if there was a way around this. Appreciate the help. Thanks
moved the deserialization in the getter from the setter because as you said the type is not there yet
public class Widget
{
public virtual int Id { get; set; }
public virtual WidgetType Type { get; set; }
private string _serializedParameters;
private virtual string SerializedParameters {
get
{
return new XmlSerializer(Parameters.GetType()).Serialize(Parameters);
}
set
{
_serializedParameters = value;
}
}
private object _parameters;
public virtual object Parameters
{
get
{
if (_parameters == null)
{
if (!string.IsNullOrEmpty(serializedParameters))
{
// code to deserialize the Parameters and set to Parameters
_parameters = new XmlSerializer(Assembly
.LoadFrom(Server.MapPath(Type.ModelAssembly))
.GetType(Type.ModelClass)
).Deserialize(value);
}
else
{
// no existing parameters, then create new object
_parameters = Activator.CreateInstance(Assembly.LoadFrom(Server.MapPath("~/bin/" + widget.Type.ParametersAssembly + ".dll")).GetType(widget.Type.ParametersClass));
}
}
return _parameters;
}
set { _parameters = value; }
}
}
Say I have a class like this:
public class MyClass
{
public int Id { get; set; }
public DateTime Date { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string String4 { get; set; }
}
Is it possible to get NHibernate to store it in the following schema?
CREATE TABLE [dbo].[MyClass](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Xml] [varchar](max) NOT NULL,
)
Where the Id maps to Id, but then all other fields get serialized into XML (or otherwise)? I don't mind if these other fields have to go on a child object like the below, if that helps:
public class MyClass
{
public int Id { get; set; }
public AllOtherOptions Options { get; set; }
}
public class AllOtherOptions
{
public DateTime Date { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string String4 { get; set; }
}
I am thinking about doing something similar for an upcoming project. The project requires collecting a lot of data but only a few elements need to be stored in a relational database. I haven't started experimenting but these are my thoughts so far.
You can map an XML data type by creating a type that implements IUserType. If the child class (AllOtherOptions) is serializable, you should be able to map the XML field as a private member in MyClass and serialize/deserialize AllOtherOptions as needed. You could either dynamically maintain the XML field (sounds like a lot of work) or create an interceptor to do it. My thinking is that MyClass would implement an interface such as
public interface IXmlObjectContainer
{
void SerializeChildObjects();
void DeSerializeChildObjects();
}
and the interceptor would call those methods as needed. That's a proof of concept idea. I would probably refine that by exposing pairs of xml fields and serializable objects to remove the work of serializing from IXmlObjectContainer implementers. Or maybe handle serialization through the XML field's get/set accessors.
More info:
Working with XML Fields in NHibernate
Another XML implementation of IUserType
I had the same idea to save object in XML column. My idea was other. I took code from links and changed it to generic IUserType implementation. So any field/prop which is [Serializable] can be saved in XML column.
public class XmlUserType<T> : IUserType where T : class
{
public new bool Equals(object x, object y)
{
return x == y;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
if (names.Length != 1)
throw new InvalidOperationException("names array has more than one element. can't handle this!");
var val = rs[names[0]] as string;
if (string.IsNullOrWhiteSpace(val) == false)
{
return KRD.Common.GenericXmlSerialization.Deserialize<T>(val);
}
return null;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var parameter = (DbParameter)cmd.Parameters[index];
T toSave = value as T;
if (toSave != null)
{
parameter.Value = KRD.Common.GenericXmlSerialization.Serialize(toSave);
}
else
{
parameter.Value = DBNull.Value;
}
}
public object DeepCopy(object value)
{
T toCopy = value as T;
if (toCopy == null)
return null;
string serialized = KRD.Common.GenericXmlSerialization.Serialize(toCopy);
return KRD.Common.GenericXmlSerialization.Deserialize<T>(serialized);
}
public object Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
public object Assemble(object cached, object owner)
{
var str = cached as string;
if (string.IsNullOrWhiteSpace(str) == false)
{
return null;
}
return KRD.Common.GenericXmlSerialization.Deserialize<T>(str);
}
public object Disassemble(object value)
{
var toCache = value as T;
if (toCache != null)
{
return KRD.Common.GenericXmlSerialization.Serialize(toCache);
}
return null;
}
public SqlType[] SqlTypes
{
get
{
return new SqlType[] { new SqlXmlType() };
}
}
public Type ReturnedType
{
get { return typeof(XmlDocument); }
}
public bool IsMutable
{
get { return true; }
}
}
public class SqlXmlType : SqlType
{
public SqlXmlType()
: base(DbType.Xml)
{
}
}
Usage with FluentNHibernate:
public class MainObject
{
public int Id { get; set; }
public ObjectAsXml Data { get; set; }
}
public class ObjectAsXml
{
public string Name { get; set; }
public int Date { get; set; }
public ObjectAsXml OtherObject { get; set; }
}
private class MainObjectMap : ClassMap<MainObject>
{
public MainObjectMap()
{
Id(id => id.Id);
Map(m => m.Data).CustomType<XmlUserType<ObjectAsXml>>().Nullable();
}
}
Maybe it will help somebody.