Not having more than a month's worth experience with EF and Automapper, I'm really battling to get the following done:
I've got 3 DB entities as follow: (unnecessary fields remove for simplicity)
Within my project i have these entities pulled in through EF, and I created 2 DTO classes as follow:
[DataContract]
public class StoreDTO
{
[DataMember]
public int StoreID {get; set;}
[DataMember]
public string StoreName{get; set;}
[DataMember]
public List<CategoryDTO> Categories {get; set;}
}
[DataContract]
public class CategoryDTO
{
[DataMember]
public int CategoryID {get; set;}
[DataMember]
public string CategoryName{get; set;}
[DataMember]
public List<StoreDTO> Stores{get; set;}
}
I'm using automapper to try and map the two classes but I'm really battling to get the composite relationship going.
My map creation looks something like this now (after numerous tries):
Mapper.CreateMap<Store, StoreDTO>(); // Not performing the category list mapping yet here
// This is where I attemped the mapping for a store list in each category:
// x.Store_StoreCategory = StoreCategory as composite entity above
Mapper.CreateMap<Category, CategoryDTO>()
.ForMember(dto => dto.Stores,
opt => opt.MapFrom(x => Mapper.Map<List<Store>, List<StoreDTO>>(x.Store_StoreCategory.Select(y => y.Store).ToList())));
This isn't working though. I read a few articles and the latest i came across is this one by Jimmy Bogard himself:
Avoid many-to-many mappings in ORMs
He suggests creating the composite DTO as well and work from there which seems like a good way to go but I need help with the mapping as I'm a TSQL guy, not yet a LINQ guy with automapper and DTO capabilities...
Other articles I tried were the following but with my lack of knowledge on Automapper and EF, I fear I might be missing the point somewhere as these articles don't refer to the composite DTO as suggested by Jimmy Bogard:
AutoMapper many to many relationship into collection
https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers
Many-to-many to DTOs using Automapper
Would really appreciate some insight!
Related
I have a strange relation in my database.
Basically it's an entity connected to entities of the same type.
What I have is this:
Model:
public class Article
{
-- Properties --
ICollection<Article> ConnectedArticles { get; set; }
}
Fluent API:
modelBuilder.Entity<Article>()
.HasMany(ca => ca.ConnectedArticles)
.WithMany(ca => ca.ConnectedArticles)
.Map(m =>
{
m.MapLeftKey("ArtNo");
m.MapRightKey("ArtNo");
m.ToTable("ConnectedArticles");
});
This does not play well with Entity Framework and results in the error: The navigation property 'ConnectedArticles' declared on type 'ServiceSystem.Models.CommonArticle' cannot be the inverse of itself.
I could solve this issue by creating a new model which contains the related articles like so:
public class ConnectedArticle
{
public Article article1 { get; set; }
public Article article2 { get; set; }
}
But I'm hoping Entity Framework can do this on it's own, just like it does with many-to-many for separate entities.
Is it possible to solve this nicely with Entity Framework or am I missing something critical?
Thanks,
Robin Dorbell
I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.
I'm having a problem with a mapping in Entity Framework.
I have the following classes (simplified):
public class Building
{
public int ID { get; set; }
// *.. snip..* other properties
public Location Location { get; private set; }
}
public class Location
{
public string Street {get; set;}
public Country country {get; set}
}
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
}
Building and Country are entities, they are saved in the database. Location is a value type and should map to the same table as Building.
However, when I map it this way, entity framework wants to map Location to a table as well and is complaining it has no Key. I don't want to give it a key since it belongs to the Building and should not be an entity at all.
I have seen workarounds which say you need to put Country on the Building-class, but that just doesn't feel good (and is semantically just plain wrong).
I'm using Entity Framework 5
Since the release of Entity Framework Core 2, it is now possible to achieve this using owned entities.
Your configuration would look like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// ...
modelBuilder.Entity<Building>().OwnsOne(x => x.Location);
modelBuilder.Entity<Location>().HasOne(x => x.Country);
// ...
}
This way, properties from Location class will be a part of the table Building class is mapped to. This means you will only have tables for Building and Country classes and the Building table will have a foreign key to the Country table.
I know it's been a long since you posted the question, but I thought this answer might be helpful to someone who comes across this question.
In my opinion the Entity Framework shouldn't allow such a case.
I understand that you don't consider the Location as an Entity but adding entity references to complex types doesn't seem like a solid approach either. The relationship of a building to a country is quite straight forward. A building belongs to one country. Thus a building model should include a country id. What would you expect to be mapped?
If you would expect the table Building to have just three columns ID, Street, CountryId and you still want to hold the Location model then you should use the following complex type.
public class Location
{
public string Street {get; set;}
public int countryId {get; set}
}
If however you would expect your Building table to have all the fields from the model Country then that could lead to some tricky situations like what would happen If you wanted to add new fields to the Country model or If you wanted to add other complex types or entities to your Country model according to a new Business Case.
Those cases would mess with the relational concept and would over-complicate your structure without any meaningful reason. (in my opinion of course)
You may mark Location property in Building class with [NotMapped] Attribute.
using System.ComponentModel.DataAnnotations.Schema;
public class Building
{
[NotMapped]
public Location Location { get; private set; }
}
Hope that solves your problem!
I have two classes:
public class CustomerDTO
{
public string Name {get;set;}
public List<Order> Orders {get;set;}
}
public class OrderDTO
{
public string Name {get;set;}
public string Description {get;set;}
public decimal Cost{get;set;}
}
I am using AutoMapper for .NET 3.5 and currently doing the following in my Application_StartUp:
Mapper.CreateMap<Customer, CustomerDTO>();
Mapper.CreateMap<Order,OrderDTO>();
This is a simplified example as I named my DTO properties different than my entity properties, so I used ForMember, but I am unclear on how to map Orders to Customer:
I tried:
Mapper.CreateMap<Customer, CustomerDTO()
.ForMember(dest => dest.Orders, opt=> opt.MapFrom(src=>src.Orders));
but it does not find src.Orders.
If I do indeed need to have both CreateMap statements, does AutoMapper "automatically" link the objects Customer to Orders?
Yes, you need to tell AutoMapper about each mapping. It will not guess for you. So, if an OrderDTO should map to an Order, you must tell AutoMapper that. You must also specify the reverse relationship if that's needed as well (i.e. Order should map to OrderDTO).
In other words, for bi-directional mapping you would need:
Mapper.CreateMap<Order, OrderDTO>();
Mapper.CreateMap<OrderDTO, Order>();
As far as Customer goes, if both Customer and CustomerDTO have a property named Orders, you don't need to do anything else. As long as you've told AutoMapper to map between Order and OrderDTO and Customer and CustomerDTO, it will automatically map your Order when you map Customer.
I'm confused on how I'm going to updated related entities using DDD. Let say I have a Employee Class and Workschedule Class. How should I updated a specific workschedule of a certain employee? The relationship between Employee and Workschedule is One-To-Many. Below is the code I'm using how to Add/Update a certain workschedule.
public class Employee
{
public int EmployeeId { get; set; }
public virtual ICollection<WorkSchedule> WorkSchedules { get; set; }
public WorkSchedule AddWorkSchedule(WorkSchedule workSchedule)
{
this.WorkSchedules.Add(workSchedule);
return workSchedule;
}
public WorkSchedule EditWorkSchedule(WorkSchedule workSchedule)
{
var originalWorkSchedule = this.WorkSchedules.FirstOrDefault(w => w.WorkscheduleId == workSchedule.WorkscheduleId);
originalWorkSchedule.ClockIn = workSchedule.ClockIn;
originalWorkSchedule.ClockOut = workSchedule.ClockOut;
return originalWorkSchedule;
}
}
public class WorkSchedule
{
public int WorkScheduleId { get; set; }
public DateTime ClockIn { get; set; }
public DateTime ClockOut { get; set; }
public int EmployeeId { get; set; }
}
Is this correct? Did I follow DDD correctly? Also, my thinking right now Workschedule is a value object but I'm putting and ID for normalization purposes
your Model should be "POCO" class
CRUD methods such.. Add or Edit will be considored as part of "Service" or "Repository"
here is a quick idea that just came to my mind / how should it look like and its usage..
IRepository repository { get; set; } //implement Interface and inject via IoC Container
//..usage
var employee = repository.GetEmployee(123); //get by id
//..new WorkSchedule
employee.WorkSchedules.Add(workSchedule);
var result = repository.Save(employee);
Since everything here is EF related, it isn't much of DDD. IF the code works as desired, then it's ok. But DDD has no relationship to EF or any other ORM. You should design the Domain objects, without caring at all about the database or an ORM. Then, in the repository you map the Domain entities to Persistence entities which will be handled by the ORM.
Also, my thinking right now Workschedule is a value object but I'm putting and ID for normalization purposes
This is the consequence when the layers and models are mixed. You don't need an ID in the domain but you need an id for persistence. Trying to fit both requirements in one model and calling that model Domain leads to nowhere.
EF it is not for DDD, it is too clumsy. EF is for same codemonkeys who likes t map SQL tables to Entities and do it like ActiveRecord antipatter, but after more intelligent developers started to call this as a bad practice, they started to use ORM, entities and continue monkeycoding.
I'm struggling with EF last 3 years to let it work DDD way. It successfully resists and wins. Without hacks it doesn't work.
The on-to-many relations still doesn't work as expected, there is not way to create entities with constructor, not the public properties and so on.