Map value object as primary key in fluent nhibernate - c#

I am mapping my class ProcessingCode in fluent-nhibernate.
It has a property Code which is a ValueObject.
How can I map it as a primary key?
Here is how I am mapping it not as a primary key:
public class ProcessingCodeMap : ClassMap<ProcessingCode>
{
public ProcessingCodeMap()
{
Component(x => x.Code, p => p.Map(x => x.Value).Not.Nullable());
... other properties
}
}
Here are the classes that are relevant for the mapping:
public class ProcessingCode
{
public virtual Code Code { get; set; }
//Other properties...
}
public class Code : IEquatable<Code>
{
public Code()
{
}
public Code(string value)
{
Value = value;
}
public string Value { get; set; }
//Some other methods
public static implicit operator string(Code code)
{
if (code == null)
return null;
return code.Value;
}
public static implicit operator Code(string value)
{
return new Code(value);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Code)obj);
}
public bool Equals(Code other)
{
return string.Equals(Value, other.Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

Component method is used to map a value object as a regular property of the a class. There are two methods to map IDs: Id to map a regular types and CompositeId to map components. So, the answer is to use CompositeId instead of Component for an "easy" solution:
public class ProcessingCodeMap : ClassMap<ProcessingCode>
{
public ProcessingCodeMap()
{
CompositeId(x => x.Code, p => p.KeyProperty(x => x.Value));
//... other properties
}
}
Or alternatively you can implement custom IUserType.

Related

Make two classes that are dependent on each other equatable

I have class A:
public class A : IEquatable<A>
{
public B Owner { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as A);
}
public bool Equals([AllowNull] A other)
{
return other is A a &&
EqualityComparer<B>.Default.Equals(Owner, a.Owner);
}
}
And I have a class B:
public class B : IEquatable<B>
{
public List<A> Children { get; set; } = new List<A>();
public override bool Equals(object obj)
{
return Equals(obj as B);
}
public bool Equals([AllowNull] B other)
{
return other is B b &&
EqualityComparer<List<A>>.Default.Equals(Children, b.Children);
}
}
The problem I am having is making Equals() methods of the above classes work. The Equals() methods in the example are generated by VS Code, but always return false in case of class B.
I also tried using LINQ expressions (such as SequenceEqual method), but it always results in Stack Overflow (because of circular dependency?).
As a side note, I used .NET Core 3.0 to run this.
So, I managed to find the answer to my question. I just implemented my own custom IEqualityComparer. (in the example below I added public Guid ID property to both classes to do proper GetHashCode()).
public class BComparer : IEqualityComparer<B>
{
public bool Equals([AllowNull] B x, [AllowNull] B y)
{
if (x is null || y is null) {return false;}
if (x.ID == y.ID) {
return x.Children.SequenceEqual(y.Children);
} else {
return false;
}
}
public int GetHashCode([DisallowNull] B obj)
{
return obj.ID.ToString().GetHashCode();
}
}

Entity query criteria with static instance members

I'm trying to create an audit-trail like order state history table. This way, Orders could have many OrderStates, and a single State which points to the most recent history item. So far so good when saving an updating. The problems arise when I try to query as if I was using an enum:
public class OrderState
{
public static OrderState Placed = new OrderState("Placed", 1, 1);
public static OrderState Accepted = new OrderState("Accepted", 10, 2);
public static OrderState Cancelled = new OrderState("Cancelled", 20, 3);
public static OrderState Completed = new OrderState("Completed", 30, 4);
protected OrderState()
{
}
public OrderState(string name, int order, int id)
{
Name = name;
Order = order;
Id = id;
}
public int Id { get; set; }
public string Name { get; protected set; }
public int Order { get; protected set; }
public static bool operator == (OrderState state1, OrderState state2)
{
if (ReferenceEquals(state1, null))
{
return ReferenceEquals(state2, null);
}
return state1.Equals(state2);
}
public static bool operator !=(OrderState state1, OrderState state2)
{
return !(state1 == state2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (!(obj is OrderState))
{
return false;
}
return Equals((OrderState)obj);
}
public virtual bool Equals(OrderState other)
{
return other.Id.Equals(Id);
}
public override int GetHashCode()
{
unchecked
{
return ((Id.GetHashCode())*397) ^ Order;
}
}
}
Order class
public class Order
{
public Order()
{
Progress(OrderState.Placed);
}
public int Id { get; set; }
public virtual OrderState State
{
get { return States.OrderByDescending(x => x.State.Order).FirstOrDefault()?.State; }
}
public void Progress(OrderState state)
{
if (States.All(x => x.State != state))
{
States.Add(new OrderStateHistory()
{
Order = this,
State = state
});
}
}
public virtual ICollection<OrderStateHistory> States { get; set; } = new List<OrderStateHistory>();
}
In my code, things like these work fine:
order.Progress(OrderState.Accepted);, if (order.State == OrderState.Accepted)
However, what I'd like to get to is Where(x => x.State.Equals(OrderState.Accepted)) or Where(x => x.State == OrderState.Accepted)
Unfortunately, either of the criterias will yield an 'The specified type member 'State' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.' error.
I know I have been able to do this with NHibernate. Can I even do this with EF?
Since EF needs to translate your LINQ statements to SQL statements,
you can't do this. If you have complex comparison logic in your
overridden Equals() method you will have to duplicate that in the
LINQ statement.
public IQueryable<Foo> FoosEqualTo(IQueryable<Foo> allFoos, Foo target) {
return from foo in allFoos
where foo.Id == target.Id // or other comparison logic...
select foo;
}
public Foo getFoo(Foo target) {
return FoosEqualTo(DC.foos, target).FirstOrDefault();
}

Foreign key receiving two primary keys from the same table - Fluent Nhibernate

I am having a problem trying to create a Composite Id that receives one foreign key from one table and another foreign key from another table, but this second foreign key contains two primary keys and this giving my a big headache to resolve. Anyone knows how to resolve this problem?
Here is my code:
Entity
public class GrupoArquivo
{
public GrupoArquivo() {}
public GrupoArquivo(ArquivoRetorno arquivoRetorno, GrupoModulo grupo, GrupoModulo modulo) : this()
{
Arquivo = arquivoRetorno;
Grupo = grupo;
Modulo = modulo;
}
public virtual ArquivoRetorno Arquivo { get; protected set; }
public virtual GrupoModulo Grupo { get; protected set; }
public virtual GrupoModulo Modulo { get; protected set; }
public override bool Equals(object obj)
{
var grupoArquivo = (obj as GrupoArquivo);
if (grupoArquivo != null)
{
if (ReferenceEquals(obj, this))
return true;
var thisHash = GetHashCode();
var otherHash = grupoArquivo.GetHashCode();
return thisHash.Equals(otherHash);
}
return false;
}
public override int GetHashCode()
{
return string.Concat("{0}|{1}|{2}", Arquivo, Grupo, Modulo).GetHashCode();
}
}
Mapping
public class GrupoArquivoMap : ClassMap<GrupoArquivo>
{
public GrupoArquivoMap()
{
Schema(Const.SCHEMA);
Table(Const.TB_EMAIL_GRUPO_ARQUIVO);
CompositeId()
.KeyReference(x => x.Arquivo, Const.ID_ARQUIVO)
.KeyReference(x => x.Grupo, Const.ID_GRUPO)
.KeyReference(x => x.Modulo, Const.ID_MODULO)
;
}
}
I resolved it and it was quite "simple", I had to reference each table only once in the entity and in the mapping I defined the two collumns coming from the same table on the same "KeyReference".
Entity
public GrupoArquivo() {}
public GrupoArquivo(ArquivoRetorno arquivoRetorno, GrupoModulo grupoModulo) : this()
{
Arquivo = arquivoRetorno;
GrupoModulo = grupoModulo;
}
public virtual ArquivoRetorno Arquivo { get; protected set; }
public virtual GrupoModulo GrupoModulo { get; protected set; }
public override bool Equals(object obj)
{
var grupoArquivo = (obj as GrupoArquivo);
if (grupoArquivo != null)
{
if (ReferenceEquals(obj, this))
return true;
var thisHash = GetHashCode();
var otherHash = grupoArquivo.GetHashCode();
return thisHash.Equals(otherHash);
}
return false;
}
public override int GetHashCode()
{
return string.Concat("{0}|{1}|{2}", Arquivo, GrupoModulo).GetHashCode();
}
Mapping
public class GrupoArquivoMap : ClassMap<GrupoArquivo>
{
public GrupoArquivoMap()
{
Schema(Const.SCHEMA);
Table(Const.TB_EMAIL_GRUPO_ARQUIVO);
CompositeId()
.KeyReference(x => x.Arquivo, Const.ID_ARQUIVO)
.KeyReference(x => x.GrupoModulo, Const.ID_GRUPO, Const.ID_MODULO)
;
}
}

HashSet<T> quickly checking if identity exists by T.identity

I have an entity called Feature which contains a value identity called FeatureIdentity.
I have a list of these entities, and i want to quickly determine if the identity already exists.
The kicker is i need to be able to compare by the FeatureIdentity and not be the Feature, the Contains procedure on lists is checking against a provided T parameter.
So I am currently doing the code:
public class SomeClass
{
HashSet<Feature> features = new HashSet<Feature>();
public void SetRequirement(FeatureIdentity feature, FeatureIdentity requires)
{
if (ContainsFeature(feature) == false || ContainsFeature(requires) == false)
{
// throw
}
this.requirements.Add(feature, requires);
}
bool ContainsFeature(FeatureIdentity identity)
{
return this.features.Where(x => x.Id.Equals(identity)).Count() > 0;
}
}
Does Linq optimize this, or is this there a correct optimal way of checking if the item exists?
public class Feature
{
public Feature(FeatureIdentity id, string name)
{
this.id = id;
this.name = name;
}
FeatureIdentity id;
string name;
FeatureIdentity Id
{
get { return this.id; }
}
}
public class FeatureIdentity : IEquatable<FeatureIdentity>
{
private readonly string sku;
public FeatureIdentity(string sku)
{
this.sku = sku;
}
public bool Equals(FeatureIdentity other)
{
return this.sku == other.sku;
}
public string Sku
{
get { return this.sku; }
}
public override int GetHashCode()
{
return this.sku.GetHashCode();
}
}
with ctor public HashSet(), HashSet<Feature> is using EqualityComparer<Feature>.Default as default Comparer.
if you use HashSet<Feature>, you should implement IEquatable<Feature> and override GetHashCode.
public class Feature: IEquatable<Feature>
{
public bool Equals(Feature other)
{
return this.id.Equals(other.id);
}
public override int GetHashCode()
{
return this.id.GetHashCode();
}
}
then you can try following workaround which waster a temp object from heap.
bool ContainsFeature(FeatureIdentity identity)
{
return this.features.Contain(new Feature(identity, null));
}

How can I create a Fluent NHibernate Convention that ignores properties that don't have setters

I'm looking for a FluentNH (Fluent NHibernate) convention or configuration that ignores all properties that have no setter:
It would still map these:
public class foo{
public virtual int bar {get; private set;}
}
And omit these:
public class foo{
public virtual int fizz{get;private set;}
public virtual int bar{get {return fizz;}} //<-------
}
You should use a custom mapping configuration
public class DefaultMappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Member member)
{
return member.CanWrite;
}
}
Usage :
var nhConfiguration = new Configuration().Configure();
var mappingConfiguration = new DefaultMappingConfiguration();
var.fluentConfiguration = Fluently.Configure(nhConfiguration );
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<MappedType>(mappingConfiguration)
));
var sessionFactory = this.fluentConfiguration.BuildSessionFactory();
However, private setters won't get mapped. You should get them as protected
Use this:
public class DefaultMappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Member member)
{
if (member.IsProperty && !member.CanWrite)
{
return false;
}
return base.ShouldMap(member);
}
}
That should handle the case of no setter and private setter.
I know this is old question but code below do well with private setters.
public override bool ShouldMap(Member member)
{
var prop = member.DeclaringType.GetProperty(member.Name);
bool isPropertyToMap =
prop != null &&
prop.GetSetMethod(true) != null &&
member.IsProperty;
return
base.ShouldMap(member) && isPropertyToMap;
}
Another way is to use an attribute.
public class MyEntity
{
[NotMapped]
public bool A => true;
}
public class AutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Member member)
{
if (member.MemberInfo.GetCustomAttributes(typeof(NotMappedAttribute), true).Length > 0)
{
return false;
}
return base.ShouldMap(member);
}
}

Categories