How can I map this parent-child relationship in AutoMapper? - c#

I have parent and child objects that are derived from LINQ to SQL entities. I want to map these onto some domain-friendlier DTOs. My SQL entity classes look somewhat like this:
public class SqlEntityParent
{
public int ParentId { get; set; }
public string Name { get; set; }
public EntitySet<SqlEntityChild> Children { get; set; }
}
public class SqlEntityChild
{
public int ChildId { get; set; }
public int ParentId { get; set; }
public int Position { get; set; }
public string CategoryName { get; set; }
public string CategoryValue { get; set; }
}
In this model, it's a standard one-to-many relationship between SqlEntityParent and SqlEntityChild. Some representative data would be...
Parent:
ParentId Name
-------- -------
1 Parent1
Child:
ChildId ParentId Position CategoryName CategoryValue
------- -------- -------- ------------ -------------
1 1 1 Contents Things
2 1 1 Group GroupOne
3 1 2 Contents Things
4 1 2 Group GroupTwo
Now I want to map these data into my domain objects, which look somewhat like this:
public class DomainParent
{
public int ParentId { get; set; }
public string Name { get; set; }
public List<DomainChild> Children { get; set; }
}
public class DomainChild
{
public int Position { get; set; }
public string Contents { get; set; }
public string Group { get; set; }
}
In this structure, a single DomainChild object is made up of data from two SqlEntityChild objects, and the grouping is determined by the Position value of the child entity. So, these sample data represent a single DomainParent object with a list of two DomainChild objects. The first child should have a Position of 1, a Contents value of "Things", and a Group value of "GroupOne". The second child should have a Position of 2, a Contents of "Things", and a Group of "GroupTwo".
I am comfortable with getting one-to-one custom mapping set up in AutoMapper using ValueResolvers, but I am not sure how best to handle this. I created the below resolver and associated mapping for the parent entities which maps the whole list of child entities in one pass, but it seems goofy because I have to do the whole mapping of child objects manually in this resolver class.
Mapper.CreateMap<SqlEntityParent, DomainParent>()
.ForMember(dto => dto.Children, opt => opt.ResolveUsing<MyResolver>());
public class MyResolver: ValueResolver<SqlEntityParent, List<DomainChild>>
{
private MyDataContext db;
public MyResolver()
{
db = new MyDataContext();
}
protected override List<DomainChild> ResolveCore(SqlEntityParent source)
{
// In here:
// 1. custom LINQ queries
// 2. manual creation of DomainChild objects
// 3. manual mapping of SqlEntityChild to DomainChild
}
}
So, my main question is this: is this the best I can do with AutoMapper in this situation, or is there some other more effective method that I can use?

Typically in these cases we map straight from SqlEntityChild to DomainChild, as lists, arrays and such are automatically supported. You just need to set up mappings for the element types, given that there is no extra logic besides iterating over the original Children collection.

Related

DotNetCore Entity Framework many-to-many on the same table

I am using DotNetCore 2, and I want to achieve a scenario where I have a single model, that can contain zero or more of the same models.
Consider the following scenario (for simplicity):
An engine can consist of many parts (components)
Each part can contain zero or more other parts (components)
I want to link these items as such:
System
Id Name
----------------
1 Drive System
Component
Id Name
----------------
1 Motor
2 Bearings
3 Couplings
4 Fixtures
With the many-to-many relationship between the System and the Component as such:
SystemComponents
SystemId ComponentId
-------------------------
1 1
And many to many link on Component (for the lack of better a better name)
ComponentComponents
ParentId ChildId
---------------------
1 2
1 3
1 4
So in the above scenario can be translated as A Drive System has a component - Motor, and the motor has additional sub-components, 'Bearings', 'Couplings', and 'Fixtures'
The SytemComponent relationship is working. For the ComponentComponent relationship, I have tried the following with my code-first migration:
Component
public class Component
{
public Component()
{
SystemComponents = new Collection<SystemComponent>();
ChildComponents = new Collection<Component>();
}
public int Id { get; set; }
public string Name { get; set; }
// Needs this for the Many to Many relationship
public virtual ICollection<SystemComponent> SystemComponents { get; set; }
// Any component can contain one or more existing components
public virtual ICollection<Component> ChildComponents { get; set; }
}
ComponentComponent linking model:
public class ComponentComponent
{
public int ParentComponentID { get; set; }
public int ChildComponentID { get; set; }
public Component ParentComponent { get; set; }
public Component ChildComponent { get; set; }
}
With my ComponentComponent configuration:
public class ComponentComponentConfiguration : IEntityTypeConfiguration<ComponentComponent> {
public void Configure(EntityTypeBuilder<ComponentComponent> builder) {
builder.ToTable("ComponentComponent");
builder.HasKey(cc => new { cc.ParentComponentID, cc.ChildComponentID });
builder.HasOne(cc => cc.ParentComponent)
.WithMany(c => c.ComponentComponents)
.HasForeignKey(cc => cc.ParentComponentID);
builder.HasOne(cc => cc.ChildComponent)
.WithMany(c => c.ComponentComponents)
.HasForeignKey(cc => cc.ChildComponentID);
}
}
When running the migration I get the following Error:
Cannot create a relationship between 'Component.ComponentComponents' and 'ComponentComponent.ChildComponent', because there already is a relationship between 'Component.ComponentComponents' and 'ComponentComponent.ParentComponent'. Navigation properties can only participate in a single relationship.
How would I achieve such a relationship in Entity Framework in DotNetCore 2?
EDIT
I have uploaded a similar sample project to my github page:
https://github.com/JAspeling/Many-to-Many
It looks like the issue is in your Component class. Instead of referencing to a collection of components it should look like this
public class Component
{
public Component()
{
SystemComponents = new Collection<SystemComponent>();
ChildComponents = new Collection<ComponentComponent>();
}
public int Id { get; set; }
public string Name { get; set; }
// Needs this for the Many to Many relationship
public virtual ICollection<SystemComponent> SystemComponents { get; set; }
// Any component can contain one or more existing components
public virtual ICollection<ComponentComponent> ChildComponents { get; set; }
}
You need to make a reference to the mapping table.
https://learn.microsoft.com/de-de/ef/core/modeling/relationships#many-to-many

