Lets say you have a complex type:
public class Identity<TEntity> : IEquatable<Identity<TEntity>>
{
public Identity(Guid value)
{
Value = value;
}
public Guid Value { get; }
public static implicit operator Guid(Identity<TEntity> identity)
{
return identity.Value;
}
public static explicit operator Identity<TEntity>(Guid value)
{
return new Identity<TEntity>(value);
}
}
How to configure a type using this complex type as Id, e.g.
public class MyEntity
{
public Identity<TEntity> Id { get; }
}
within a type configuration for ef core?
For example a type configuration like:
public class MyEntityTypeConfiguration : IEntityTypeConfiguration<MyEntity>
{
public void Configure(EntityTypeBuilder<MyEntity> builder)
{
var converter = new ValueConverter<Identity<MyEntity>, Guid>(
v => v.Value,
v => new Identity<MyEntity>(v));
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.ValueGeneratedOnAdd()
.HasConversion(converter);
}
}
will produce an empty Guid (no values generated).
You have a constructor which set the Value. In order to get a value you need to set a value like below.
Identity<Gibra> identity = new Identity<Gibra>(Guid.NewGuid());
Or you have a empty constructor
public class Identity<TEntity> : IEquatable<Identity<TEntity>>
{
Guid _value = Guid.NewGuid();
public Identity(Guid value)
{
Value = value;
}
public Identity()
{
}
public Guid Value { get { return _value; } set { _value = value; } }
public bool Equals(Identity<TEntity> other)
{
throw new NotImplementedException();
}
public static implicit operator Guid(Identity<TEntity> identity)
{
return identity.Value;
}
public static explicit operator Identity<TEntity>(Guid value)
{
return new Identity<TEntity>(value);
}
}
So that you can use like this
Identity<Gibra> identity = new Identity<Gibra>();
Related
I am trying to create migration but don't not how to configure an Entity Framework to work with property with custom type.
My ValueObject BrandName class:
public class BrandName : ValueObject<BrandName>
{
public string Value { get; }
private BrandName(string name)
{
Value = name;
}
public static Result<BrandName> Create(string brandName)
{
brandName = (brandName ?? string.Empty).Trim();
if (String.IsNullOrEmpty(brandName))
{
return Result.Fail<BrandName>(BrandValidationValues.BrandNameEmpty);
}
if (brandName.Length > BrandValidationValues.BrandNameLength)
{
return Result.Fail<BrandName>(BrandValidationValues.BrandNameTooLong);
}
return Result.Ok(new BrandName(brandName));
}
protected override bool EqualsCore(BrandName other)
{
return Value.Equals(other.Value, StringComparison.InvariantCultureIgnoreCase);
}
protected override int GetHashCodeCore()
{
return Value.GetHashCode();
}
public static implicit operator string(BrandName brandName)
{
return brandName.Value;
}
public static explicit operator BrandName(string brandName)
{
return Create(brandName).Value;
}
}
My Entity Brand class:
public class CatalogBrand : Entity
{
private string _name;
public virtual BrandName Name
{
get => (BrandName)_name;
set => _name = value;
}
}
Configuration for Entity Framework:
class BrandEntityTypeConfiguration
: IEntityTypeConfiguration<CatalogBrand>
{
public void Configure(EntityTypeBuilder<CatalogBrand> builder)
{
builder.ToTable("Brand");
builder.HasKey(ci => ci.Id);
builder.Property(ci => ci.Id)
.UseHiLo("brand_hilo")
.IsRequired();
builder.Property(cb => cb.Name)
.IsRequired()
.HasMaxLength(100);
}
}
Error which I get:
The property 'CatalogBrand.Name' is of type 'BrandName' which is not supported by the current database provider. Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
By default, PropertyGrid only allowed editing properties with public setter. I'd like to allow editing of properties without a setter.
For example:
class A {
public int X {get;set}
public int Y {get;}
}
In the example above, only X will be editable. Y will be displayed but grayed out. How can I make Y editable?
Note: making a private backing field would be OK. For example:
class A {
public int X {get;set}
private int y;
public int Y {get => y; }
}
You can build a wrapper/proxy class based on the ICustomTypeDescriptor Interface that allows you to tweak properties at runtime.
This is how you could use it:
var a = new A();
// build a proxy
var proxy = new Proxy(a);
// tweak any properties
proxy.Properties["Y"].IsReadOnly = false;
// you can also tweak attributes
proxy.Properties["Y"].Attributes.Add(new CategoryAttribute("R/O -> R/W"));
proxy.Properties["Y"].Attributes.Add(new DescriptionAttribute("This works"));
// handle property change
propertyGrid1.PropertyValueChanged += (s, e) =>
{
if (e.ChangedItem.PropertyDescriptor.Name == "Y")
{
a.Y = (int)e.ChangedItem.Value;
}
};
// select the proxy instead of the original instance
propertyGrid1.SelectedObject = proxy;
And here is the result
...
class A
{
public int X { get; set; }
public int Y { get; internal set; }
}
...
public class Proxy : ICustomTypeDescriptor
{
public Proxy(object instance)
{
if (instance == null)
throw new ArgumentNullException(nameof(instance));
Instance = instance;
Properties = TypeDescriptor.GetProperties(instance).OfType<PropertyDescriptor>().Select(d => new ProxyProperty(instance, d)).ToDictionary(p => p.Name);
}
public object Instance { get; }
public IDictionary<string, ProxyProperty> Properties { get; }
public AttributeCollection GetAttributes() => TypeDescriptor.GetAttributes(Instance);
public string GetClassName() => TypeDescriptor.GetClassName(Instance);
public string GetComponentName() => TypeDescriptor.GetComponentName(Instance);
public TypeConverter GetConverter() => TypeDescriptor.GetConverter(Instance);
public EventDescriptor GetDefaultEvent() => TypeDescriptor.GetDefaultEvent(Instance);
public object GetEditor(Type editorBaseType) => TypeDescriptor.GetEditor(Instance, editorBaseType);
public EventDescriptorCollection GetEvents() => TypeDescriptor.GetEvents(Instance);
public EventDescriptorCollection GetEvents(Attribute[] attributes) => TypeDescriptor.GetEvents(Instance, attributes);
public PropertyDescriptor GetDefaultProperty() => TypeDescriptor.GetDefaultProperty(Instance);
public PropertyDescriptorCollection GetProperties() => new PropertyDescriptorCollection(Properties.Values.Select(p => new Desc(this, p)).ToArray());
public PropertyDescriptorCollection GetProperties(Attribute[] attributes) => GetProperties();
public object GetPropertyOwner(PropertyDescriptor pd) => Instance;
private class Desc : PropertyDescriptor
{
public Desc(Proxy proxy, ProxyProperty property)
: base(property.Name, property.Attributes.ToArray())
{
Proxy = proxy;
Property = property;
}
public Proxy Proxy { get; }
public ProxyProperty Property { get; }
public override Type ComponentType => Proxy.GetType();
public override Type PropertyType => Property.PropertyType ?? typeof(object);
public override bool IsReadOnly => Property.IsReadOnly;
public override bool CanResetValue(object component) => Property.HasDefaultValue;
public override object GetValue(object component) => Property.Value;
public override void ResetValue(object component) { if (Property.HasDefaultValue) Property.Value = Property.DefaultValue; }
public override void SetValue(object component, object value) => Property.Value = value;
public override bool ShouldSerializeValue(object component) => Property.ShouldSerializeValue;
}
}
public class ProxyProperty
{
public ProxyProperty(string name, object value)
{
if (name == null)
throw new ArgumentNullException(nameof(value));
Name = name;
Value = value;
Attributes = new List<Attribute>();
}
public ProxyProperty(object instance, PropertyDescriptor descriptor)
{
if (descriptor == null)
throw new ArgumentNullException(nameof(descriptor));
Name = descriptor.Name;
Value = descriptor.GetValue(instance);
var def = descriptor.Attributes.OfType<DefaultValueAttribute>().FirstOrDefault();
if (def != null)
{
HasDefaultValue = true;
DefaultValue = def.Value;
}
IsReadOnly = (descriptor.Attributes.OfType<ReadOnlyAttribute>().FirstOrDefault()?.IsReadOnly).GetValueOrDefault();
ShouldSerializeValue = descriptor.ShouldSerializeValue(instance);
Attributes = descriptor.Attributes.Cast<Attribute>().ToList();
PropertyType = descriptor.PropertyType;
}
public string Name { get; }
public object Value { get; set; }
public object DefaultValue { get; set; }
public bool HasDefaultValue { get; set; }
public bool IsReadOnly { get; set; }
public bool ShouldSerializeValue { get; set; }
public Type PropertyType { get; set; }
public IList<Attribute> Attributes { get; }
}
There is a way to do this, but it is absurdly complex;
you create a custom TypeDescriptionProvider and link it to the type, or implement ICustomTypeDescriptor in the type, and
you create a custom PropertyDescriptor that knows how to edit the field in the GetValue and SetValue
you create a custom TypeDescriptor that echos the standard property descriptors for most things, and your new property descriptor in this case
In all seriousness, though; please don't do this! Just make the property accessible in the first place. If it isn't a settable property, you shouldn't be trying to set it.
based on the comments it sounds like what you actually need is "popsicle immutability"; consider:
class Foo {
private bool _frozen;
public void Freeze() => _frozen = true;
protected void ThrowIfFrozen() {
if (_frozen) throw new InvalidOperationException(
"The object cannot be changed once Freeze has been called");
}
private int _x, _y;
public int X {
get => _x;
set {
if (value != _x) {
ThrowIfFrozen();
_x = value;
}
}
}
public int Y {
get => _y;
set {
if (value != _y) {
ThrowIfFrozen();
_y = value;
}
}
}
}
I get the exception message:
The binary operator NotEqual is not defined for the types 'NotificationArea' and 'System.Object'
Source enum:
public enum NotificationArea
{
One,
Two,
Three
}
Destination class:
public class EnumValue
{
public EnumValue()
{
}
public EnumValue(Enum tEnum)
{
Id = Convert.ToInt32(tEnum);
Name = GetEnumValue(tEnum, tEnum.GetType());
}
public int Id { get; set; }
public string Name { get; set; }
public string GetEnumValue(Enum tEnum, Type type)
{
MemberInfo member = type.GetMember(tEnum.ToString())[0];
if (member.GetCustomAttribute<DisplayAttribute>() != null)
{
DisplayAttribute attribute = member.GetCustomAttribute<DisplayAttribute>();
return attribute.Name;
}
return tEnum.ToString();
}
}
Conversion class:
public class EnumConverter : IMemberValueResolver<Notification,
NotificationModel, NotificationArea, EnumValue>
{
public EnumValue Resolve(Notification source, NotificationModel destination,
NotificationArea sourceMember, EnumValue destMember, ResolutionContext context)
{
var model = source.Area.ToDisplay();
return model;
}
}
ToDisplay Extension:
public static EnumValue ToDisplay(this Enum value)
{
var display = new EnumValue(value);
return display;
}
Implementation:
// Query
var notifications = await _db.Notifications
.OrderByDescending(x => x.Timestamp)
.ProjectTo<NotificationModel>(_mapperConfig)
.ToListAsync(token);
// Mapping
CreateMap<Notification, NotificationModel>()
.ForMember(dest => dest.Area, opt => opt.ResolveUsing(src => src.Area));
Source and Destination Models:
public class Notification
{
public int Id {get;set;}
public NotificationArea Area { get; set; }
}
public class NotificationModel
{
public int Id {get;set;}
public EnumValue Area {get;set;}
}
I know I'm doing something wrong... obviously... I just don't know exactly where it's at. I feel Like I have A and C but am missing B.
I have a class which stores value.
public class Entry<T>
{
private T _value;
public Entry() { }
public Entry(T value)
{
_value = value;
}
public T Value
{
get { return _value; }
set { _value = value; }
}
// overload set operator.
public static implicit operator Entry<T>(T value)
{
return new Entry<T>(value);
}
}
To utilize this class:
public class Exam
{
public Exam()
{
ID = new Entry<int>();
Result = new Entry<int>();
// notice here I can assign T type value, because I overload set operator.
ID = 1;
Result = "Good Result.";
// this will throw error, how to overload the get operator here?
int tempID = ID;
string tempResult = Result;
// else I will need to write longer code like this.
int tempID = ID.Value;
string tempResult = Result.Value;
}
public Entry<int> ID { get; set; }
public Entry<string> Result { get; set; }
}
I'm able to overload the set operator which I can straightaway do "ID = 1".
But when I do "int tempID = ID;", it will throw error.
How to overload the get operator so I can do "int tempID = ID;" instead of "int tempID = ID.Value;"?
Simple, add another implicit operator but for the other direction!
public class Entry<T>
{
private T _value;
public Entry() { }
public Entry(T value)
{
_value = value;
}
public T Value
{
get { return _value; }
set { _value = value; }
}
public static implicit operator Entry<T>(T value)
{
return new Entry<T>(value);
}
public static implicit operator T(Entry<T> entry)
{
return entry.Value;
}
}
And usage is a breeze:
void Main()
{
Entry<int> intEntry = 10;
int val = intEntry;
}
I'm attempting to create a mapper so PetaPoco can hydrate and persist POCOs with Enumeration class properties. See more about Enumeration classes here or here.
For instance, Take this class.
public class PetType : Headspring.Enumeration<PetType>
{
public static readonly PetType Frog = new PetType(1, "Frog");
public static readonly PetType Cat = new PetType(2, "Cat");
public static readonly PetType Fish = new PetType(3, "Fish");
public static readonly PetType Dog = new PetType(4, "Dog");
private PetType(int value, string displayName) : base(value, displayName) { }
}
Which can be used like so:
var MyPet = PetType.Dog;
Here is the Poco I want to hydrate/persist with the database:
public class Pet
{
public int ID { get; set; }
public string OwnerName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PetName{ get; set; }
public PetType PetType{ get; set; }
}
I have designed a custom mapper that will work with PetType:
class EnumClassMapper : PetaPoco.StandardMapper
{
public override Func<object, object> GetFromDbConverter(System.Reflection.PropertyInfo targetProperty, Type sourceType)
{
if (targetProperty.PropertyType == typeof(PetType))
{
return (x) => PetType.FromValue((int) x);
}
return base.GetFromDbConverter(targetProperty, sourceType);
}
public override Func<object, object> GetToDbConverter(System.Reflection.PropertyInfo sourceProperty)
{
if (sourceProperty.PropertyType == typeof(PetType))
{
return (x) => ((PetType)x).Value;
}
return base.GetToDbConverter(sourceProperty);
}
}
However suppose I create another Enumeration subclass for disposition.
public class Disposition: Headspring.Enumeration<Disposition>
{
public static readonly Friendly = new Disposition(1, "Friendly");
public static readonly Timid = new Disposition(2, "Timid");
public static readonly Aggressive = new Disposition(3, "Aggressive");
private Disposition(int value, string displayName) : base(value, displayName) { }
}
I don't want to have to update my mapper every time I create a new subclass of the Enumeration class. I prefer that the mapping code could recognize that the property type is a descendent of the Enumeration class, and map accordingly. I assume the answer is to make use of reflection, but I don't know how to proceed.
What about
public class EnumClassMapper<T> : PetaPoco.StandardMapper
where T : Headspring.Enumeration<T>
{
public override Func<object, object> GetFromDbConverter(System.Reflection.PropertyInfo targetProperty, Type sourceType)
{
return (x) => Enumeration<T, int>.FromValue((int) x);
}
public override Func<object, object> GetToDbConverter(System.Reflection.PropertyInfo sourceProperty)
{
return (x) => ((T)x).Value;
}
}
var builder = DatabaseConfiguration.Build()
.UsingConnectionStringName("sqlite")
.UsingDefaultMapper<ConventionMapper>(m =>
{
m.FromDbConverter = (targetProperty, sourceType) =>
{
if (targetProperty == null)
return null;
var t = targetProperty.PropertyType;
if (t.BaseType == null || ! t.BaseType.IsGenericType)
return null;
if (t.BaseType.GetGenericTypeDefinition() != typeof(Headspring.Enumeration<>))
return null;
return ((IMapper)Activator.CreateInstance(typeof(EnumClassMapper<>).MakeGenericType(t))).GetFromDbConverter(targetProperty, sourceType);
};
m.ToDbConverter = sourceProperty =>
{
if (sourceProperty == null)
return null;
var t = sourceProperty.PropertyType;
if (t.BaseType == null || !t.BaseType.IsGenericType)
return null;
if (t.BaseType.GetGenericTypeDefinition() != typeof(Headspring.Enumeration<>))
return null;
return ((IMapper)Activator.CreateInstance(typeof(EnumClassMapper<>).MakeGenericType(t))).GetToDbConverter(sourceProperty);
};
});
var db = builder.Create();