I use .Select(i=> new T{...}) after every db hit manually to convert my entity objects into DTO object. Here are some sample entities and DTOS
User Entity;
public partial class User
{
public int Id { get; set; }
public string Username { get; set; }
public virtual UserEx UserEx { get; set; }
}
User DTO;
public class UserDTO
{
public int Id { get; set; }
public string Username { get; set; }
}
UserEx entity;
public class UserEx
{
public int UserId { get; set; }
public string MyProperty1 { get; set; }
public virtual User User { get; set; }
}
UserEx DTO;
public class UserExDTO
{
public int MyProperty1 { get; set; }
public UserDTO UserModel { get; set; }
}
My Conversion Expression Methods;
public static class ConversionExpression
{
public static Expression<Func<UserEx, UserExDTO>> GetUserExDTOConversionExpression()
{
return userEx => new UserExDTO
{
MyProperty1 = userEx.MyProperty1,
UserModel = new UserDTO
{
Id = userEx.User.Id,
Username = userEx.User.Username
}
};
}
public static Expression<Func<User, UserDTO>> GetUserDTOConversionExpression()
{
return user => new UserDTO
{
Id = user.Id,
Username = user.Username
};
}
}
And my current usage for UserDTO;
myContext.Users
.Select(ConversionExpression.GetUserDTOConversionExpression())
.ToList();
for UserExDTO;
myContext.UserExes
.Select(ConversionExpression.GetUserExDTOConversionExpression())
.ToList();
Apologize for long introduction, now here is my question ;
I need to group
new UserDTO
{
Id = userEx.User.Id,
Username = userEx.User.Username
}
due to single point of concerns. So I want something like this;
public static Expression<Func<UserEx, UserExDTO>> GetUserExDTOConversionExpression()
{
return userEx => new UserExDTO
{
MyProperty1 = userEx.MyProperty1,
//this line does not behave like the other one
UserModel = userEx.User.GetUserDTOConversionExpression()
};
}
Is there any way to do that or should I write down every expression individual and nonrelated to similar needs?
I've solved my issue and beyond only with NeinLinq. Here is my solution;
You need to remove nested declarations first.
public static Expression<Func<UserEx, UserExDTO>> GetUserExDTOConversionExpression()
{
return userEx => new UserExDTO
{
MyProperty1 = userEx.MyProperty1
//We removed other model declaration here.
};
}
Then use To method of NeinLinq to define the translation;
public static Expression<Func<UserEx, UserExDTO>> GetUserExDtOCompbinedExpression()
{
//Translate() and To() methods do all the job
return GetUserDTOConversionExpression().Translate()
.To(userEx => userEx.User, userExDTO => userExDTO.UserModel, GetUserExDTOConversionExpression());
}
Related
I have the following DTO:
public class QuestionGroupDTO : IBaseDTO
{
public string Description { get; set; }
public string Header { get; set; }
public Guid Id { get; set; }
public int Order { get; set; }
public IEnumerable<Services.Forms.Models.RelationForm_QuestionGroupDTO> RelationForms_QuestionGroups { get; set; }
public IEnumerable<RelationQuestionGroup_QuestionDTO> RelationQuestionGroups_Questions { get; set; }
}
I have problem with the RelationQuestionGroups_Questions while converting.
Here Is how my RelationQuestionGroup_QuestionDTO looks like
public class RelationQuestionGroup_QuestionDTO
{
public int Order { get; set; }
[Required]
public Guid QuestionGroupId { get; set; }
[Required]
public Guid QuestionId { get; set; }
public virtual QuestionGroupDTO QuestionGroup { get; set; }
public virtual QuestionDTO Question { get; set; }
}
Here Is how I convert:
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
Id = src.Id,
Header = src.Header,
Description = src.Description,
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.ToList()
};
return dto;
}
As you can see, I'm trying to just assign It and make a list of It, but I got a casting error here. I'm not sure how to do this.
I get the following error:
Cannot implicity convert type Generic List to System.Collections.Generic.IEnumerble
You're having a great start at mapping, but at RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.ToList(), you're trying to assign a List<Entity> to List<Dto>. You can't do that.
You need to map any non-primitive properties as well. You can do that like this:
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
// ...
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions
.Select(ToDTO)
.ToList()
};
return dto;
}
Then you add a method to map RelationQuestionGroups_Question to RelationQuestionGroups_QuestionDTO:
public RelationQuestionGroups_QuestionDTO ToDTO(RelationQuestionGroups_Question entity)
{
return new RelationQuestionGroups_QuestionDTO
{
Order = entity.Order,
// ...
};
}
And then you'll go look at AutoMapper to automate this.
You forgot to map RelationQuestionGroups_Questions to RelationQuestionGroup_QuestionDTO.
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
Id = src.Id,
Header = src.Header,
Description = src.Description,
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.Select(rq => new RelationQuestionGroup_QuestionDTO
{
Order = rq.Order,
QuestionGroupId = rq.QuestionGroupId,
QuestionId = rq.QuestionId
}).ToList()
};
return dto;
}
I have created classes using EF Code First that have collections of each other.
Entities:
public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<AppUser> Teachers { get; set; }
public Field()
{
Teachers = new List<AppUser>();
}
}
public class AppUser
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public virtual List<Field> Fields { get; set; }
public AppUser()
{
Fields = new List<FieldDTO>();
}
}
DTOs:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<AppUserDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<AppUserDTO>();
}
}
public class AppUserDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
Mappings:
Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();
And I am getting StackOverflowException when calling this code (Context is my dbContext):
protected override IQueryable<FieldDTO> GetQueryable()
{
IQueryable<Field> query = Context.Fields;
return query.ProjectTo<FieldDTO>();//exception thrown here
}
I guess this happens because it loops in Lists calling each other endlessly. But I do not understand why this happens. Are my mappings wrong?
You have self-referencing entities AND self-referencing DTOs. Generally speaking self-referencing DTOs are a bad idea. Especially when doing a projection - EF does not know how to join together and join together and join together a hierarchy of items.
You have two choices.
First, you can force a specific depth of hierarchy by explicitly modeling your DTOs with a hierarchy in mind:
public class FieldDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<TeacherDTO> Teachers { get; set; }
public FieldDTO()
{
Teachers = new List<TeacherDTO>();
}
}
public class TeacherDTO
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string UserName => Email;
}
public class AppUserDTO : TeacherDTO
{
public List<FieldDTO> Fields { get; set; }
public AppUserDTO()
{
Fields = new List<FieldDTO>();
}
}
This is the preferred way, as it's the most obvious and explicit.
The less obvious, less explicit way is to configure AutoMapper to have a maximum depth it will go to traverse hierarchical relationships:
CreateMap<AppUser, AppUserDTO>().MaxDepth(3);
I prefer to go #1 because it's the most easily understood, but #2 works as well.
Other option is using PreserveReferences() method.
CreateMap<AppUser, AppUserDTO>().PreserveReferences();
I use this generic method:
public static TTarget Convert<TSource, TTarget>(TSource sourceItem)
{
if (null == sourceItem)
{
return default(TTarget);
}
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var serializedObject = JsonConvert.SerializeObject(sourceItem, deserializeSettings);
return JsonConvert.DeserializeObject<TTarget>(serializedObject);
}
...
MapperConfiguration(cfg =>
{
cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
...
When you giving 1 navigation_property to 2nd entity and visa-versa it go in an infinite loop state. So, the compiler automatically throws a Stackoverflow exception.
So, to avoid that, you just need to remove one navigation_property from any of the entities.
I have a 3 level entity and I am having some trouble saving it.
public partial class Event
{
public Event()
{
Recurrences = new HashSet<Recurrence>();
}
public int Id { get; set; }
public string Title { get; set; }
public ICollection<Recurrence> Recurrences { get; set; }
}
public partial class Recurrence
{
public Recurrence()
{
AspNetUsers = new HashSet<AspNetUser>();
}
public int Id { get; set; }
public int EventId { get; set; }
public string Venue { get; set; }
public ICollection<AspNetUser> AspNetUsers { get; set; }
}
public partial class AspNetUser
{
public AspNetUser()
{
Recurrences = new HashSet<Recurrence>();
}
public string Id { get; set; }
public string UserName { get; set; }
public ICollection<Recurrence> Recurrences { get; set; }
}
The Post controller for the Event Class looks like this:
// POST: api/Events
[ResponseType(typeof(Event))]
public async Task<IHttpActionResult> PostEvent(EventDTO #event)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var newEvent = new Event();
newEvent.Title = #event.Title;
newEvent.EventTypeId = #event.EventTypeId;
var recurrence = new Recurrence();
recurrence.Venue = #event.Venue;
var users = db.AspNetUsers.Where(u => #event.UserId.Contains(u.Id));
foreach (var u in users)
recurrence.AspNetUsers.Add(u);
newEvent.Recurrences.Add(recurrence);
db.Events.Add(newEvent);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = newEvent.Id }, newEvent);
}
When I call the PostEvent method with an Event like DTO i get the http error code 500.
first, how can i add exception handling to capture the specific error and secondly, what I'm I doing wrong here?
Any help is deeply appreciated.
Thanks.
I have coded methods looking like this:
var result = await db.TaskStatuses
.Select(e => new
{
Id = e.TaskStatusId,
Name = e.Name
})
.ToListAsync();
and
var result = await db.ContentStatuses
.Select(e => new
{
Id = e.ContentStatusId,
Name = e.Name
})
.ToListAsync();
var result = await db.<xxx>
.Select(e => new
{
Id = e.<xxx>Id,
Name = e.Name
})
.ToListAsync();
All the methods are the same except for the database object they get data from. Is there a way that I could combine all these into one method where I just pass in the object as a parameter?
Here's how db is created:
private myContext db = new MyContext();
public partial class MyContext : DbContext
{
public DbSet<TaskStatuse> TaskStatuses { get; set; }
public DbSet<ContentStatuses> ContentStatuses { get; set; }
Here's the full code of a controller (Asp.Net Web-API):
namespace WebRole1.Controllers
{
[Route("api/ContentStatus/{id?}", Name = "api_ContentStatus")]
public class ContentStatusController : ApiController
{
private MyContext db = new MyContext();
public async Task<IHttpActionResult> GetMapData()
{
var result = await db.ContentStatuses
.Select(e => new
{
Id = e.ContentStatusId,
Name = e.Name
})
.ToListAsync();
return Ok(result);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
My problem is I have many of these controllers and they are all the same except for the data source. ContentStatus, TaskStatus etc.
Sample classes:
public class ContentStatus
{
public ContentStatus()
{
this.Contents = new List<Content>();
}
public int ContentStatusId { get; set; }
public string Name { get; set; }
public virtual ICollection<Content> Contents { get; set; }
}
public class TaskStatus
{
public TaskStatus()
{
this.Tasks = new List<Task>();
}
public int TaskStatusId { get; set; }
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
You can create a common interface for your classes that exposes the properties you need in your methods. You can then consume this interface in a single method that can deal with items of either type
public interface IIdentifiableStatus
{
int Id { get; }
string Name { get; }
}
public class ContentStatus : IIdentifiableStatus
{
public ContentStatus()
{
this.Contents = new List<Content>();
}
public int ContentStatusId { get; set; }
public string Name { get; set; }
public virtual ICollection<Content> Contents { get; set; }
int IIdentifiable.Id
{
get { return ContentStatusId; }
}
string IIdentifiable.Name
{
get { return Name; }
}
}
public class TaskStatus : IIdentifiableStatus
{
public TaskStatus()
{
this.Tasks = new List<Task>();
}
public int TaskStatusId { get; set; }
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
int IIdentifiable.Id
{
get { return TaskStatusId; }
}
string IIdentifiable.Name
{
get { return Name; }
}
}
Your method would then look like this:
public async Task<IHttpActionResult> GetMapData(IEnumerable<IIdentifiableStatus> statuses)
{
var result = await statuses
.Select(e => new {
Id = e.Id,
Name = e.Name
})
.ToListAsync();
return Ok(result);
}
You could call the method like so:
GetMapData(db.ContentStatuses)
or:
GetMapData(db.TaskStatuses)
Hi I am new to entity framework. I have following objects
public partial class lr
{
public lr()
{
this.lr_history = new HashSet<lr_history>();
this.people = new HashSet<person>();
}
public int _id { get; set; }
public string name { get; set; }
public string notes { get; set; }
public virtual ICollection<lr_history> lr_history { get; set; }
public virtual ICollection<person> people { get; set; }
In my Web Api controller I have following code
public class lrController : ApiController
{
private CTM db = new CTM();
// GET api/addL
public IEnumerable<lr> Getlr()
{
from a in DbContext.Activities
return db.lr.AsEnumerable();
}
In above Get method it returns lr but i want to return my own object like
lr.id
lr.name
lr_history.description
lrh_history.rate
Can anyone show me how to achieve this? Than
Create a new model:
class HistoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public string HistoryDescription { get; set; }
public string HistoryRate { get; set; }
}
and return that:
public IEnumerable<HistoryModel> Getlr()
{
from a in DbContext.Activities
return db.lr.AsEnumerable()
.Select(x => new HistoryModel
{
Id = x.id,
Name = x.name,
HistoryDescription = x.history.description,
HistoryRate = x.history.rate
});
}
Instade of return db.lr.AsEnumerable();
try this
return db.lr.ToList();