I have a mapping like this.
public class MyObjectMap : ClassMap<MyObject> {
public MyObjectMap()
{
Component(_ => _.MyItem, key =>
{
key.Map(x => x.MyItemValue).Column("COL");
/** I want to set this value to a particular enum in this mapper **/
key.Map(x => x.MyItemType).AssignSomeValue(MyEnum.MyValueType)
});
}
}
How do I set the value to some particular item type. It is a component of a particular type.
IUserType can do this
class ConstantValueUserType : IUserType
{
NullSafeGet(IDataReader rd, string[] names, object owner)
{
return 5; // Constant Value
}
public object NullSafeSet(ICommand cmd, object value, int index)
{
// empty, we dont want to write
}
public SqlType[] SqlTypes { get { return new SqlType[0]; } }
}
key.Map(x => x.MyItemType).ReadOnly().Formula(((int)MyEnum.MyValueType).ToString()).CustomType<int>();
Related
In short I wanted to create a custom IUserType to represent IPAddress from .NET (as an inet type in postgresql) and to be able to query it with custom functions via HQL and Linq. I have trouble with implementing a custom function for Linq.
What I have thus far:
A) I am able to map it:
public class SomeEntityMapper : ClassMap<SomeEntity> {
public SomeEntityMapper() {
...
Map(x => x.IpAddressField)
.CustomSqlType("inet")
.CustomType<IPAddressUserType>()
...
}
}
IUserType implementation below:
[Serializable]
public class IPAddressUserType : IUserType
{
public new bool Equals(object x, object y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
return x.Equals(y);
}
public int GetHashCode(object x)
{
if (x == null)
return 0;
return x.GetHashCode();
}
public object NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
{
if (names.Length == 0)
throw new InvalidOperationException("Expected at least 1 column");
if (rs.IsDBNull(rs.GetOrdinal(names[0])))
return null;
object value = rs[names[0]];
return value;
}
public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session)
{
NpgsqlParameter parameter = (NpgsqlParameter) cmd.Parameters[index];
parameter.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Inet;
if (value == null)
{
parameter.Value = DBNull.Value;
}
else
{
parameter.Value = value;
}
}
public object DeepCopy(object value)
{
if (value == null)
return null;
IPAddress copy = IPAddress.Parse(value.ToString());
return copy;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
if (cached == null)
return null;
if (IPAddress.TryParse((string)cached, out var address))
{
return address;
}
return null;
}
public object Disassemble(object value)
{
if (value == null)
return null;
return value.ToString();
}
public SqlType[] SqlTypes => new SqlType[] { new NpgsqlSqlType(DbType.String, NpgsqlTypes.NpgsqlDbType.Inet), };
public Type ReturnedType => typeof(IPAddress);
public bool IsMutable => false;
}
public class NpgsqlSqlType : SqlType
{
public NpgsqlDbType NpgDbType { get; }
public NpgsqlSqlType(DbType dbType, NpgsqlDbType npgDbType)
: base(dbType)
{
NpgDbType = npgDbType;
}
public NpgsqlSqlType(DbType dbType, NpgsqlDbType npgDbType, int length)
: base(dbType, length)
{
NpgDbType = npgDbType;
}
public NpgsqlSqlType(DbType dbType, NpgsqlDbType npgDbType, byte precision, byte scale)
: base(dbType, precision, scale)
{
NpgDbType = npgDbType;
}
}
}
B) I am able to query it using HQL and a custom function (inet_equals) I implemented.
using(var session = _factory.OpenSession()) {
var q = session.CreateQuery("from SomeEntity as s WHERE inet_equals(s.IpAddressField, :ip)");
q.SetParameter("ip", IPAddress.Parse("4.3.2.1"), NHibernateUtil.Custom(typeof(IPAddressUserType)));
}
Custom Postgresql Dialect extending the HQL functions implementation below:
public class CustomPostgresqlDialect : PostgreSQL83Dialect
{
public CustomPostgresqlDialect()
{
RegisterFunction("inet_equals", new SQLFunctionTemplate(NHibernateUtil.Boolean, "(?1::inet = ?2::inet)"));
}
}
C) I want to be able to query it using LINQ like so:
using(var session = _factory.OpenSession()) {
var q = session.Query<SomeEntity>()
.Where(s => s.IpAddressField.InetEquals(IPAddress.Parse("4.3.2.1")));
}
Custom LINQ Generator for NHibernate provider below:
public static class InetExtensions
{
public static bool InetEquals(this IPAddress value, IPAddress other)
{
throw new NotSupportedException();
}
}
public class InetGenerator : BaseHqlGeneratorForMethod
{
public InetGenerator()
{
SupportedMethods = new[]
{
ReflectHelper.GetMethodDefinition(() => InetExtensions.InetEquals(default(IPAddress), default(IPAddress)))
};
}
public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
HqlExpression lhs = visitor.Visit(arguments[0]).AsExpression();
HqlExpression rhs = visitor.Visit(arguments[1]).AsExpression();
return treeBuilder.BooleanMethodCall(
"inet_equals",
new[]
{
lhs,
rhs
}
);
}
}
public class ExtendedLinqToHqlGeneratorsRegistry :
DefaultLinqToHqlGeneratorsRegistry
{
public ExtendedLinqToHqlGeneratorsRegistry()
: base()
{
this.Merge(new InetGenerator());
}
}
Unfortunately when using LINQ I get this exception:
HibernateException: Could not determine a type for class: System.Net.IPAddress
Interestingly, this is the exact same error I get if I omit the NHibernateUtil.Custom(typeof(IPAddressUserType)) parameter inside the HQL query.
Which leads me to believe I am on the right track, but I can't figure out what I may be missing exactly. I assume I need to inform NHibernate inside the Generator that this is a custom UserType (just like I did with the HQL query via the NHibernateUtil.Custom(typeof(IPAddressUserType)) parameter.
I found the solution.
To hint NHibernate to use the proper UserType use: MappedAs extension method, like so:
using(var session = _factory.OpenSession()) {
var q = session.Query<SomeEntity>()
.Where(s => s.IpAddressField.InetEquals(
IPAddress.Parse("4.3.2.1").MappedAs(NHibernateUtil.Custom(typeof(IPAddressUserType))
);
}
The year is 2019 and I'm still stuck on version 3.3.2.GA of NHibernate and the MappedAs extension method exists only on version 4.x onwards.
My scenario was the need to pass a large string as a parameter to a supported method in an HqlGeneratorForMethod.
I created the following class to store any large string to use as a parameter of any method throughout the application:
public class StringClob
{
public StringClob()
{
}
public StringClob(string value)
{
Value = value;
}
public virtual string Value { get; protected set; }
}
In order to link the NHibernateUtil.StringClob type with the string value I had the idea to create a mapping for my class without informing table mapping just the property (I use FluentNHibernate to map class):
public class StringClobMap : ClassMap<StringClob>
{
public StringClobMap()
{
Id(x => x.Value, "VALUE").CustomType("StringClob").CustomSqlType("VARCHAR(MAX)").Length(int.MaxValue / 2);
}
}
And now hypothetically following the example of #krdx the usage would look like this:
using(var session = _factory.OpenSession())
{
var q = session.Query<SomeEntity>()
.Where(s => s.IpAddressField.InetEquals(new StringClob("<large_string_here>")));
// ...
}
Therefore by passing the StringClob class as a parameter, NHibernate gets the custom type defined in the mapping.
I hope I helped those who still use NHibernate 3.3.2.GA.
I'm trying to serialize this type of object using protobuf-net:
[ProtoContract]
public class RedisDataObject
{
[ProtoMember(1)]
public string DataHash;
[ProtoMember(2, DynamicType = true)]
public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
}
[Serializable]
public enum ContextActions
{
Insert,
Update,
Delete
}
I'm using List<object> because I'm storing there different class instances of other classes I have in my code.
But I'm getting this error message:
Unable to resolve a suitable Add method for System.Collections.Generic.Dictionary...
This is clearly because of the dictionary, but I couldn't find a solution how to resolve this issue.
Your basic problem is that DynamicType = true applies to that specific property only, and serializes type information for the value of that specific property only. It doesn't apply recursively to any of the properties contained by that object. However, your object value is nested deep within several levels of container:
public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
What you need to do is to serialize type information for each object inside this dictionary of tuples of lists. You can do this by introducing a surrogate value type:
[ProtoContract]
public struct DynamicTypeSurrogate<T>
{
[ProtoMember(1, DynamicType = true)]
public T Value { get; set; }
}
public static class DynamicTypeSurrogateExtensions
{
public static List<DynamicTypeSurrogate<T>> ToSurrogateList<T>(this IList<T> list)
{
if (list == null)
return null;
return list.Select(i => new DynamicTypeSurrogate<T> { Value = i }).ToList();
}
public static List<T> FromSurrogateList<T>(this IList<DynamicTypeSurrogate<T>> list)
{
if (list == null)
return null;
return list.Select(i => i.Value).ToList();
}
}
And then modifying your RedisDataObject to serialize a surrogate dictionary as follows:
[ProtoContract]
public class RedisDataObject
{
[ProtoMember(1)]
public string DataHash;
[ProtoIgnore]
public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
[ProtoMember(2)]
private Dictionary<ContextActions, List<Tuple<string, List<DynamicTypeSurrogate<object>>>>> SurrogateValue
{
get
{
if (Value == null)
return null;
var dictionary = Value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.ToSurrogateList())).ToList()));
return dictionary;
}
set
{
if (value == null)
Value = null;
else
{
Value = value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.FromSurrogateList())).ToList()));
}
}
}
}
Note also the restrictions on DynamicType mentioned here:
DynamicType - stores additional Type information with the type (by default it includes the AssemblyQualifiedName, although this can be controlled by the user). This makes it possible to serialize weak models, i.e. where object is used for property members, however currently this is limited to contract types (not primitives), and does not work for types with inheritance (these limitations may be removed at a later time). Like with AsReference, this uses a very different layout format
While the documentation above exists at the former project site and has not been moved to the current site, the restriction on non-contract types definitely still exists as of version 2.0.0.668. (I tested that adding an int value to the List<object> fails; I have not checked whether the restriction on inheritance still exists.)
With help from dbc, and all links mentioned in his answer and comments
[ProtoContract]
[ProtoInclude(1, typeof(ObjectWrapper<int>))]
[ProtoInclude(2, typeof(ObjectWrapper<decimal>))]
[ProtoInclude(3, typeof(ObjectWrapper<DateTime>))]
[ProtoInclude(4, typeof(ObjectWrapper<string>))]
[ProtoInclude(5, typeof(ObjectWrapper<double>))]
[ProtoInclude(6, typeof(ObjectWrapper<long>))]
[ProtoInclude(8, typeof(ObjectWrapper<Custom>))]
[ProtoInclude(9, typeof(ObjectWrapper<CustomType[]>))]
public abstract class ObjectWrapper
{
protected ObjectWrapper() { }
abstract public object ObjectValue { get; set; }
public static ObjectWrapper Create(object o) {
Type objectType = o.GetType();
Type genericType = typeof(ObjectWrapper<>);
Type specializedType = genericType.MakeGenericType(objectType);
return (ObjectWrapper)Activator.CreateInstance(specializedType, new object[] { o });
}
}
Downside is you have to register all types you are using in your list of objects. Every time a new type that is not included in the ProtoInclude series surfaces out, you would get an InvalidOperationException with message Unexpected sub-type: ObjectWrapper`1[[NewType]].
[ProtoContract]
public class RedisDataObjectWrapper {
[ProtoMember(1)] public string DataHash;
[ProtoIgnore] public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
[ProtoMember(2)]
private Dictionary<ContextActions, List<Tuple<string, List<ObjectWrapper>>>> AdaptedValue {
get {
if (Value == null) return null;
var dictionary = Value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>ObjectWrapper.Create(x)).ToList() )).ToList()));
return dictionary;
}
set {
if (value == null) Value = null;
else {
Value = value.ToDictionary(
p => p.Key,
p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>x.ObjectValue).ToList() )).ToList()));
} } } }
I have a database which is accessed by multiple systems. Ours reads a table using Fluent Nhibernate and Automapping.
One of the columns is a string representing an enum. In most installations this is fine, but every now and then another system will persist an invalid value. How can I catch these invalid values and either convert or ignore them?
Example:
Database Table:
ID MyEnum
0 "One"
1 "Two"
2 "Invalid"
MyEnum
{
One,
Two
}
I'm already using IAutoMappingOverride with my model, is there anyway to pass a function to the mapper to do validation/conversion (say I wanted to convert any unknowns to MyEnum.One)?
Or is there a simpler way that I'm missing?
Where I got to was using a combination of #Firo's answer and the answers here and here
class SafeEnumType<T> : ImmutableUserType where T : struct
{
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
T value;
if (Enum.TryParse((string)rs[names[0]], out value))
return value;
else
return default(T);
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.String.NullSafeSet(cmd, value.ToString(), index);
}
public override Type ReturnedType
{
get { return typeof(T); }
}
public override SqlType[] SqlTypes
{
get { return new[] { SqlTypeFactory.GetString(100) }; }
}
}
and adding
mapping.Map(x => x.Type).CustomType(typeof(SafeEnumType<EventType>));
to my AutomapOverride.Override() fn
In NHibernate there is IUserType which lets you define custom conversion code. here is a base class of mine which you can use because enums are immutable types
code
class SpecialEnumUserType : ImmutableUserType
{
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
TheSpecialEnum value;
if (Enum.TryParse<TheSpecialEnum>((string)rs[names[0]], out value))
return value;
else
return default(TheSpecialEnum);
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.String.NullSafeSet(cmd, value.ToString(), index);
}
public override Type ReturnedType
{
get { return typeof(TheSpecialEnum); }
}
public override SqlType[] SqlTypes
{
get { return new[] { SqlTypeFactory.GetString(50) }; }
}
}
and convention to apply them to every enum property
class EnumHandlingConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Type == typeof(TheSpecialEnum))
{
instance.CustomType<SpecialEnumUserType>();
}
}
}
My question is somewhat similar to
Generic List of Generic Interfaces not allowed, any alternative approaches?
If I have an interface such as
public interface IPrimitive
{
}
public interface IPrimitive<T> : IPrimitive
{
T Value { get; }
}
public class Star : IPrimitive<string> //must declare T here
{
public string Value { get { return "foobar"; } }
}
public class Sun : IPrimitive<int>
{
public int Value { get { return 0; } }
}
Then I have a list
var myList = new List<IPrimitive>();
myList.Add(new Star());
myList.Add(new Sun());
When looping through this list, how do I get the Value property?
foreach (var item in myList)
{
var value = item.Value; // Value is not defined in IPrimitive so it doesn't know what it is
}
I'm not sure how this is possible.
Thanks,
Rob
You can take advantage of dynamic:
foreach (dynamic item in myList)
{
var value = item.Value;
}
The dynamic type enables the operations in which it occurs to bypass compile-time type checking. Instead, these operations are resolved at run time
You could do something like this:
public interface IPrimitive
{
object Value { get; }
}
public interface IPrimitive<T> : IPrimitive
{
new T Value { get; }
}
public class Star : IPrimitive<string> //must declare T here
{
public string Value { get { return "foobar"; } }
object IPrimitive.Value { get { return this.Value; } }
}
public class Sun : IPrimitive<int>
{
public int Value { get { return 0; } }
object IPrimitive.Value { get { return this.Value; } }
}
You're then able to get the value out as an object when you only have IPrimitive.
of course not, your value is going to be of different types..... so you will have to downcast to the real type to get at the different values.
Basically your interface is failing. Its not "A common interface" It's more a "similar interface"
If you don't want to do casting, then you will have to find an interface which is common to both of them.
You can move you Value property to base interface.
public interface IPrimitive
{
object Value { get; }
}
How do you want to procced value in the loop it has different type?
I have a property grid that I am using for users to be able to configure objects for any plugin that is written to be used in my application. I would like to be able to tell developers writing plugins to use the ComponentModel Attributes for their members like so:
[CategoryAttribute("On Screen Display Settings"),
DescriptionAttribute("Whether or not to show the session timer."),
DisplayName("Show Session Timer")]
public bool ShowTimer
{
get;
set;
}
This works great. Now I would like for the members of an enumeration to be able to be edited as well. i.e.
public enum Resolution_ : byte
{
DCIF,
CIF,
QCIF,
[DisplayName("4CIF")]
CIF4,
[DisplayName("2CIF")]
CIF2
}
So that they are displayed in the PropertyGrid's list like so:
DCIF
CIF
QCIF
CIF4
CIF2
Along with any Descriptions and Display names they may have with them.
It seems that I can only do this with properties, events, and methods. Does anyone know how I can do this for an enumeration?
You will have to make an EnumConverter class and decorate your property with a TypeConverter attribute in order to do this.
See this Using PropertyGrid in .NET, it's a fun example:
Imagine that you want more than two items in list. The boolean type is not enough; you need to set Description attributes with a name for every element in enum.
enum DrinkDoses {
[Description("Half of litre")]
litre,
[Description("One litre")]
oneLitre,
[Description("Two litres")]
twoLitre,
[Description("Three litres")]
threeLitres,
[Description("Four litres")]
fourLitres,
[Description("Death dose, five litres")]
fiveLitres
}
In another class you need to utilize the type EnumConverter.
class DrinkDosesConverter : EnumConverter {
private Type enumType;
public DrinkDosesConverter(Type type) : base(type) {
enumType = type;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destType) {
return destType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture,
object value, Type destType) {
FieldInfo fi = enumType.GetField(Enum.GetName(enumType, value));
DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
typeof(DescriptionAttribute));
if (dna != null)
return dna.Description;
else
return value.ToString();
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type srcType) {
return srcType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture,
object value) {
foreach (FieldInfo fi in enumType.GetFields()) {
DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
typeof(DescriptionAttribute));
if ((dna != null) && ((string)value == dna.Description))
return Enum.Parse(enumType, fi.Name);
}
return Enum.Parse(enumType, (string)value);
}
}
Third, you need set the attribute TypeConverter for displaying the property.
class DrinkerDoses {
DrinkDoses doses;
[DisplayName("Doses")]
[Description("Drinker doses")]
[Category("Alcoholics drinking")]
[TypeConverter(typeof(DrinkDosesConverter))]
public DrinkDoses Doses {
get { return doses; }
set { doses = value; }
}
int dataInt;
public int DataInt {
get { return dataInt; }
set { dataInt = value; }
}
}
You can attach a custom TypeConverter implementation to the property whose type is your enumeration and override the GetStandardValuesSupported and GetStandardValues to return a custom list of items to show in the drop-down list in the PropertyGrid. You can then override ConvertFrom/ConvertTo methods to handle converting values to/from your enumeration type.
You may also want to override GetStandardValuesExclusive and have it return "true" so the user can't type anything into the property value.
So, something like this:
public class MyTypeConverter : TypeConverter
{
//Override GetStandardValuesExclusive,
//GetStandardValues and GetStandardValuesSupported
}
public class SomeClass
{
[TypeConverter(typeof(MyTypeConverter))]
public Resolution SomePropertry
{
...
}
}
In your implementation of GetStandardValues/ConvertFrom/ConvertTo you could then use Reflection to pull out the DisplayNameAttribute (or DescriptionAttribute, which may be more suited to this task) attributes of the various enum members to show that text instead of hard-coding the list of items to show.
The answer I gave here Has a working example of this. Here is the specific code from that example that you want:
/// <summary>
/// This attribute is used to represent a string value
/// for a value in an enum.
/// </summary>
public class StringValueAttribute : Attribute {
#region Properties
/// <summary>
/// Holds the stringvalue for a value in an enum.
/// </summary>
public string StringValue { get; protected set; }
#endregion
#region Constructor
/// <summary>
/// Constructor used to init a StringValue Attribute
/// </summary>
/// <param name="value"></param>
public StringValueAttribute(string value) {
this.StringValue = value;
}
#endregion
}
public static class MyExtension
{
public static string GetStringValue(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute), false) as StringValueAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].StringValue : null;
}
public static String[] GetEnumNames(Type t)
{
Array enumValueArray= Enum.GetValues(t);
string[] enumStrings = new String[enumValueArray.Length];
for(int i = 0; i< enumValueArray.Length; ++i)
{
enumStrings[i] = GetStringValue((test)enumValueArray.GetValue(i));
}
return enumStrings;
}
}
enum test
{
[StringValue("test ONE")]
test1,
[StringValue("test TWO")]
test2
}
This also seems to work:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
{
public EnumDisplayNameAttribute(string data) : base(data) { }
}
public enum Resolution_ : byte
{
DCIF,
CIF,
QCIF,
[EnumDisplayName("4CIF")]
CIF4,
[EnumDisplayName("2CIF")]
CIF2
}
Components looking for a DisplayName attribute via Reflection will find one, and as far as I can tell this works. Is there a reason why this might be a bad idea?