AutoMapper - flatten domain model to view model. One is to many relationship

I have a complex domain model which has many relationships with other entities in the system.
For the purpose of editing this model I want to set up a view model that simplifies things. I want to break up the model into smaller logical chunks with edit screens for each part instead of trying to represent the whole model on one screen.
In the domain model where I have a one is to many relationship it is represented like this:
public partial class CbItemsContent
{
public CbItemsContent()
{
this.cbItemsContentRegulators = new HashSet<cbItemsContentRegulator>();
}
public int ItemContentId { get; set; }
public int ItemID { get; set; }
......
public virtual CbItem CbItem { get; set; }
public virtual ICollection<cbItemsContentRegulator> cbItemsContentRegulators { get; set; }
}
cbItemsContentRegulator is another set of entities that are owned by CbItemsContent (the model shown above)
I would like to replace the Collection of cbItemsContentRegulators in my model with a simplified viewModel called ItemContentRegulatorsViewModel like this:
public class ItemContentRegulatorsViewModel
{
public int ItemContentId { get; set; }
public int[] RegulatorIds { get; set; }
}
which reduces the relationship to the ItemContent parent ID and an int array if Regulator IDs
is what I am trying to do possible?
How do I Map My collection of CbItemsContentRegulators to an int[] of Ids
Assuming that you're using Entity Framework, you should change the view model to
public class ItemContentRegulatorsViewModel
{
public int ItemContentId { get; set; }
public IList<int> RegulatorIds { get; set; } // IList
}
No you can define the mapping:
var profile = Mapper.CreateProfile("SomeName");
profile.CreateMap<CbItemsContent,ItemContentRegulatorsViewModel>()
.ForMember(dest => dest.RegulatorIds,
m => m.MapFrom(src => src.cbItemsContentRegulators.Select(c => c.RegulatorId)));
Now you can use it in a query like:
context.CbItemsContents.ProjectTo<ItemContentRegulatorsViewModel>()
With RegulatorIds as an array this would throw an exception that ToArray is not recognized.

SQLite-Net Extensions not inserting in cascade in some cases

I am using SQLite-Net PCL together with SQLite-Net extensions for the development of an application using Xamarin.
In my model I have an entity (let's call it A) which is connected to other four entities through one-to-many relationships (that are represented as lists in the model). In order to populate the tables recursively when inserting an object of A in the database I have defined the relations to use Cascade on both read, insert and delete.
In order to test if I did everything correctly I created an object of type A and populated the including lists, and finally I have inserted it into the database. The strange thing is that, for 2 of the 4 including lists the insertion went well, and all the connected objects are inserted. For other 2, instead, only the first object of the list is inserted in the database. To be clear, I am checking the database content directly with a db browser.
The following is an example of one of the objects for which only the first element of the list is inserted.
public class Username : Entity
{
public string Name
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
public Username(string username)
{
Name = username;
}
}
This is instead one of the objects for which the insertion is correct.
public class AnAddress: Entity
{
public string Address
{
get;
set;
}
public AddressType Type
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
}
To be clear, the base object Entity contains the definition of the primary key:
public abstract class Entity
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public Entity()
{
Id = -1;
}
}
And this is the way the relationships are defined:
public class A : Entity
{
public string Name
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<AnAddress> Addresses
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Username> Usernames
{
get;
set;
}
}
I then create an A object by initialising it with two lists (List and List) in the same identical way.
I finally insert the object in the database with
c.InsertWithChildren(entity, recursive: true));
Where entity is of type A and c is the connection object.
Do you have any clue about the motivation of this strange behaviour?

