I know that this question have asked a million times. But my case, I weird and I don't understand.
I got a cyclic reference and below are my code/configuration:
I have Entity as a base class for all entities.
[DataContract(IsReference = true)]
public class Entity : IEntity
Inherited class is like this, and created by Entity Framework 6
[DataContract]
public partial class User: Entity
I have Group, User, GroupUser, GroupAdministrator. The name here is clear, I think. Group has GroupUser, User has GroupUser and GroupUser has Group & User.
In DbContext, proxy creation is turned off, lazy loading is on.
Error comes from this method, when I call it, I got a cyclic reference.
public IList<Group> GetGroups()
{
return _groupRepository.GetAll(true, _ => _.GroupAdministrators.Select(__=>__.User), _ => _.GroupUsers.Select(__ => __.User));
}
I debug and the root cause is this: _ => _.GroupUsers.Select(__ => __.User)
The _groupRepository.GetAll():
public IList<T> GetAll(bool? isActive = true, params Expression<Func<T, object>>[] includes)
{
IQueryable<T> query = BrokerageSimulatorContext.Set<T>()
.Where(_ => (isActive.HasValue && _.IsActive == isActive) || !isActive.HasValue);
return includes.Aggregate(query, (current, includeProperty) => current.Include(includeProperty)).ToList();
}
Why I need ? _ => _.GroupUsers.Select(__ => __.User) Because I need to include the Users of that group when list all groups.
Related
I have the following entities:
ProjectGroup.cs
//Parts omitted for brevity
public class ProjectGroup : Entity<int>, IAggregateRoot
{
private HashSet<Project> _projects;
public IReadOnlyCollection<Project> Projects
=> this._projects.ToList().AsReadOnly();
}
Project.cs
//Parts omitted for brevity
public class Project: Entity<int>
{
private HashSet<Task> _backlog;
public IReadOnlyCollection<Task> Backlog
=> this._backlog.ToList().AsReadOnly();
}
Task.cs
//Parts omitted for brevity
public class Task : Entity<int>
{
public TaskDescription Description { get; private set; }
}
TaskDescription.cs is a flat value object that encapsulates properties of interest about Task.cs.
I have read/write persistance functionality in place using EF Core 3.1 and SQL Server. Inserting a new Task in the Task collection of Project for a particular ProjectGroup works as expected and everything is inserted in the database.
However there is a problem when I'm reading from the database. When I query via EF for a particular project, like below, the Description property of each member of the Task collection is null, even though there are records in the database.
var projectGroup = await this.All()
.Include(g => g.Projects).ThenInclude(p => p.Backlog).ThenInclude(t => t.Description)
.AsNoTracking()
.FirstOrDefaultAsync(g => g.Id == projectGroupId);
var project = projectGroup.Projects.FirstOrDefault(p => p.Id == projectId);
this.All() is dbContext.Set<ProjectGroup>()
This is the EF configuration for the Task entity:
builder
.OwnsOne(t => t.Description, description =>
{
description.WithOwner();
});
I initially tried the query without the second ThenInclude as I know from the official docs that owned types are by default populated without including them, but as that didn't work I thought I'd try to manually include the type, but to no avail.
Say I have the following models:
public class Subject
{
private List<SubjectResponse> responses;
public int Id { get; private set; }
public IEnumerable<SubjectResponse> Responses => responses.ToList();
public void Foo()
{
// How do I check here if Responses fully has been loaded?
foreach (var response in Responses)
{
// ...
}
}
}
public class SubjectResponse
{
public int Id { get; private set; }
}
How do I check if all responses have been loaded in Foo()? I'd probably check for if (Responses is null), but that won't work in all cases.
Here is a minimum example of what could do wrong. In a real app the responses could be loaded at a completely different place. But h This shows how the responses could be fixed up by EF, so it could contain entries, but not all entries.
public async Task Bar()
{
var response = await dbContext.SubjectResponses.SingleAsync(s => s.Id == 1);
var subject = await dbContext.Subjects.SingleAsync(s => s.Id == 1);
subject.Foo();
// subject.Responses now has a count if 1, when there might actually be more responses.
}
I don't want to use lazy loading, because of performance implications (and because Lazy Loading won't load related entities async). Eager and Explicit loading are fine.
Edit: what I’m mainly looking for is a way to check if the navigation property has been loaded fully, so that I can load it it has not been.
You cannot detect whether all related entities happen to have passed by Entity Framework.
What you show works because the entity from dbContext.SubjectResponses.SingleAsync(s => s.Id == 1) has a SubjectId of 1, and will be cached, and successively be attached to the result of dbContext.Subjects.SingleAsync(s => s.Id == 1).
There is no way for EF, nor for your code, to know that all SubjectResponses with a SubjectId of 1 have been loaded from the database, so you'll have to explicitly load them:
var subject = await dbContext.Subjects.SingleAsync(s => s.Id == 1);
await dbContext.Entity(subject)
.Reference(s => s.responses)
.LoadAsync();
But you can't do that, as Subject.responses is private, so you'll have to do that from within your entity's Foo() method, and you'll have to inject your DbContext into your entity, and that'll just become a giant mess.
Why not just do it pragmatically, make Responses a public auto-property and Include() the related entities on beforehand:
var subject = await dbContext.Subjects.Include(s => s.Responses).SingleAsync(s => s.Id == 1);
There is the possibility to throw an exception if a related entity (or a collection of entities) has not been loaded (included), see Non-nullable properties and initialization.
Here is an example of a one-to-many relationship:
class MyEntity
{
// Assume read-only access.
public IReadOnlyList<MyRelatedEntity> MyRelatedEntities =>
_myRelatedEntities?.ToList().AsReadOnly() ??
throw new InvalidOperationException("MyRelatedEntities not loaded.");
private readonly IEnumerable<MyRelatedEntity>? _myRelatedEntities = null;
}
class MyRelatedEntity
{
public MyEntity MyEntity
{
get => _myEntity ??
throw new InvalidOperationException("MyEntity not loaded.");
set => _myEntity = value;
}
private MyEntity? _myEntity = null;
}
Entity Framework Core will automatically set the backing field and you can detect if the related entity was loaded.
Unfortunately this approach does not work for optional relationships, or at least I haven't figured out (yet) how to do it.
TLDR version: I'm having trouble getting my DDD domain model to work with NHibernate. If my value object itself contains a collection of value objects, I can't assign a new value without getting an NHibernate exception, and want to know what the best practice is in this situation.
Longer version:
Say I have an entity which contains a value object as a property, ValueObjectA, which itself contains a set of a different value objects of type ValueObjectB.
ValueObjectB only exists meaningfully as a property of ValueObjectA, i.e. if myEntity.ValueObjectA == null, it doesn't make sense for ValueObjectB to exist either.
I've written some example code to illustrate what I mean, with simplifications for brevity.
public class Entity
{
public int Id { get; private set; }
public ValueObjectA ValueObjectA { get; set; }
// Constructor: public Entity(ValueObjectA valueObjectA)
}
public class ValueObjectA : IEquatable<ValueObjectA>
{
public string X { get; private set; }
public ISet<ValueObjectB> ValueObjectBs { get; private set; }
// Constructor: public ValueObjectA(string x, ISet<ValueObjectB> valueObjectBs)
// Implementation of Equals/GetHahcode
}
public class ValueObjectB : IEquatable<ValueObjectB>
{
public int Y { get; private set; }
public int Z { get; private set; }
// Constructor: public ValueObjectB(int y, int z)
// Implementation of Equals/GetHahcode
}
I have a corresponding mapping class using mapping by code:
public class EntityMap : ClassMapping<Entity>
{
public EntityMap()
{
Table("Entity");
Id(x => x.Id, map => map.Generator(Generators.Identity));
Component(x => x.ValueObjectA, c =>
{
c.Property(x => x.X);
// Component relation is equilavent to <composite-element> in xml mappings
c.Set(x => x.ValueObjectBs, map =>
{
map.Table("ValueObjectB");
map.Inverse(true);
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Key(k => k.Column("Id"));
}, r => r.Component(ce =>
{
ce.Property(x => x.Y);
ce.Property(x => x.Z);
}));
});
}
}
The properties of ValueObjectA are mapped to the Entity table, but the properties of ValueObjectA.ValueObjectB are mapped to another table, since it is a one to many relationship. When a ValueObjectB is removed, I want that row to be deleted in the ValueObjectB table.
Since value objects are immutable, when I change the properties of entity.ValueObjectA, I should create a new instance of ValueObjectA. The problem is that the set of ValueObjectBs is a reference type, so when I try to save the entity with a different ValueObjectA, NHibernate will throw an exception because the original set that NHibernate is tracking is no longer referenced:
A collection with cascade="all-delete-orphan" was no longer referenced
by the owning entity instance.
Consider the following code:
var valueObjectBs_1 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2),
new ValueObjectB(3, 4)
};
var valueObjectA_1 = new ValueObjectA("first", valueObjectBs_1);
var entity = new Entity(valueObjectA_1);
// Save entity, reload entity
var valueObjectBs_2 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2)
};
var valueObjectA_2 = new ValueObjectA("second", valueObjectBs_2);
entity.ValueObjectA = valueObjectA_2;
// Save entity again
// NHIBERNATE EXCEPTION
I've managed to get around this by creating another ValueObjectA in order to preserve the reference to the set, e.g.
valueObjectA_1.ValueObjectBs.Remove(new ValueObjectB(3, 4));
entity.ValueObjectA = new ValueObjectA(valueObjectA_2.X, valueObjectA_1.ValueObjectBs);
However... that feels like a code smell - even if I wrote a custom setter for Entity.ValueObjectA, the implementation is starting to get complicated where the design is supposed to be simple.
public class Entity
{
// ...
private ValueObjectA valueObjectA;
public ValueObjectA ValueObjectA
{
// get
set
{
// Add/Remove relevant values from ValueObjectA.ValueObjectBs
valueObjectA = new ValueObjectA(value.X, ValueObjectA.ValueObjectBs);
}
}
}
What is the best practice in this type of situation? Or is this a sign that I'm trying to do something which violates the principles of DDD?
What you have is an anemic domain model.
You should replace public setters of the entity with methods that have meaningful names from the Ubiquitous language, that check the invariants and that do all the necessary cleanup in case of value objects replacements.
Although it may seem that things are more complicated this is payed back by the fact the now the entity is in full control about what happens with its internals. You now have full encapsulation.
I have the following convention which I load into my FNH config
public class TableNameConvention : IClassConvention, IClassConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
public void Apply(IClassInstance instance)
{
var tableName = instance.EntityType.Name.Pluralise();
instance.Table(tableName);
}
}
I do not specify table names on any of my mappings, yet this convention is not applied. I'm using Fluent NHibernate 1.4.1.1. Can anyone spot anything I might have done wrong?
UPDATE
The conventions are loaded in the following manner:
public static NHibernate.Cfg.Configuration BuildConfiguration()
{
var connectionStringName = "mydb";
return Fluently.Configure(new NHibernate.Cfg.Configuration())
.Database(MsSqlConfiguration
.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringName))
.Dialect<MsSql2008Dialect>()
.AdoNetBatchSize(50))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<Profile>();
m.FluentMappings.Conventions.Add(DefaultLazy.Always(), DynamicUpdate.AlwaysTrue(), DynamicInsert.AlwaysTrue());
m.FluentMappings.Conventions.AddFromAssemblyOf<HiLoConvention>();
})
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.CurrentSessionContextClass, typeof(ManagedWebSessionContext).AssemblyQualifiedName))
.ExposeConfiguration(HiLoConvention.CreateScript)
.ExposeConfiguration(RunSchemaUpdate)
.BuildConfiguration();
}
All conventions sit in the same assembly and namespace as the HiLoConvention referenced above in the .AddFromAssembly() method call.
UPDATE 2:
The problem is in the Accept() method, because if I remove this method (and also the IClassConventionAcceptance interface from the class declaration) then the convention is applied. I have also tried this expectation to no avail
criteria.Expect(x => string.IsNullOrEmpty(x.TableName))
The original code worked with Fluent 1.2.1...
This question is old, but perhaps this can help someone else:
I assume you wanted to set the convention on each entity, unless a table name was specified explicitly in the map. So to achieve that, you can simply do the following:
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
var tableName = instance.EntityType.Name.Pluralise();
instance.Table(tableName);
}
}
This will apply the convention on all entities, unless TableName was specified explicitly in the map.
Have you tried
m.FluentMappings.ConventionDiscovery.AddFromAssemblyOf<HiLoConvention>()
in place of
m.FluentMappings.Conventions.AddFromAssemblyOf<HiLoConvention>()
Disclaimer: I'm fairly new to NH & ORM in general.
Disclaimer: I'm working with a build of FNH from here in order to use with NH3.0GA.
The problem in a nutshell is that I would like to use FNH's SubclassMap as a way to map a LEFT JOIN, table-per-subclass scenario to my object hierarchy which is defined as:
public class MyBaseClass {
public virtual int Id { get; set; }
}
public class MySubClass : MyBaseClass {
public virtual string SubClassVal { get; set; }
}
This is mapped via FNH as:
public class MyBaseClassMap : ClassMap<MyBaseClass> {
public MyBaseClassMap() {
Table("BaseClass");
Id(x => x.Id, "Id").GeneratedBy.Assigned();
}
}
public class MySubClassMap : SubclassMap<MySubClass> {
public MySubClassMap() {
Table("SubClass");
KeyColumn("Id");
Map(x => x.SubClassVal);
}
}
And I retrieve via:
public class Repository {
ISession session; //assume properly initialized ISession
public IList<T> GetAll<T>() where T: class {
return session.CreateCriteria<T>().List<T>();
}
}
And in my database, I've got 1 record in my BaseClass table, 0 records in SubClass.
Now, what I would like to do is pull the entity out as a MySubClass instance by doing something like this:
var rep = new Repository();
var subclasses = rep.GetAll<MySubClass>();
And of course, there are no instances in the returned collection as this is presumably performing an INNER JOIN underneath it all. This is where I'm stuck. I've managed to discover that specifying an 'optional' join is what I'm supposed to do. I've attempted to modify MySubClassMap to:
public class MySubClassMap : SubclassMap<MySubClass> {
public MySubClassMap() {
Join("SubClass", j => {
j.KeyColumn("Id");
j.Optional();
j.Map(x => x.SubClassVal); // note that I've tried the map outside the Join() below, to no avail
});
//Map(x => x.SubClassVal);
}
}
Compiling/running this presents me with the following (innermost) exception:
The element 'joined-subclass' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'join' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'property, many-to-one, one-to-one, component, dynamic-component, properties, any, map, set, list, bag, idbag, array, primitive-array, joined-subclass, loader, sql-insert, sql-update, sql-delete, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.
I'll save posting the stack trace, but the jist of it is:
MyApp -->
FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() -->
NHibernate.Cfg.FluentConfiguration.BuildConfiguration()
I think that's all the relevant info. I suspect I may be bumping into a breaking change between this very new version of NH and version of FNH that isn't so new. But, as mentioned earlier, I am a rookie, and could well be doing something stupid. If this is the case, I'd very much appreciate somebody smacking me over the head with what probably should be obvious.
Thanks in advance.
Entities have one type, which doesn't change. If you have a record in your BaseClass table only, that entity is and will always be a MyBaseClass.
If entities can change their "type", you shouldn't use inheritance but composition.