my simple app has 10 tables, each of these tables has same propery "CreatedDate" which is a DateTime prop and as its name says its holding creation date.
Since I'm fetching all the data through DTO's for example if I want to get articles from database I'm mapping it to ArticleDto and returning data to the user.
And I'm doing that all for each 10 classes-entites.
And each Dto, (ArticleDto, GroupDto, UserDto, TownDto, AddressDto) all of them now have DateTime property which I'm populating when retrieving data from database..
Is it possible to achieve somehow that this prop is automatically populated ?
This is how I am doing it right now for Towns for example:
public class TownGetDto : DateTimeGetDto
{
public long Id { get; set; }
public string Title { get; set; }
public string ZipCode { get; set; }
public string DialingCode { get; set; }
public long CountryId { get; set; }
}
public class DateTimeGetDto
{
public DateTime CreatedDate { get; set; }
}
public async Task<TownGetDto> GetTownsAsync(CancellationToken cancellationToken)
{
var towns = await _dbContext.Towns
.Select(x => new TownGetDto
{
Id = x.Id,
Title = x.Title,
DialingCode = x.DialingCode,
ZipCode = x.ZipCode,
CountryId = x.CountryId,
CreatedDate = x.CreatedDate // How to get rid of this prop?
})
.ToListAsync(cancellationToken);
return towns;
}
As you can see I am popualting CreatedDate in Select and I'm doing that for all my classes/entities..
Is it possible somehow to fill-populate this prop automatically ?
Thanks everyone,
Cheers
You still need to specify to each of the 10 classes where the DateTime value should come from, but it might be cleaner to use constructors and inheritance
In DateTimeGetDto:
public DateTime CreatedDate { get; set; }
public DateTimeGetDto(DateTime dateTime)
{
CreatedDate = dateTime;
}
In TownGetDto:
public class TownGetDto : DateTimeGetDto
{
public long Id { get; set; }
public string Title { get; set; }
public string ZipCode { get; set; }
public string DialingCode { get; set; }
public long CountryId { get; set; }
public TownGetDto(Town town) : base(town.CreatedDate)
{
Id = town.Id;
Title = town.Title;
DialingCode = town.DialingCode;
ZipCode = town.ZipCode;
CountryId = town.CountryId;
CreatedDate = town.CreatedDate;
}
public async Task<TownGetDto> GetTownsAsync(CancellationToken cancellationToken)
{
var towns = await _dbContext.Towns
.Select(x => new TownGetDto(x))
.ToListAsync(cancellationToken);
return towns;
}
}
Related
I don't want to use .include to get the entire child tables. I just need select columns.
public class ProjectTypeDTO {
public string Type { get; set; }
}
public class CourseDTO {
public string CourseCode { get; set; }
public string CourseTitle { get; set; }
}
public class ProjectDTO {
public int Id { get; set; }
public ProjectTypeDTO ProjectType { get; set; }
public CourseDTO Course { get; set; }
public string StartTerm { get; set; }
public DateTime SignOff { get; set; }
public DateTime StartDateTime { get; set; }
}
[HttpGet("getallprojects")]
public IActionResult GetAllProjects()
{
var projects = _context.Projects
.Select(p => new ProjectDTO
{
Id = p.Id,
ProjectType = { Type = p.ProjectType.Type },
Course = { CourseCode = p.Course.CourseCode, CourseTitle = p.Course.CourseTitle },
StartTerm = p.StartTerm,
SignOff = p.SignOff,
StartDateTime = p.StartDateTime,
}).ToList();
return Ok(projects);
}
This is throwing a "NotImplementedException: The method or operation is not implemented." error.
I've tested it as an anonymous function and it works.
var projects = _context.Projects
.Select(p => new
{
p.Id,
p.ProjectType.Type,
p.SignOff,
p.StartDateTime,
p.Course.CourseCode,
p.Course.CourseTitle,
p.StartTerm
}).ToList();
An anonymous type won't work for my app, since I need to be able to make changes to this data before it gets returned.
Based on other examples I've seen here and other sites, this looks correct. Could it be a bug?
I haven't seen that syntax for sub-objects before. eg:
ProjectType = { Type = p.ProjectType.Type }
I believe that should be:
ProjectType = new ProjectTypeDTO{ Type = p.ProjectType.Type }
Is there any way to somehow combine the data from two models and THEN map them both to the same viewModel in the context of an edit action?
I have never had to update several tables at once in an edit action in ASP.NET MVC with Entity Framework 6.1.3. This is the layout:
I have a DB table called "Address" which has fields for StreetNumber, StreetName, City, State, ZipCode. It has a one-to-one relationship with another table called Bars. As in, a bar can only have one address and one address can only have one bar.
Because I am storing this data in two separate tables, I am having a very difficult time trying to successfully implement an Edit action which takes data from one form (BarForm) and should update both the Bar and Address database tables. See my code:
BarController
public ActionResult Edit(int id)
{
var bar = _context.Bars.SingleOrDefault(m => m.Id == id);
var address = _context.Addresses.SingleOrDefault(a => a.BarId == id);
//Make sure that the id actually exists:
if (bar == null)
{
return HttpNotFound();
}
var viewModel = Mapper.Map<Bar, BarFormViewModel>(bar, new BarFormViewModel());
if (address == null)
{
address = new Address();
}
Mapper.Map<Address, BarFormViewModel>(address, viewModel);
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
[ValidateAntiForgeryToken]
public ActionResult Save(BarFormViewModel bar)
{
if (!ModelState.IsValid)
{
var viewModel = Mapper.Map<BarFormViewModel, BarFormViewModel>(bar, new BarFormViewModel());
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
if (bar.Id == 0)
{
var newbar = Mapper.Map<BarFormViewModel, Bar>(bar);
newbar.LastUpdated = DateTime.UtcNow;
_context.Bars.Add(newbar);
var addressToAdd = Mapper.Map<BarFormViewModel, Address>(bar);
_context.Addresses.Add(addressToAdd);
}
else
{
var barInDb = _context.Bars.Single(b => b.Id == bar.Id);
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Mapper.Map<BarFormViewModel, Bar>(bar, barInDb);
Mapper.Map<BarFormViewModel, Address>(bar, addressInDb);
}
_context.SaveChanges();
return RedirectToAction("Index", "Bar");
}
Domain Models:
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public string GooglePlaceId { get; set; }
public string SundayDiscounts { get; set; }
public string MondayDiscounts { get; set; }
public string TuesdayDiscounts { get; set; }
public string WednesdayDiscounts { get; set; }
public string ThursdayDiscounts { get; set; }
public string FridayDiscounts { get; set; }
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime LastUpdated { get; set; }
}
public class Address
{
public int Id { get; set; }
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int ZipCode { get; set; }
public Bar Bar { get; set; }
public int BarId { get; set; }
}
View Model which includes both Address and Bar properties:
{
public class BarFormViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
[Required]
[Display(Name = "Google Place ID")]
public string GooglePlaceId { get; set; }
[Display(Name = "Sunday Happy Hour Info:")]
public string SundayDiscounts { get; set; }
[Display(Name = "Monday Happy Hour Info:")]
public string MondayDiscounts { get; set; }
[Display(Name = "Tuesday Happy Hour Info:")]
public string TuesdayDiscounts { get; set; }
[Display(Name = "Wednesday Happy Hour Info:")]
public string WednesdayDiscounts { get; set; }
[Display(Name = "Thursday Happy Hour Info:")]
public string ThursdayDiscounts { get; set; }
[Display(Name = "Friday Happy Hour Info:")]
public string FridayDiscounts { get; set; }
[Display(Name = "Saturday Happy Hour Info:")]
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime? LastUpdated { get; set; }
//Address Model Info
public Address Address { get; set; }
public int? AddressId { get; set; }
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Must be a number")]
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int? ZipCode { get; set; }
public bool IsNew { get; set; }
}
The problem here is that I am getting an empty AddressId with this setup, which is causing an exception when the Save action gets run. This is because the BarForm view is getting passed a ViewModel which has been mapped from a Bar object and the Bar domain model actually has no Address information in it, since it is not the Address model/table.
Is there any way to somehow combine the data from both the Address and Bar models and THEN map them both to the same viewModel?
I keep getting a Sequence Contains no Elements error for this line in the Save action:
var addressInDb = _context.Addresses.Single(a => a.Id == bar.AddressId);
I also tried:
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Neither work. I understand what the error is saying and have also checked the actual HTML for my hidden Addressid field and it is blank... See code in my BarForm View:
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.AddressId)
#Html.AntiForgeryToken()
Remove the new BarFormViewModel() as the second parameter in your mapping calls as it is not necessary.
In your post action, inside your if statement that checks if the ModelState is valid and if bar.Id == 0, bar is already a view model, so no need to mapping.
And when you create your AutoMapper mapping, you must create a custom property mapping because the Address.Id property will not map automatically to the AddressId property as the name is not the same.
AutoMapper.Mapper.CreateMap<Address, BarFormViewModel>()
.ForMember(dest => dest.AddressId, o => o.MapFrom(source => source.Id));
And then do the same for the inverse mapping.
I have gone through numerous examples, trying to figure out how to automatically set the CreatedDate value for each entity when saving changes to through unitOfWork and Repository pattern.
So what i have is simple POCO:
public class Partners:IAutoGenerateDateFields
{
[Key]
public int PartnerId { get; set; }
public string PartnerCode { get; set; }
public string PartnerName { get; set; }
public string CompanyName { get; set; }
public string City { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
public string TaxId { get; set; }
public int PartnerTypeId { get; set; }
public int PartnerStateId { get; set; }
public int LocationId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate { get; set; }
// [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
// public DateTime ModifiedDate { get;set;}
public virtual Locations Location { get; set; }
}
This Class implements the IAutoGenerateDateFields interface:
interface IAutoGenerateDateFields
{
DateTime CreatedDate { get; set; }
// DateTime ModifiedDate { get; set; }
}
Finally in my unitOfWork class i check if the added/modified entity implements the above interface and set the CreatedDate value to current date:
public void Save() {
context.ChangeTracker.DetectChanges();
var added = context.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Added)
.Select(t => t.Entity).ToArray();
foreach (var entity in added) {
if (entity is IAutoGenerateDateFields) {
var track = entity as IAutoGenerateDateFields;
track.CreatedDate = DateTime.Now;
}
}
var modified = context.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified)
.Select(t => t.Entity).ToArray();
foreach (var entity in modified) {
if (entity is IAutoGenerateDateFields) {
var track = entity as IAutoGenerateDateFields;
track.CreatedDate = DateTime.Now;
}
}
context.SaveChanges();
}
But every time i hit savechanges i get an error that CreatedDate cannot be null. Thus, the entity does not get set with the current date.
What am i missing here?
Remove the [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute, since you generate the value in your code.
I basically did the same thing yesterday, and the following code is working fine :
var pendingChanges = GetContext().ChangeTracker.Entries<T>().Select(e => e.Entity).ToList();
foreach (var entity in pendingChanges)
{
entity.DateModified = DateTime.Now
}
I have the following error when running my get method for a list in my controller:
The specified LINQ expression contains references to queries that are associated with different contexts.
Having debugged the controller it is the orderby statement that produces the error
The Method is:
public ActionResult OwnerList()
{
var owners = (from s in db.Owners
orderby Peopledb.Posts.FirstOrDefault(x => x.PostId == s.PostId).PostName
select s).ToList();
var viewModel = owners.Select(t => new OwnerListViewModel
{
Created = t.Created,
PostName = Peopledb.Posts.FirstOrDefault(x => x.PostId == t.PostId).PostName,
Dormant = t.Dormant,
OwnerId = t.OwnerId,
});
return PartialView("_OwnerList", viewModel);
}
The Class for Owner in the first database is, dbcontext = IARContext:
public class Owner
{
public int OwnerId { get; set; }
[Column(TypeName = "int")]
public int PostId { get; set; }
[Column(TypeName = "bit")]
public bool Dormant { get; set; }
[Column(TypeName = "datetime2")]
public DateTime Created { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
public People.Models.Post Post { get; set; }
}
The Class for Post in the second database is, dbcontext = PeopleContext:
public class Post
{
public int PostId { get; set; }
[StringLength(50)]
[Column(TypeName = "nvarchar")]
public string PostName { get; set; }
[Column(TypeName = "bit")]
public bool Dormant { get; set; }
[StringLength(350)]
[Column(TypeName = "nvarchar")]
public string Description { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
public virtual ICollection<Owner> Owners { get; set; }
}
I am trying to look up PostName from Post in the People db when displaying a list of Owners from the IAR db
for me the message is quite clear: you are mixing two contexts. That is not possible. This would nearly mean creating an hadhoc dblinq.
The solutions are :
a global context
a separation by code (if the context can't be globalized):
for me you should have
public ActionResult OwnerList()
{
var owners = (from s in db.Owners
//can't order from here without a dbling/global context
select s);
//may be a where is missing here ?
List<DAOSomeName> viewModel = owners.Select(t => new DAOSomeName
{
Created = t.Created,
Dormant = t.Dormant,
OwnerId = t.OwnerId,
});// .ToList(); the materialization is done by the following foreach
//until here, no run to the db, no data transfered.
foreach (DAOSomeName m in viewModel ) {
m.PostName = Peopledb.Posts.Where(x => x.PostId == t.PostId).
Select(x => x.PostName).FirstOrDefault();
//this way you also handle the null case pointed by Trevor
}
//please note that this way, yout view model is not anymore linked
//to the context, except if one property is a navigation property
return PartialView("_OwnerList", viewModel.OrderBy(x => x.PostName));
}
public class DAOSomeName {
public DateTime Created {get; set;}
//Dormant, OwnerId, PostName...
}
By amending my controller to this:
public ActionResult OwnerList()
{
var posts = new List<People.Models.Post>(Peopledb.Posts);
var owners = new List<Owner>(db.Owners);
var ownerposts = (from c in posts
join d in owners on c.PostId equals d.PostId
orderby c.PostName
select new OwnerPost { OwnerId = d.OwnerId, PostName = c.PostName, Created = d.Created, Dormant = d.Dormant }).ToList();
var viewModel = ownerposts.Select(t => new OwnerListViewModel
{
Created = t.Created,
PostName = t.PostName,
Dormant = t.Dormant,
OwnerId = t.OwnerId,
});
return PartialView("_OwnerList", viewModel);
}
and adding a OwnerPost Class:
public class OwnerPost
{
public int OwnerId { get; set; }
public string PostName { get; set; }
public bool Dormant { get; set; }
public DateTime Created { get; set; }
}
I solved the issue
I am using Telerik Open Access for database operation. I have a Parent Class named Order and it includes 2 classes named OrderHistory and Tasks. Now any kind of event happen like if order is put on hold or put for dispatching it's related entry will be put on OrderHistory table and Order table will be updated. Now I have done following code but it works sometimes but sometimes not. I don't know much about this telerik open access and automapper.
controller call:
OrderDTO updateorder = orderManagement.GetOrdersByOrderId(2);
updateorder.QueueId = 3;
updateorder.IsLocked = false;
updateorder.UpdatedBy = Convert.ToInt32(Session["UserId"], CultureInfo.CurrentCulture);
updateorder.UpdatedDate = DateTime.Now;
OrderHistoryDTO alertDto = new OrderHistoryDTO()
{
Event = 'Putting On Hold',
OrderID = orderDTO.Id
UserID = Convert.ToInt32(Session["UserId"], CultureInfo.CurrentCulture),
OccuerdDate = DateTime.Now,
EventType = 'Event'
};
updateorder.OrderHistories.Clear();
updateorder.OrderHistories.Add(alertDto);
updateorder = orderManagement.UpdateOrder(updateorder);
db operations
public OrderDTO UpdateOrder(OrderDTO orderEntity)
{
AutoMapper.Mapper.CreateMap<OrderDTO, Order>()
.ForMember(d => d.Tasks, m => m.Ignore())
.ForMember(d => d.OrderHistories, m => m.MapFrom(s => s.OrderHistories));
AutoMapper.Mapper.CreateMap<OrderHistoryDTO, OrderHistory>();
var orderBase = AutoMapper.Mapper.Map<Order>(orderEntity); // It will sometimes make OrderHistories list count to 0. though in starting orderEntity has OrderHistories count = 1.
base.Update(orderBase);
base.Save();
orderEntity = AutoMapper.Mapper.Map<OrderDTO>(orderBase);
return orderEntity;
}
OrderDTO
public class OrderDTO
{
public int OrderID { get; set; }
public bool? IsLocked { get; set; }
public int QueueId { get; set; }
[ScriptIgnore]
private IList<OrderHistoryDTO> _orderHistories = new List<OrderHistoryDTO>();
[ScriptIgnore]
public virtual IList<OrderHistoryDTO> OrderHistories
{
get { return this._orderHistories; }
}
public DateTime? UpdatedDate { get; set; }
public int? UpdatedBy { get; set; }
}
OrderHistoryDTO
public class OrderHistoryDTO
{
public int ID { get; set; }
public int UserID { get; set; }
public int OrderID { get; set; }
public string Event { get; set; }
public string EventType { get; set; }
public DateTime? OccuerdDate { get; set; }
public UserDTO User { get; set; }
}