Object referenced by many, without own property - c#

I have a class that is referenced by many other classes:
class Foo
{
// Some properties
}
class Bar
{
public Foo Foo { get; set; }
}
I can map this relationship on the Bar end:
class BarMap : ClassMap<Bar>
{
public BarMap()
{
References(b => b.Foo).Cascade.All();
}
}
This works when I am deleting a Bar object but I am getting foreign key conflicts if I try and delete a Foo object. I understand this is because NHibernate doesn't know that there are Bars relying on it, so happily tries to delete the Foo and not the Bars.
I know I can do something along the lines of:
class Foo
{
public IList<Bar> Bars { get; set; }
// some properties
}
class FooMap : ClassMap<Foo>
{
public FooMap()
{
HasMany(f => f.Bars)
.Inverse()
.Cascade.AllDeleteOrphans();
}
}
Is there a way to map Foo so that it knows about the inverse many-to-one relationship without adding a property to Foo? I don't really want to add a collection to Foo for each type of object referencing it.

I think I have correctly understood your aim, and I fear the answer is you cannot do that easily.
You may try using IInterceptor or event combined with some analysis of the meta-model NHibernate has generated from your mapping (see ISessionFactory.GetAllClassMetadata), but this would probably requires a bunch of work hours.

Related

How to prevent cyclic response with DBSet.Add() in Entity Framework?

I'm working with an ASP.NET Core 6.0 Web API using EF Core 7.0. Mostly, it's working well but I am stuck on something I can't quite figure out.
In my service layer, I am passing in a FooForCreationDto and manually mapping it to a Foo. The Foo object gets passed to the repo which calls Context.Set<T>().Add(object);
Fine.
But, the Foo object has a many-to-many relationship with a Bar object. So each object has a list of the other object. The problem is that the add method returns an object with a cyclic error, returning each objects list of the other, recursively. I want the add to just return Foo and its list of Bars, not to then also include each Bar's list of Foo's and so on, round and round.
The repo method is generic, so this should also work the other way around when adding a new Bar.
In a get method, we would have to use .Include(x => x.Bar) to include nested objects, but the return object of the Add method seems to do this by default.
How can I disable this?
I think the above is pretty clear, but here is a code example:
// Models
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public List<Bar> Bars { get; }
}
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
public List<Foo> Foos { get; }
}
// Service
public async Task<Foo> CreateFoo(FooForCreationDto fooForCreation)
{
Foo foo = new Foo()
foo.name = fooForCreation.name //map top level props.
if (!fooForCreation.Bars.IsNullOrEmpty())
{
var bars = (List<Bar>)await _repository.Bar.GetBarsFromIds(fooForCreation.Bars);
foo.Bars.AddRange(bars);
}
// everything is fine to here. Foo has a list of Bar, but Bar does not contain nested recursive Foos
_repository.Foo.CreateFoo(foo);
//At this point foo now has nested bars with nested foos with nested bars etc... even though createFoo has void return.
await _repository.SaveAsync();
return foo
}
// Repository : RepositoryBase
public void CreateFoo(Foo foo)
{
Create(foo);
}
// RepositoryBase<T>
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
{
protected MyDb RepositoryContext;
public RepositoryBase(MyDb repositoryContext)
=> RepositoryContext = repositoryContext;
//This add method returns foo with recursive bars and foos, which then changes what is returned from the service method.
public void Create(T entity) => RepositoryContext.Set<T>().Add(entity);
}
I had a go adding the Microsoft.EntityFrameworkCore.Proxies and then adding UseLazyLoadingProxies, and then marking the lists in the model classes as virtual, but this seemed to cause all sorts of other errors I've not managed to unpick yet. For example, even it throws cyclic errors even though I have
JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Ignore

Automapper Flattening Common Nested Relationship

I'm trying to use Automapper to flatten a Entityframework object that has a nested complex property to a few various models that all inherit the same common properties from a base class, a simplified version of this is as follows:
public class EFObject {
public int Id { get; set; }
public int NestedId { get; set; }
public virtual AnotherModel AnotherModel { get; set; }
}
public class AModel : Model {
public int AModelId { get; set; }
}
public class BModel : Model {
public int BModelId { get; set; }
}
As both AModel and BModel share a common base (Model) ideally I would like to declare a common map like so:
mapper.Map<AnotherModel, Model>();
And then when mapping EFObject to AModel I would map the AModelId property within
mapper.Map<EFObject, AModel>();
and then include the AnotherModel -> Model map so that I wouldn't have to map the common properties multiple times (once for AModel and again for BModel, etc).
I was able to achieve the above by using an AfterMap, i.e.
CreateMap<EFObject, AModel>()
.AfterMap((src, dest) => Mapper.Map(src.AnotherModel, dest))
.ReverseMap();
However there is a fairly major issue with this solution in that it relies on the static instance of Automapper, and as I am dependency injecting an instance of Automapper, my unit tests that use this map are all failing due to the static Mapper not being instantiated.
I could get around this problem by initializing the static instance in my unit test, but as it requires knowledge of how your maps are structured, it defeats the aim of using Automapper (I feel).
I also know that I could write a converter for each Model, however that isn't ideal as it produces a lot of additional code for something I feel is supported but I'm struggling to find the answer for.
Any ideas on the best way to structure my maps to get around this problem?

Nhibernate - Getting two object type in repository return result