How to load only the ids for an associated entities

I have N-Tier architecture and in service layer I need a way to get only the ids for associated entities or the full entities. So on one session I only need the ids and in other session I may need the full entities.
I have two entities:
public class ParentEntity
{
public virtual long Id { get; set; }
public virtual IList<ChildEntity> Children { get; set; }
public virtual string Name { get; set; }
// ... other fields
}
public class ChildEntity
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
// ... other fields
}
I some times I need to load the full ChildEntity (in verification layer) but some times I need only to load the ids for it like this in my service layer:
ParentEntity parent = repository.GetById(someId);
SendChildIds(parent.Children.Select(x => x.Id));
But doing this will load the ChildEntity fully I only want to load the ids like this
SELECT parentchild0_.ParentId as ParentId0_,
parentchild0_.ChildId as ChildId0_
FROM ParentChildEntities parentchild0_
WHERE parentchild0_.ParentId0_= 447 /* #p0 */
but he do something like this
SELECT pce.ParentId, ce.* FROM ChildEntities ce INNER JOIN ParentChildEntities pce on pce.ChildId = ce.Id WHERE pce.ParentId = 447
I use FluentNHibernate to configure the mappings.
You could either use projections or map to another entity which would only return the said requirements.
i.e.
ParentEntity {..., IList<ChildEntity> Children}
ChildEntity {...}
ParentEntityLite {...} //mapped w.r.t requirements i.e. no mappings for children
or via projections
from parent in _session.Query<ParentEntity>()
select new {parent.Id, ...}; //projection --> parent.Id
from parent in _session.Query<ParentEntity>()
select new ParentEntity(){Id = parent.Id}; // projection --> parent.Id

Is inheritance of navigation properties supported?

Having difficulty finding relevant search results...
Given this model:
public abstract class A
{
public int ID { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
public class B : A
{
}
public class C : A
{
}
public class Customer
{
public int ID { get; set; }
public virtual ICollection<B> Bs { get; set; }
public virtual ICollection<C> Cs { get; set; }
}
With this configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>().ToTable("As");
modelBuilder.Entity<B>().ToTable("Bs");
modelBuilder.Entity<C>().ToTable("Cs");
base.OnModelCreating(modelBuilder);
}
I get this result in the DB:
Question:
Is inheritance of navigation properties not supported? If I add public string SomeSharedProperty { get; set; } to A then as I would expect the column for that property only showed up in the As table.
What reason is there for the Customer_ID column in the Bs and Cs table? Is there any way to tell EF to not map that inherited property?
Thanks!
First off, inheritance is supported. But it seems in this specific instance not as you would expect.
Since Relational DBs do not support inheritance as we know it from object oriented programming there has to be some kind of transformation in order to make it happen.
Here is a series of blog post covering the issue in detail:
Table per hierarchy
Table per type
Table per class
It also tries to give guidelines when to use which of the strategies.
UPDATE
Apparently this is more tricky than it seemed at first glance. What you see is most likely due to a circular reference: A -> B -> Customer -> Bs.
The CustomerID columns of Bs/Cs are NOT the inherited ones from the As Table. It is in fact the representation of the relation properties specified on the Customer class:
public virtual ICollection<B> Bs { get; set; }
results in a nullable CustomerID column on table B.
public virtual ICollection<C> Cs { get; set; }
results in a nullable CustomerID column on table C.
So those nullable columns are used to represent the relation Customer -> Bs and Customer -> Cs. Their appearance has nothing to do with the Customer property on the A class.
You can easily check this by removing the navigation properties of the customer class. Then the result is what you would expect: A CustomerID column on the A table and no CustomerID column on B / C Table.
So in order to solve this you need to specifically tell EF how to resolve the circular reference. Not sure this is possible though, I'm afraid you will need to omit the Bs/Cs properties on the Customer and write a LINQ query instead to retrieve the info.
If you need those properties on the Customer class you can do is something like this:
public class Customer
{
public int ID { get; set; }
// this is necessary to have access to the related Bs/Cs
// also it cant be private otherwise EF will not overload it properly
public virtual ICollection<A> As { get; set; }
public IEnumerable<B> Bs { get { return this.As.OfType<B>(); } }
public IEnumerable<C> Cs { get { return this.As.OfType<C>(); } }
}

Categories

Resources