Automapper nested relationship "back" mapping - c#

Let's say I have a nested DTO, like this (in reality it is a lot more complex):
public class OrderDTO
{
public int Id { get; set; }
public List<ItemDTO> Item { get; set; }
}
public class ItemDTO
{
public int Id { get; set; }
}
that maps to:
public class Order
{
public int Id { get; set; }
public List<Item> Item { get; set; }
}
public class Item
{
public int Id { get; set; }
public Order Order { get; set; }
}
When mapping from the OrderDTO down to Order, I would like that the Order property from Item references its order, as to make something like var order = item.Order; possible.
Is there any way I can do that when mapping? Also, is it possible to configure that for all my nested mappings?

Using BeforeMap you can save the parent object in context.Items and then use that value in a resolver for the parent reference. If you respect a naming convention, you can use ForAllMaps to apply it wherever you need.

Related

Mapster - Dynamic property selection or ignore

I have a business need to dynamically select ONLY the properties of a given model that are specified, similar to an OData select clause. I am currently using Mapster's ProjectToType functionality to populate view models from EF Core entities.
Is there any way to tell Mapster to only select a given list of properties in the query that it generates? Or a way to take the full model mapping, and change mappings at runtime in an instance of TypeAdapterConfig to ignore properties that aren't in a given list of properties?
The end solution needs to be generic and work with navigation properties, because it will be applied to all of our entities in the database. We also used DynamicLinq in some cases, not sure if that can be used on top of Mapsters ProjectToType functionality.
Example:
Entities (Some properties omitted for length):
namespace DataAccess.Entities
{
public class Series
{
public Guid Id { get; set; }
public string Description { get; set; }
public long? StackRank { get; set; }
public string EntityId { get; set; }
// Other properties
}
public class Model
{
public Guid Id { get; set; }
public string Description { get; set; }
public string EntityId { get; set; }
public long? StackRank { get; set; }
public Guid SeriesId { get; set; }
public virtual Series Series { get; set; }
// Other properties
}
}
View Models (Some properties omitted for length):
namespace Models
{
public class Model
{
public Guid Id { get; set; }
public string Description { get; set; }
public string EntityId { get; set; }
public long? StackRank { get; set; }
public Guid SeriesId { get; set; }
public virtual Series Series { get; set; }
// Other properties
}
public class Series
{
public Guid Id { get; set; }
public string Description { get; set; }
public long? StackRank { get; set; }
public string EntityId { get; set; }
// Other properties
}
}
Given a rest call to get a list of all Model view models, with this list of properties to include:
var properties = new List<string> {
"Id",
"EntityId"
"Description",
"Series.Id",
"Series.Description",
"Series.EntityId"
}
The results would return some type of dictionary, dynamic, or anonymous object that contained ONLY these properties, and the other properties would not even be included in the final select of the SQL query that gets created.
In the end, I decided to use Arca Artem's suggestion, with a little twist. I used reflection to grab a list of all properties of the model and cache them. After that, I compared the cached properties vs the list of properties to include, and ignored the properties that weren't in both lists. Kinda like this:
var clonedConfig = mapsterInstance.Clone();
clonedConfig.ForType<TSource, TDestination>().Ignore(propertiesToIgnore);
var models = await query.ProjectToType<TDestination>(clonedConfig).ToListAsync();
Maybe not the most elegant solution, but it worked well enough for what I needed. I also set up our json serializer to ignore null values.

Using Data annotations to create navigation property from class to itself?

Using Entity Framework Code first I have a class that holds data for a drop-down list. The same class holds records that are sub-items for the items in the main list. Ultimately this will create a cascading set of drop-down lists.
I am trying to figure out how to make the navigation property for the class link back to itself. The issue class is the one that I am using to populate the drop-down list. The Complaint class also has a link to the Issues class but does not need a link back to the subcategory.
public class Issue
{
public Issue()
{
Complaints = new List<Complaint>();
SubIssues = new List<Issue>();
}
[Key]
public int IssueID { get; set; }
public string Name { get; set; }
public bool IsSubCategory { get; set; }
[ForeignKey("IssueID")]
public ICollection<Issue> SubIssues { get; set; }
public virtual ICollection<Complaint> Complaints { get; set; }
}
public class Complaint
{
public Complaint()
{
}
public int ComplaintID { get; set; }
public string Name {get; set;}
[ForeignKey("IssueID")]
public virtual Issue Issue { get; set; }
}
I did something similar, but actually did only have a parent reference in the children. Either way this should work.
public class Folder
{
[Key]
public int Id { get; set; }
// Some Property
public string Name { get; set; }
// They foreignkey for Many-side
public virtual Folder Parent { get; set; }
// The list for One-side (Not tested in my application)
public virtual ICollection<Folder> SubFolders { get; set; }
}
It is same as a regular one-to-many relation, just all the references are within same entity.

What is the best strategy of creating code first database with inherited entities?