I have two class BookingInfo.cs and BookingTransaction class.
public class BookingInfo
{
public virtual string Code { get; set; }
}
public class BookingTransaction : BookingInfo {
public virtual string CustomerRefNo { get; set; }
}
below is the NHibernate mapping for both classes
public class BookingInfoConfiguration : ClassMap<BookingInfo> {
public BookingInfoConfiguration() {
Table("Bkg_BookingInfo");
LazyLoad();
DynamicUpdate();
Id(x => x.Id).GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
}
}
public class BookingTransactionConfiguration :
ClassMap<BookingTransaction> {
public BookingTransactionConfiguration() {
Table("Bkg_BookingInfo");
LazyLoad();
DynamicUpdate();
Id(x => x.Id).GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
}
}
Now i am querying to get rows from database.
CurrentSession.Query<BookingInfo>().ToList();
I get two items for single row in database table. one for Bookinginfo and another for BookingTransaction. but i want to get only result of type Bookinginfo.
How to remove the items of the child class from the result?
As said in Rabban's answer, this is by design. This is called implicit polymorphism. Rabban propose you to change your class hierarchies, but you can instead disable implicit polymorphism if you prefer.
With hbm mappings (I do not use fluent and do not know it), add the attribute polymorphism="explicit" on your class.
Mapping by code supports it too on class mapper with .Polymorphism(PolymorphismType.Explicit).
You can read more about implicit/explicit polymorphism here:
Implicit polymorphism means that instances of the class will be
returned by a query that names any superclass or implemented interface
or the class and that instances of any subclass of the class will be
returned by a query that names the class itself. Explicit polymorphism
means that class instances will be returned only be queries that
explicitly name that class and that queries that name the class will
return only instances of subclasses mapped inside this <class>
declaration as a <subclass> or <joined-subclass>. For most purposes
the default, polymorphism="implicit", is appropriate. Explicit
polymorphism is useful when two different classes are mapped to the
same table (this allows a "lightweight" class that contains a subset
of the table columns).
Its intended that NHibernate returns you both objects. To prevent this behavior create a abstract base class where both other class derive. Then you don't need to duplicate code and you can query each class separately.
Create the base class:
public abstract class BookingBase
{
public virtual string Code { get; set; }
}
and then derive your classes from it:
public class BookingInfo : BookingBase
{
}
public class BookingTransaction : BookingBase
{
public virtual string CustomerRefNo { get; set; }
}
Your mappings and queries can remain the same. And if you want to query both classes with one query, just query BookingBase.

Entity Framework Validation Errors on Insert with Shadowed Property

Consider the following scenario:
public abstract class Entity
{
public object Id { get; set; }
}
public abstract class Entity<T> : Entity
{
public new T Id { get; set; }
}
public class Foo : Entity<Guid>
{
}
If I have the following code:
var foo = new Foo { Id = Guid.NewGuid() };
db.Foos.Add(foo);
db.SaveChanges();
I'm getting a DbEntityValidation exception saying that Id is not allowed to be null. If I debug, I can see that Foo.Id does indeed have a value, but if I go into the base object down to the level of Entity, Id is null there, and it seems that is what Entity Framework is using to validate against rather than the non-shadowed property. I feel like I'm missing something really basic here; maybe I just haven't had enough coffee yet this morning. Any ideas what might be the problem?
The problem is that
public new T Id { get; set; }
...in Entity<T> hides the Id property in Entity. If you do this:
var foo = new Foo { Id = Guid.NewGuid() };
Entity entity = foo as Entity;
Console.WriteLine(entity.Id);
...that's not going to return a Guid, it's going to return what's behind Entity.Id which is an object which has not been set.
Edit:
Chris came up with this, which should work fine.
public abstract class Entity<T> : Entity
{
public new T Id
{
get { return (T)base.Id; }
set { base.Id = value; }
}
}
Yes, I missed something simple: Entity Framework is incompetent and doesn't support property shadowing. I thought it did because in the past I've successfully used shadowing to alter property attributes (e.g. make the property only required in a subclass), but I've never gone so far as to actually attempt to change the type.
UPDATE
It occurred to me that if I just set the base property with the right value, everything would be fine. So, I changed my Entity<T> class to the following:
public abstract class Entity<T> : Entity
{
public new T Id
{
get { return (T)base.Id; }
set { base.Id = value; }
}
}
The exception went away and the object was saved to the database successfully. Are there any unforeseen consequences to doing this that I'm missing?

Fluent NHibernate - Query over a derived class

Lets say I have two classes:
public class A
{
public virtual int Id { get; set; }
public virtual Object1 Obj { get; set; }
}
public class B : A
{
public new virtual Object2 Obj { get; set; }
}
I use Fluent NHibernate and I have created two different mappings for the two classes. However, when I try to query class A in my repository, FNH finds both class B and A, which kind of makes sense since both are A.
Example (this criteria will query over both A and B):
public List<T> GetByName(string name)
{
return Session.CreateCriteriaOf<A>.Add(Restrictions...);
}
When writing CreateCriteriaOf<A>, I only want to query over A - not B. How can I solve my problem?
I think you better make an inheritance tree where both A and B derive from a common (abstract) base type. Then NHibernate can make the distinction by a discriminator column.
Of course, your data model should accommodate this, so I hope your model is not prescribed in any way.

Categories