I am trying to use AutoMapper to map some DTO (data contract) objects received from a web service into my business objects. The root DTO object contains a collection of child objects. My business object also has a child collection of child business objects. In order to get AutoMapper to work, I had to include a setter on the collection property in my business object or the collection would always be empty. In addition, I had to add a default constructor to the collection type. So, it appears to me that AutoMapper is instantiating a new collection object, populating it and setting as the collection property of my business object.
While this is all well and good, I have additional logic that has to be wired up when the collection is created and having the default constructor defeats the purpose. Essentially, I am establishing the parent-child relationship and wiring up some events so they bubble from child to parent.
What I would like to do is to have AutoMapper simply map the child objects from the DTO's collection to the existing collection on my BO. In other words, skip creating a new collection and simply use the one the business object already has.
Is there any way to easily accomplish this?!?!?
UPDATE
Perhaps a better question, and simpler solution to my problem, is if it is possible to define arguments that AutoMapper will pass to the collection when instantiated? My child collection is defined like this:
public class ChildCollection : Collection<ChildObjects>
{
public ChildCollection(ParentObject parent) { Parent = parent; }
}
If I can configure AutoMapper to use this constructor and pass in the proper object, that would be PERFECT!
ANOTHER UPDATE
For the sake of clarity, here are the other classes in the problem space:
public class ParentObject
{
private ChildCollection _children;
public ChildCollection Children
{
get
{
if (_children == null) _children = new ChildCollection(this);
return _children;
}
}
}
public class ParentDTO
{
public ICollection<ChildDTO> Children { get; set; }
}
public class ChildDTO
{
public String SomeProperty { get; set; }
}
I configure AutoMapper this way:
Mapper.CreateMap<ParentDTO, ParentObject>();
Mapper.CreateMap<ChildDTO, ChildObject>();
Doing so this way and I have to add a setter to the Children property in ParentObject and a default (parameterless) constructor to ChildCollection. While I can work around the need to define the parent-child relationship, it seems that it would be logical to expect AutoMapper to support configuring the map to use a specific constructor when creating the child collection. Something like this:
Mapper.CreateMap<ParentDTO, ParentObject>()
.ForMember(obj => obj.Children, opt.MapFrom(dto => dto.Children))
.ConstructUsing(col => new ChildCollection(obj));
Notice that I am passing in the reference to "obj" which is the ParentObject instance being mapped.
It turns out that the answer was right there all along. The UseDestinationValue option does exactly what I want.
This options instructs AutoMapper to use the existing property on the target object and map any child properties or collection items into that object rather than creating a new proxy object.
So, here's all I have to do in my application:
Mapper.CreateMap<ParentDTO, ParentObject>()
.ForMember(obj => obj.Children,
opt.UseDestinationValue());
And, voila! Now I can instantiate the child collection, with parent reference, and setup the reference back to the parent in each item as it is added to the collection.
If I understood your problem, you should be able to use ConstructUsing as stated in this answer:
Automapper - how to map to constructor parameters instead of property setters
Its possible for EntityFramework (mvc or core) with AutoMapper.Collection.EntityFrameworkCore or EntityFramework by matching child ids to destination object ids like below
_config = new MapperConfiguration(cfg =>
{
cfg.AddCollectionMappers();
cfg.CreateMap<Order, OrderDto>()
.EqualityComparison((odto, o) => odto.Id == o.Id)
.ReverseMap();
cfg.CreateMap<OrderDetail, OrderDetailDto>().EqualityComparison((odto, o) => odto.Id == o.Id).ReverseMap();
});
In my case i have a List<Order> and one of my order contains a list of items such as List<OrderDetails> if i want to map List<Order> to List<OrderDto> with List<OrderDetailDto> that is not possible to mapping configuration. But i done it by seperating them like above. And i can use it like below
public int AddMany(List<OrderDto> orderDtos)
{
try
{
List<Order> orders = new List<Order>();
foreach (var oi in orderDtos)
{
var oneOrder = _mapper.Map<OrderDto, Order>(oi);
oneOrder.OrderDetails = new List<OrderDetail>();
foreach (var oid in oi.OrderDetails)
{
var oneOrderItem = _mapper.Map<OrderDetailDto, OrderDetail>(oid);
oneOrder.OrderDetails.Add(oneOrderItem);
}
orders.Add(oneOrder);
}
_orderRepository.InsertMany(orders);
return _uow.SaveChangesAsync().Result;
}
catch (Exception ex)
{
return 0;
}
}
There would be a manner to configuring it from the top but its very difficult, i couldn't find it now. But this maybe a resulation for someone.
Also in my opinion Mapster powerfull than autofac for those mapping configurations.
Related
I have a private list and I don't want that it can be modified from outside in general. Just adding from outside is allowed and only if the object is valid. Therefore I used to write it like this:
private List<Object> list = new ArrayList<>();
public List<Object> getList()
{
return Collections.unmodifiableList(list);
}
public void addObject(Object object)
{
if (isObjectValid(object)) //any validation
list.add(object);
}
Now for JavaFX purposes I turn the list to a property:
private ListProperty<Object> list =
new SimpleListProperty<>(FXCollections.observableArrayList());
To profit from the benefits of an property like data binding and the ListChangeListener I have to provide the property to the outer world. But then access to all methods of a list is provided, too. (To use a ReadOnlyListProperty has no effect since the list instance itself will never change.) What can I do to achieve all goals:
private ListProperty
ListChangeListener can be added from outside
Usable for binding dependencies from outside (the create methods form class Bindings)
No modifying of the property or list instance itself
Modifying of list content from outside only through my own methods
Not tested, but try:
private ListProperty<Object> list = new SimpleListProperty<>(FXCollections.observableArrayList());
private ReadOnlyListWrapper<Object> publicList = new ReadOnlyListWrapper<>();
and in the constructor:
publicList.bind(Bindings.createObjectBinding(
() -> FXCollections.unmodifiableObservableList(list.getValue()),
list));
then your accessor method is
public ReadOnlyListProperty<Object> getList() {
return publicList.getReadOnlyProperty();
}
I'm trying to update an entity using Entity Framework version 6.
I'm selecting the entity from the database like so...
public T Find<T>(object id) where T : class
{
return this._dbContext.Set<T>().Find(id);
}
And updating the entity like so..
public T Update<T>(T entity) where T : class
{
// get the primary key of the entity
object id = this.GetPrimaryKeyValue(entity);
// get the original entry
T original = this._dbContext.Set<T>().Find(id);
if (original != null)
{
// do some automatic stuff here (taken out for example)
// overwrite original property values with new values
this._dbContext.Entry(original).CurrentValues.SetValues(entity);
this._dbContext.Entry(original).State = EntityState.Modified;
// commit changes to database
this.Save();
// return entity with new property values
return entity;
}
return default(T);
}
The GetPrimaryKeyValue function is as so...
private object GetPrimaryKeyValue<T>(T entity) where T : class
{
var objectStateEntry = ((IObjectContextAdapter)this._dbContext).ObjectContext
.ObjectStateManager
.GetObjectStateEntry(entity);
return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
}
Just for clarity. I'm selecting the original entry out as I need to perform some concurrency logic (that Ive taken out). I'm not posting that data with the entity and need to select it manually out of the DB again to perform the checks.
I know the GetPrimaryKeyValue function is not ideal if there's more than one primary key on the entity. I just want it to work for now.
When updating, entity framework coughs up the error below when trying to execute the GetPrimaryKeyValue function.
The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'NAME_OF_ENTITY_IT_CANNOT_FIND'
I've written many repositories before and I've never had this issue, I cannot seem to find why its not working (hence the post).
Any help would be much appreciated.
Thanks guys!
Steve
It seems like you are having issues getting the PK from the entity being passed in. Instead of trying to go through EF to get this data you could either use their Key attribute or create your own and just use reflection to collect what the key names are. This will also allow you to retrieve multiple keys if it is needed. Below is an example I created inside of LinqPad, you should be able to set it to "Program" mode and paste this in and see it work. Hack the code up and use what you may. I implemented an IEntity but it is not required, and you can change the attribute to anything really.
Here are the results:
Keys found:
CustomIdentifier
LookASecondKey
Here is the code:
// this is just a usage demo
void Main()
{
// create your object from wherever
var car = new Car(){ CustomIdentifier= 1, LookASecondKey="SecretKey", Doors=4, Make="Nissan", Model="Altima" };
// pass the object in
var keys = GetPrimaryKeys<Car>(car);
// you have the list of keys now so work with them however
Console.WriteLine("Keys found: ");
foreach(var k in keys)
Console.WriteLine(k);
}
// you probably want to use this method, add whatever custom logic or checking you want, maybe put
private IEnumerable<string> GetPrimaryKeys<T>(T entity) where T : class, IEntity
{
// place to store keys
var keys = new List<string>();
// loop through each propery on the entity
foreach(var prop in typeof(T).GetProperties())
{
// check for the custom attribute you created, replace "EntityKey" with your own
if(prop.CustomAttributes.Any(p => p.AttributeType.Equals(typeof(EntityKey))))
keys.Add(prop.Name);
}
// check for key and throw if not found (up to you)
if(!keys.Any())
throw new Exception("No EntityKey attribute was found, please make sure the entity includes this attribute on at least on property.");
// return all the keys
return keys;
}
// example of the custom attribute you could use
[AttributeUsage(AttributeTargets.Property)]
public class EntityKey : Attribute
{
}
// this interface is not NEEDED but I like to restrict dal to interface
public interface IEntity { }
// example of your model
public class Car : IEntity
{
[EntityKey] // add the attribure to property
public int CustomIdentifier {get;set;}
[EntityKey] // i am demonstrating multiple keys but you can have just one
public string LookASecondKey {get;set;}
public int Doors {get;set;}
public string Make {get;set;}
public string Model {get;set;}
}
In database I have table: Notes and table Comments. In my solution I have 3 projects: DAL, BLL and Web.
I need to show a user notes with comments which aren't set as spam so I have created in DAL project that class:
public class NotesWithComments
{
public Notes Note { get; set; }
public IEnumerable<Comments> Comments { get; set; }
}
I use above class in each project: DAL, BLL and Web. Is this class Data Transfer Object, Business Object, Domain Object or what?
In a repository class I have that query:
public class NotesRepository
{
DatabaseContext context;
public NotesRepository(DatabaseContext context)
{
this.context = context;
}
public IQueryable<NotesWithComments> GetNotesWithNoSpamComments()
{
IQueryable<NotesWithComments> notesWithNoSpamComments = context.Notes.Include(x => x.Comments).OrderByDescending(x => x.CreateDate)
.Select(x => new NotesWithComments
{
Note = x,
Comments = x.Comments.Where(y => y.IsSpam == false).OrderBy(y => y.CreateDate)
});
return notesWithNoSpamComments;
}
}
In BLL project I use the method from the repository class:
public class NotesService
{
private readonly IUnitOfWork _unitOfWork;
public NotesService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IEnumerable<NotesWithComments> GetNotesWithComments()
{
IQueryable<NotesWithComments> result = _unitOfWork.NotesRepository.GetNotesWithNoSpamComments();
return result;
}
}
And in Web project I use the method form the service class:
public ActionResult Index()
{
List<NotesWithComments> result = _notesService.GetNotesWithComments();
return View(result);
}
Since it neither exposes any behavior (properties or getters/setters don't qualify) nor encapsulates its structure (again, properties or getters/setters that do nothing but expose the underlying data don't qualify) it is no object at all.
No matter if the language you use calls it an object or not. It is just a data structure (which is perfectly fine if you only want to move data from one place, like a database, to another, like a UI.)
Or, to quote Dan North:
Data Transfer Object is an oxymoron
Is this class Data Transfer Object, Business Object, Domain Object or
what?
A DTO is typically a class that is mainly used for transferring data between layers or some type of boundaries..typically just an object with no behavior.
I have always referred to Domain Objects as something that maps directly to a database table. So In your example, your domain models would be Notes, and Comments.
I would consider your NotesWithComments object a dto, or possibly a view model (as you're using it as your asp.net mvc model for the view).
The practice I would normally use here is use your NotesWithComments as a dto (transfer data, no behavior, easily serializable, very clean ect), and create another class to act as your view model.
In the beginning these classes would probably be very similar, possibly the same..but if you make changes over time, or your view needs to display different things, you would just change your view model, and populate it from other dtos, or tranform your data however you need to. You could also then get rid of the properties on your view model that your view doesn't need.. (unless your view magically maps directly to every property on your current dto). It's a bit more work up front but if you're working on a big long living project I think you'd be happy you did it later on.
So you would populate your domain models using EF in your data layer, you would then use your dto and transfer that data to the Biz layer, do w/e you need there, then use your dto (could be the same one) to transfer your data to your presentation layer (mvc), and populate your view model from the dtos you receive.
Anyway that's my take on it.
I have a Data layer and Business layer in my App.
In data layer I have imported database tables as objects using entity framework.
One of them is Unit table for example.
Now in business layer I want to add some methods to Unit of data layer so I have this class:
namespace Business.Entity
{
public class Unit : Data.Unit
{
//Some business affairs here
}
}
And for loading units in UI I have created a repository in business layer :
public static IEnumerable<Data.Unit> LoadUnits()
{
return from entity in StaticDataContext.Units select entity;
}
Everything is good till now.
But I want to load a list of Business.Unit in UI so I wrote this one:
public static IEnumerable<Business.Entity.Unit> LoadUnits()
{
var result = (from entity in StaticDataContext.Units
select entity).ToList().Cast<Business.Entity.Unit>();
return result;
}
It compiles well but then I get this runtime error when binding it to a Grid:
InvalidCastException: Unable to cast object of type 'Data.Unit' to type 'Business.Entity.Unit'
Can any one say how to arrange the classes to be able to load business classes in UI ?
Thanks
You can not directly cast parent object to child object. Possible solutions for your problem:
Create in Business.Entity.Unit class a constructor accepting Data.Unit as argument and assigning all necessary properties, like:
namespace Business.Entity
{
public class Unit : Data.Unit
{
public Unit(Data.Unit parent)
{
// Assign proprties here
}
//Some business affairs here
}
}
After that you can do:
public static IEnumerable<Business.Entity.Unit> LoadUnits()
{
var result = (from entity in StaticDataContext.Units
select new Business.Entity.Unit(entity)).ToList().Cast<Business.Entity.Unit>();
return result;
}
Rewrite your Business.Entity.Unit class so that it does not inherit Data.Unit, but accepts Data.Unit entity as a constructor parameter, aggregates it (stores in a local private member) and presents wrapper properties and functions to operate on it
Remove your Business.Entity.Unit class entirely and implement all additional methods as extension methods.
I'd vote for the third one because IMHO it leaves code a bit cleaner and does not have overhead of introducing and managing additional entity.
Try following approach, return your class Business.Entity.Unit objects list rather than casting to another class.
public static IEnumerable<Business.Entity.Unit> LoadUnits()
{
var result = (from entity in StaticDataContext.Units
select new Business.Entity.Unit {
///set properties of this class as
Id = entity.ID, .. son
}).ToList();
return result;
}
I suggest you to go through ScottGu's article on Code-First Development with Entity Framework 4.
I hope this makes sense. I have a ASP.NET web application that uses Entity Framework. I have added a couple of custom tables to the db and created a separate project to handle the CRUD operations for those tables. I chose the separate project because I don't want future upgrades to the application to overwrite my custom features.
My problem is this. How do I attach/combine my custom ObjectContext to the ObjectContext of the application? I want to use the same UnitOfWorkScope (already in the application) to maintain the one ObjectContext instance per HTTP request. Again, I don't want to add my ObjectSets to the application's ObjectContext for my reason listed above.
Here is some code:
Widget.cs
public partial class Widget
{
public Widget()
{
}
public int WidgetId {get;set;}
public string WidgetName {get;set;}
}
WidgetObjectContext.cs
public partial class WidgetObjectContext : ObjectContext
{
private readonly Dictionary<Type, object> _entitySets;
public ObjectSet<T> EntitySet<T>()
where T : BaseEntity
{
var t = typeof(T);
object match;
if(!_entitySets.TryGetValue(t, out match))
{
match = CreateObjectSet<T>();
_entitySets.Add(t, match);
}
return (ObjectSet<T>)match;
}
public ObjectSet<Widget> Widgets
{
get
{
if((_widgets == null))
{
_widgets = CreateObjectSet<Widget>();
}
return _widget;
}
}
private ObjectSet<Widget> _widgets;
In my WidgetManager class if I was using the application's ObjectContext I would query my tables like this:
var context = ObjectContextHelper.CurrentObjectContext;
var query = from c in context.ObjectSet .... etc
What I want would be to do something like this:
var context = ObjectContextHelper.CurrentObjectContext.Attach(WidgetObjectContext);
I know this won't work but that is the gist of what I am trying to accomplish. Hope this is clear enough. Thanks.
I don't think it is possible. ObjectContext creates entity connection which connects to metadata describing mapping and database. But you have to different sets of metadata - one for ASP.NET application and one for separate project. Simply you need two connection to work with these models => you need two ObjectContexts.
FYI: The previous answer was correct at the time of the answer. It is now possible to do this using the DbContext available in EF 4.1. The caveat is that you must use the code-first strategy in order to build your custom context. In other words, you won't be able to use EDMX files to accomplish this.