Goal: to save ViewModel object by Entity Framework. I have UserViewModel object which has list of UnitViewModel. Then, I have a UserAdapter class which converts UserViewModel into Entity Framework User object (see Convert()below how).
Now, my question is how do I convert this list of UnitViewModel to its corresponding Entity Framework Unit list? - Do I have to get each object from DB Context by calling something like context.Units.Where(u=>myListofUnitIDs.Contains(u.UnitID))?
public class UserViewModel
{
public Guid? UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? CreateTime { get; set; }
public List<UnitViewModel> UserUnits { get; set; }
}
public class UnitViewModel
{
public Guid UnitID { get; set; }
public string Name { get; set; }
public int? SortIndex { get; set; }
public DateTime CreateTime { get; set; }
public bool Assigned { get; set; }
}
public class UserAdapter
{
public static User Convert(UserViewModel userView)
{
User user;
if (userView.UserID.HasValue)
{
using (var provider = new CoinsDB.UsersProvider())
{
user = provider.GetUser(userView.UserID.Value);
}
}
else
{
user = new User();
}
user.FirstName = userView.FirstName;
user.LastName = user.LastName;
user.Password = StringHelper.GetSHA1(userView.Password);
user.UserName = user.UserName;
user.CreateTime = DateTime.Now;
// Problem here :)
// user.Units = userView.UserUnits;
return user;
}
}
UPDATE: The main concern here is that I have to retrieve each Unit from database to match (or map) it with ViewModel.Unit objects, right? Can I avoid it?
For your information, this operation is called as Mapping mainly. So, you want to map your view model object to the entity object.
For this, you can either use already existed 3rd party library as AutoMapper. It will map properties by reflection which have same name. Also you can add your custom logic with After method. But, this approach has some advantages and disadvantages. Being aware of these disadvantages could help you to decide whether you must use this API or not. So, I suggest you to read some articles about advantages and disadvantages of AutoMapper especially for converting entities to other models. One of such disadvantages is that it can be problem to change the name of one property in the view model in the future, and AutoMapper will not handle this anymore and you won't get any warning about this.
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit = Mapper.Map<UnitViewModel, UserUnit>(item);
user.Units.Add(userUnit);
}
So, I recommend to write your custom mappers.
For example, I have created a custom library for this and it maps objects lik this:
user.Units = userView.UserUnits
.Select(userUnitViewModel => userUnitViewModel.MapTo<UserUnit>())
.ToList();
And I am implementing these mapping functions as:
public class UserUnitMapper:
IMapToNew<UnitViewModel, UserUnit>
{
public UnitViewModel Map(UserUnit source)
{
return new UnitViewModel
{
Name = source.Name,
...
};
}
}
And then in runtime, I am detecting the types of the objects which will be used during mapping, and then call the Map method. In this way, your mappers will be seperated from your action methods. But, if you want it urgently, of course you can use this:
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit= new UserUnit()
{
Name = item.Name,
...
};
user.Units.Add(userUnit);
}
Related
We are using AutoMapper (9.0.0) in .net core for mapping values between source and destination. Till time this is working fine. However, we need to keep some of the values in destination as it is after mapping.
We have tried to used UseDestinationValue() and Ignore() methods on member, but it is not preserving the existing values. Below is the code for the same.
RequestModel
public class RequestModel
{
public int Id { get; set; }
public int SubmittedById { get; set; }
public string Description { get; set; }
public string Location { get; set; }
}
RequestDto
public class RequestDto
{
public int Id { get; set; }
public int SubmittedById { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public string SubmittedByName { get; set; }
}
We are accepting Dto in API as request parameter
API
[HttpPost]
public IActionResult Save([FromBody] RequestDto requestDto)
{
// Some logic to save records
}
So, before saving the records we are mapping RequestDto to RequestModel and passing that model to DAL layer to save the records like this
var requestModel = MapperManager.Mapper.Map<RequestDto, RequestModel>(RequestDto);
And call to data layer
var requestModel = DAL.Save(RequestModel)
So, after receiving the updated request model we are again mapping it to requestDto, in this case we are loosing the value for SubmittedByName property.
return MapperManager.Mapper.Map<RequestModel, RequestDto>(requestModel);
Mapper Class
public class RequestProfile: Profile
{
public RequestProfile()
{
CreateMap<RequestModel, RequestDto>()
CreateMap<RequestDto, RequestModel>()
}
}
This SubmittedByName column is not present in the Request table, but we want to utilize its value after saving the records.
So, how can we preserve the destination value after mapping.
Any help on this appreciated !
I think you have to use the Map overload that accepts destination.
This works for me, using same model / dto you posted, in a console application:
var config = new MapperConfiguration(cfg => cfg.CreateMap<RequestModel, RequestDto>().ReverseMap());
var mapper = config.CreateMapper();
var source = new RequestDto
{
Id = 1,
SubmittedById = 100,
SubmittedByName = "User 100",
Description = "Item 1",
Location = "Location 1"
};
Console.WriteLine($"Name (original): {source.SubmittedByName}");
var destination = mapper.Map<RequestDto, RequestModel>(source);
Console.WriteLine($"Name (intermediate): {source.SubmittedByName}");
source = mapper.Map<RequestModel, RequestDto>(destination, source);
Console.WriteLine($"Name (final): {source.SubmittedByName}");
The standard Map method creates a new object but the overloaded method uses existing object as destination.
We have tried to used UseDestinationValue() and Ignore() methods on member, but it is not preserving the existing values. Below is the code for the same.
since that didn't work for you
I would suggest creating a generic class like this (assuming you have multiple classes of RequestDto)
class RequesterInfo<T>
{
public string RequesterName { get; set; } // props you want to preserve
public T RequestDto { get; set; } // props to be mapped
}
by keeping the mapping as it is,
and modifying your code to something like this:
var requestModel = MapperManager.Mapper.Map<RequestDto, RequestModel>(RequesterInfo.RequestDto);
so what happens is that you modify the T RequestDto part of the object without modifying other properties.
I have a repository for a DocumentDb database. My documents all have a set of common properties so all documents implement the IDocumentEntity interface.
public interface IDocumentEntity {
[JsonProperty("id")]
Guid Id { get; set; }
[JsonProperty("documentClassification")]
DocumentClassification DocumentClassification { get; set; }
}
public class KnownDocument : IDocumentEntity {
[JsonProperty("id")]
Guid Id { get; set; }
[JsonProperty("documentClassification")]
DocumentClassification DocumentClassification { get; set; }
[JsonProperty("knownProperty")]
string KnownProperty { get; set; }
}
public class BaseDocumentRepository<T> where T : IDocumentEntity {
public Set(T entity) {
// ... stuff
}
}
This works fine with a KnownDocument where I know all of the properties. But, of course, what's great about a Document Db is that I don't need to know all of the properties (and in many cases I won't).
So my client submits something like this-
{unknownProperty1: 1, unknownProperty2: 2}
And I want to upsert this using my document repository.
public OtherDocumentService() {
_otherDocumentService = new OtherDocumentRepository();
}
public UpsertDocument(dynamic entity) {
entity.id = new Guid();
entity.documentClassification = DocumentClassification.Other;
_otherDocumentRepository.Set(entity);
}
But I get an InvalidCastException from dynamic to IDocumentEntity. I assume it's because of the extra properties that exist on the dynamic object but not on the IDocumentEntity interface?
What I'm trying to do is leave my document entities open to be dynamic, but rely on a few properties being there to maintain them.
Entity parameter passed to the UpsertDocument should explicitly implement IDocumentEntity in order do make the code works, it is not enough just have a Id property.
Some options:
1) Proxy may be applied:
public class ProxyDocumentEntity : IDocumentEntity
{
public dynamic Content { get; private set; }
public ProxyDocumentEntity(dynamic #content)
{
Content = #content;
}
public Guid Id
{
get { return Content.Id; }
set { Content.Id = value; }
}
}
... using
public void UpsertDocument(dynamic entity)
{
entity.Id = new Guid();
repo.Set(new ProxyDocumentEntity(entity));
}
The stored document will have nested Object property, which may be not acceptable
2)There is a lib https://github.com/ekonbenefits/impromptu-interface which creates a proxy dynamically
and does not make extra property like solution above.
Drawback will be in performance.
Technically it could be 2 methods:
public void UpsertDocument(IDocumentEntity entity){...}
public void UpsertDocument(dynamic entity){...}
so the first (fast) will work for the objects which implement IDocumentEntity and second(slow) for the rest of the objects.
But this is a speculation a bit , as I dunno the details of the whole code base of the project you have.
If you have some flexibility as to how to name those dynamic properties, you could stuff them into a Dictionary property on your object:
public Dictionary<string, dynamic> extra { get; set; }
Probably the questin title is not self-explanationary.
I have an ASP.NET MVC5 project with Entity Framework 6. I use code first and I've implemented a TPH pattern for an entity.
There's a base Request entity (I've removed most fields, it's just an example).
public class Request
{
public int Id { get; set; }
public string Title { get; set; }
}
also there's some models with exclusive properties that extend it:
public class RequestQuestion : Request
{
public string Question { get; set; }
public string Answer { get; set; }
}
public class RequestForWork : Request
{
public string WorkName { get; set; }
}
Each of them is added to the EntityContext:
public DbSet<Request> Requests { get; set; }
public DbSet<RequestQuestion> RequestQuestions { get; set; }
public DbSet<RequestForWork> RequestForWorks { get; set; }
When I create some of the requests I add them like this:
var db = new EntityContext();
var requestQuestion = new RequestQuestion{ some initialization };
this.db.Requests.Add(requestQuestion);
this.db.SaveChanges();
And here comes the question. When I query requests of the user
var requests = this.db.Students.Find(userId).Requests.ToList();
in debug I can access the properties of the extending class for every request through the base. So, is there a way to somehow get a type of class that is extending the selected entity and to access it's properties?
Currently to build a list of all requests and fill some viewmodel with data I need to seperately select every type of request and fill a global list from these seperate selects.
You need to cast the base type to its subtype and test for null
foreach (r in requests)
{
var rq = r as RequestQuestion;
if(rq != null)
{
string rq = rq.Question
}
var rfw = r as RequestForWork;
if(rfw != null)
{
string wn = rfw.WorkName;
}
}
I am converting a string into a list and then trying to use entity framework to insert it into a DB. The issue that I am having is that I don't know how to save the changes to the DB.
This is the code that I am trying to use and is where the string is converted to a list:
if (intCounter == 0)
{
return JsonConvert.DeserializeObject<List<foo>>(jsonString).Cast<T>().ToList();
}
Then in a seperate class below.
ConvertJson.Convert<CouncilEvent>(strResponseJSONContent, intCounter);
The Entity Framework Model that I am trying to use for the list.
namespace foo.Models
{
public partial class foo
{
public foo()
{
this.EventDates = new List<EventDate>();
}
public System.Guid foo_PK { get; set; }
public string EntityID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public bool Adult { get; set; }
}
}
The class foo contains properties that match those in the string.
It is this foo that I am then trying to insert into the DB. foo is also part of my Entity Framework model.
I have never used a list in this situation before and I thought it would just be a matter of using db.SaveChanges() but that doesn't seem to work. Where would I place the necessary lines of code such as using (db = new contextFoo) and db.SaveChanges(). Also do I need to add the items? I haven't because I thought I was already adding them to the class and therefore didn't need to do this manually?
db.SaveChanges() will only 'update' your database to what was changed. So, you need to add something to the database, and then call SaveChanges() for it to work.
You can loop the list to add the objects to the context, then call SaveChanges()...
var councilEvents = ConvertJson.Convert<CouncilEvent>(strResponseJSONContent, intCounter);
using (var db = new contextFoo())
{
foreach (var councilEvent in councilEvents)
{
db.CouncilEvents.Add(councilEvent);
}
db.SaveChanges();
}
I just want to get some opinions on the most efficient and quickest way to populate my composite class using the EF4.0. I have a parent class which has a structure similar to the class below. It mirrors my database structure.
public class Person
{
public string FirstName { get; set; }
........
public Address WorkAddress { get; set; }
public IList<Account> Workspace { get; set; }
}
public class Address
{
public string FirstName { get; set; }
......
}
public class Account
{
public string SortCode { get; set; }
public IList<string> TransactionHistory {get; set;}
......
}
So, at this moment in time I pull back all the 'Persons' from the EF and I loop through each of them and populate the Address and Accounts for each. Lazy loading is enabled, so I have to encapsulate all my loops in the using statement or my Accounts will be empty when I try to iterator through them. So, can I disable lazy loading for this call only or should approach the population of my list of persons in another manner.
using (var entities = new PersonEntities())
{
var dbPeople = (from person in entities.Persons
select person).ToList();
foreach(var person in dbPeople)
{
foreach(var account in person.Accounts)
{
// In here I populate my 'Person' business object account and add it to my collection to return.
}
}
}
If I got you right, you're going to include the relation keeping the LazyLoading enabled. You can do this for the query using the Include method:
using (var entities = new PersonEntities())
{
var dbPeople = entities.Persons.Include("Accounts").ToList();
foreach(var person in dbPeople)
{
//Do nothing with Accounts if the relation is mapped correct
}
}
EDIT:
Also you can disable LazyLoading for the created PersonEntities instance's lifetime:
using (var entities = new PersonEntities())
{
entities.Configuration.LazyLoadingEnabled = false;
//...
}