For example if I have a model:
public class BasePolicy {
public int Id { get; set; }
public string Name { get; set; }
}
public class PaymentPolicy : BasePolicy {
public string PaymentMethod { get; set; }
}
public class ReturnPolicy : BasePolicy {
public int ReturnTerm { get; set; }
}
... and want to create codefirst database and a repository with next requirements:
ability to retrieve base entities by Id in base type (BasePolicy). This is necessary for admin index page which displays list of policies only by it's name.
I need to have access to children specific properties (PaymentMethod if it is PaymentPolicy - etc.) using select by Id . This is necessary for edit actions.
What is the best way to do that? Should I create seperate table for each child type?
public class Database : DbContext
{
public DbSet<PaymentPolicy> PaymentPolicies { get; set; }
public DbSet<ReturnPolicy> ReturnPolicies { get; set; }
}
+ data is logically sorted
- I will not be able to get BasePolicy entity by it's unique id without joining those tables and specifying policy type in select query. That's why I should inject some PolicyType enum to base type and implement repository method which will get BasePolicy by it's type (to determine which table to get from) and only then by unique Id - and downcast BasePolicy to specific child type policy. This is the solution I'm using just now.
... should I remove inheritance at all?
public class PaymentPolicy {
public int Id { get; set; }
public string Name { get; set; }
public string PaymentMethod { get; set; }
}
public class ReturnPolicy {
public int Id { get; set; }
public string Name { get; set; }
public int ReturnTerm { get; set; }
}
+ data is still logically sorted
- I still will not be able to get BasePolicy entity by it's unique id.
... should I add child types as navigation properties to base type?
public class BasePolicy {
public int Id { get; set; }
public string Name { get; set; }
public PaymentPolicy PaymentPolicy { get; set; }
public ReturnPolicy ReturnPolicy { get; set; }
}
public class PaymentPolicy {
public string PaymentMethod { get; set; }
}
public class ReturnPolicy {
public int ReturnTerm { get; set; }
}
- This will destroy logic model structure
+ I will be able to get list of policies without redundant joins
+ It will provide strong one to one relationship
Or are there some more advanced strategies and techniques?

EF Code First: Trying to get a child property's parameters to appear in the parent class' table

I have a class, "Search". See definition below:
public class Search
{
[Key]
public int SearchID { get; set; }
public int UserID { get; set; }
public SearchParameters SearchParameters { get; set; }
public ICollection<SearchProvider> SearchProviders { get; set; }
public User User;
}
SearchParameters is a class with value types, and a few sub-classes; as defined below:
public class SearchParameters
{
public List<string> SearchTerms { get; set; }
public int MaxRecords { get; set; }
public DistanceParameter Distance { get; set; }
public PriceRangeParameter PriceRange { get; set; }
}
The idea is that I do not want a separate SearchParameters table that has to link to the Search table because every property of the search is always one to one (Except for SearchTerms). Really, what I want EF to do is 'bring up' the child classes' properties so we end up with All the properties of SearchParameter in the SearchTable (and all the parameters of the DistanceParameter and PriceRangeParameter objects themselves). What annotations or other logic would I need for this to work? Thanks!
I think EF Complex Type mapping is what you need, see more here:
http://weblogs.asp.net/manavi/archive/2011/03/28/associations-in-ef-4-1-code-first-part-2-complex-types.aspx

Implement a navigation property in c# class

i have 2 entities each with a relating c# class. I set up a navigation property on table A to contain a reference to many items in table B. When i make a new table A class object i need to be able to create the collection of table B objects in table A. How do i set up the navigation property in the table A c# class?
DATAMODEL:
http://bluewolftech.com/mike/mike/datamodel.jpg
Navigation properties are simple in EF. The example below shows how a navigation property would look:
public class Foo
{
public int FooId { get; set; }
public string SomeProperty { get; set; }
public virtual IEnumerable<Bar> Bars { get; set; }
}
Where Foo represents tableA and Bar represents tableB. They key word for the navigation property is virtual which enables lazy-loading by default. This is assuming you're using EF4.1 Code First.
EDIT
Off the top of my head, this should be a good starting template for you:
public class PointOfInterestContext : DbContext
{
public IDbSet<PointOfInterest> PointOfInterest { get; set; }
public IDbSet<POITag> POITag { get; set; }
public IDbSet<Tag> Tag { get; set; }
public override OnModelCreating(DbModelBuilder modelBuilder)
{
// custom mappings go here
base.OnModelCreating(modelBuilder)
}
}
public class PointOfInterest
{
// properties
public int Id { get; set; }
public string Title { get; set; }
// etc...
// navigation properties
public virtual IEnumerable<POITag> POITags { get; set; }
}
public class POITag
{
// properties
public int Id { get; set;}
public int PointOfInterestId { get; set; }
public int TagId { get; set; }
// navigation properties
public virtual PointOfInterest PointOfInterest { get; set; }
public virtual Tag Tag { get; set; }
}
public class Tag
{
// properties
public int Id { get; set; }
public string TagName { get; set; }
// etc...
// navigation properties
public virtual IEnumerable<POITags> POITags { get; set; }
}
Then you would implement the other logic in your business objects. The entities are supposed to be lightweight and at most should have data attributes. I prefer to use the fluent mappings through the OnModelCreating though.
Here are a few good references:
MSDN - EF 4.1 Code First
Code First Tutorial

Categories