Create a DropDown List from a db Entity MVC5 - asp.net-mvc-5

I want to create a dropdown list based on the contents of a db entity.
This seems like a simple enough concept but I can't really seem to pin it down.
Here's my code:
MODEL
public partial class Escuela
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Escuela()
{
this.Empleadoes = new HashSet<Empleado>();
}
public int ID { get; set; }
public string Nombre { get; set; }
public int PuestoID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Empleado> Empleadoes { get; set; }
public virtual Puesto Puesto { get; set; }
}
public partial class ESCUELAEntities : DbContext
{
public ESCUELAEntities()
: base("name=ESCUELAEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Alumno> Alumnoes { get; set; }
public virtual DbSet<Empleado> Empleadoes { get; set; }
public virtual DbSet<Escuela> Escuelas { get; set; }
public virtual DbSet<Grupo> Grupoes { get; set; }
public virtual DbSet<Puesto> Puestoes { get; set; }
}
CONTROLLER
public ActionResult Index()
{
// HAVE TRIED THESE THREE
// #1
var EscQry = from d in db.Escuelas
select d.Nombre;
var escuelas = new SelectList(EscQry, "ID", "Nombre");
ViewData["esc"] = escuelas;
// #2
var escuelas = new SelectList(from d in db.Escuelas
select new SelectListItem { Text = d.Nombre, Value = d.ID.ToString() });
ViewData["esc"] = escuelas;
// #3
IEnumerable<Escuela> model = from p in db.Escuelas
select new Escuela { Nombre = p.Nombre };
// #1
return View();
// #2
return View();
// #3
return View(model);
}
VIEW
#model IEnumerable<_3E_III.Models.Escuela>
#{
ViewBag.Title = "Home Page";
}
#Html.DropDownList("Escuelas", ViewData["esc"] as List<SelectListItem>)
I get this error.
There is no ViewData item of type 'IEnumerable' that has the key 'Escuelas'.

I would make a View Model of the domain class, based on what properties I would need to show in my view.
public class EscuelaViewModel
{
public int ID { get; set; }
public string Nombre { get; set; }
public int PuestoID { get; set; }
.............. etc.
public Collection<SelectListItem> Escuelas {get; set;}
private static Collection<SelectListItem> CreateEscuelasOptions(string selectedOption == "")
{
var model = from p in db.Escuelas select new Escuela { Number = p.Nombre, Id= p.Id };
var options = new Collection<SelectListItem>();
foreach(var esc in model)
options.Add(new SelectListItem {Value = esc.Id, Text = esc.Number, Selected = selectedOption.Equals(esc.Number)});
return options;
}
public void LoadViewData()
{
Escuelas = CreateEscuelasOptions(Nombre);
}
}
Then, in the Controller:
public ActionResult Index()
{
var EscuelaViewModel = new EscuelaViewModel();
EscuelaViewModel.LoadViewData();
return View(EscuelaViewModel);
}
And the View:
#model EscuelaViewModel
#{
ViewBag.Title = "Home Page";
}
#Html.DropDownListFor(x => x.Nombre, Model.Escuelas, --Select option--)

Related

Adding multiple classes to a shopping cart class .net mvc 5

I'm trying to come up with a car service booking application that allows one to either book a car into a service as well as buy a few parts, which is not essential, but I get an error that reads as follows:
SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.BasketLines_dbo.Parts_PartID". The conflict occurred in database "aspnet-Noir-20190224082924", table "dbo.Parts", column 'PartId'.
The statement has been terminated.
My classes are as follows:
PART
public class Part
{
[Key]
public int PartId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public byte[] ImageFile { get; set; }
public string ImageFilePath { get; set; }
public decimal Price { get; set; }
public virtual ICollection<ServicePartMapping>
ServicePartMappings { get; set;}
}
Service
public class Service
{
public int ServiceId { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public decimal Price { get; set; }
public ICollection<Part> Parts { get; set; }
}
ServicePartMapping
public class ServicePartMapping
{
public int ServicePartMappingID { get; set; }
public int PartNumber { get; set; }
public int? ServiceId { get; set; }
public int? ServicePartId { get; set; }
public virtual Service Service { get; set; }
public virtual ServicePart ServicePart { get;
set; }
}
Basket
public class Basket
{
public int Id { get; set; }
private string BasketID { get; set; }
private const string BasketSessionKey =
"BasketID";
private ApplicationDbContext db = new
ApplicationDbContext();
private string GetBasketID()
{
if
(HttpContext.Current.Session[BasketSessionKey]
== null)
{
if
(!string.IsNullOrWhiteSpace
(HttpContext.Current
.User.Identity.Name))
{
HttpContext.Current
.Session[BasketSessionKey] =
HttpContext.Current
.User.Identity.Name;
}
else
{
Guid tempBasketID = Guid.NewGuid()
HttpContext.Current
.Session[BasketSessionKey]
= tempBasketID.ToString();
}
}
return
HttpContext.Current
.Session[BasketSessionKey].ToString();
}
public static Basket GetBasket()
{
Basket basket = new Basket();
basket.BasketID = basket.GetBasketID();
return basket;
}
public void AddServiceToBasket(int serviceID,
int quantity)
{
var basketLine =
db.BasketLines.FirstOrDefault(b =>
b.BasketID == BasketID && b.ServiceID
== serviceID);
if (basketLine == null)
{
basketLine = new BasketLine
{
ServiceID = serviceID,
BasketID = BasketID,
Quantity = quantity,
DateCreated = DateTime.Now
};
db.BasketLines.Add(basketLine);
}
else
{
basketLine.Quantity += quantity;
}
db.SaveChanges();
}
public void AddPartToBasket(int partID, int
quantity)
{
var basketLine =
db.BasketLines.FirstOrDefault(b =>
b.BasketID == BasketID && b.PartId
== partID);
if (basketLine == null)
{
basketLine = new BasketLine
{
PartId = partID,
BasketID = BasketID,
Quantity = quantity,
DateCreated = DateTime.Now
};
db.BasketLines.Add(basketLine);
}
else
{
basketLine.Quantity += quantity;
}
db.SaveChanges();
}
public void RemoveLine(int ID)
{
var basketLine = db.BasketLines.FirstOrDefault(b => b.BasketID == BasketID && b.ServiceID
== ID || b.PartId == ID);
if (basketLine != null)
{
db.BasketLines.Remove(basketLine);
}
db.SaveChanges();
}
public void UpdateBasket(List<BasketLine> lines)
{
foreach (var line in lines)
{
var basketLine = db.BasketLines.FirstOrDefault(b => b.BasketID == BasketID &&
b.ServiceID == line.ServiceID);
if (basketLine != null)
{
if (line.Quantity == 0)
{
RemoveLine(line.ServiceID);
}
else
{
basketLine.Quantity = line.Quantity;
}
}
}
db.SaveChanges();
}
public void EmptyBasket()
{
var basketLines = db.BasketLines.Where(b => b.BasketID == BasketID);
foreach (var basketLine in basketLines)
{
db.BasketLines.Remove(basketLine);
}
db.SaveChanges();
}
public List<BasketLine> GetBasketLines()
{
return db.BasketLines.Where(b => b.BasketID == BasketID).ToList();
}
public decimal GetTotalCost()
{
decimal basketTotal = decimal.Zero;
decimal serviceTotal = decimal.Zero;
decimal partTotal = decimal.Zero;
if (GetBasketLines().Count > 0)
{
serviceTotal = db.BasketLines.Where(b => b.BasketID == BasketID).Sum(b => b.Service.Price
* b.Quantity);
partTotal = db.BasketLines.Where(b => b.BasketID == BasketID).Sum(b => b.Part.Price
* b.Quantity);
basketTotal = serviceTotal + partTotal;
}
return basketTotal;
}
public int GetNumberOfItems()
{
int numberOfItems = 0;
if (GetBasketLines().Count > 0)
{
numberOfItems = db.BasketLines.Where(b => b.BasketID == BasketID).Sum(b => b.Quantity);
}
return numberOfItems;
}
public void MigrateBasket(string userName)
{
//find the current basket and store it in memory using ToList()
var basket = db.BasketLines.Where(b => b.BasketID == BasketID).ToList();
//find if the user already has a basket or not and store it in memory using ToList()
var usersBasket = db.BasketLines.Where(b => b.BasketID == userName).ToList();
//if the user has a basket then add the current items to it
if (usersBasket != null)
{
//set the basketID to the username
string prevID = BasketID;
BasketID = userName;
//add the lines in anonymous basket to the user's basket
foreach (var line in basket)
{
AddServiceToBasket(line.ServiceID, line.Quantity);
AddPartToBasket(line.PartId, line.Quantity);
}
//delete the lines in the anonymous basket from the database
BasketID = prevID;
EmptyBasket();
}
else
{
//if the user does not have a basket then just migrate this one
foreach (var basketLine in basket)
{
basketLine.BasketID = userName;
}
db.SaveChanges();
}
HttpContext.Current.Session[BasketSessionKey] = userName;
}
public decimal CreateOrderLines(int orderID)
{
decimal orderTotal = 0;
var basketLines = GetBasketLines();
foreach (var item in basketLines)
{
BillLine BillLine = new BillLine
{
Service = item.Service,
ServiceID = item.ServiceID,
ServiceName = item.Service.Name,
Quantity = item.Quantity,
ServicePrice = item.Service.Price,
BillID = orderID
};
orderTotal += (item.Quantity * item.Service.Price);
db.BillLines.Add(BillLine);
}
db.SaveChanges();
EmptyBasket();
return orderTotal;
}
}
BasketLine
public class BasketLine
{
public int ID { get; set; }
public string BasketID { get; set; }
public int ServiceID { get; set; }
public int PartId { get; set; }
[Range(0, 50, ErrorMessage = "Please enter a quantity between 0 and 50")]
public int Quantity { get; set; }
public DateTime DateCreated { get; set; }
public virtual Service Service { get; set; }
public virtual Part Part { get; set; }
}
Assumed that EF Code First is used, the exception message indicates that you're using foreign key constraint inside BasketLines table which references PartId primary key column in Parts table, and you're trying to insert a value into BasketLines.PartId column which not exist in Parts table at this statement:
basketLine = new BasketLine
{
PartId = partID, // this assignment is the problem source
BasketID = BasketID,
Quantity = quantity,
DateCreated = DateTime.Now
};
db.BasketLines.Add(basketLine);
Based from inspection, you're trying to build relationship between Service, Part and BasketLine entities, therefore I suggested to add ForeignKeyAttribute for ServiceId and PartId property in BasketLine entity:
public class BasketLine
{
public int ID { get; set; }
public string BasketID { get; set; }
[ForeignKey("Service")]
public int ServiceID { get; set; }
[ForeignKey("Part")]
public int PartId { get; set; }
[Range(0, 50, ErrorMessage = "Please enter a quantity between 0 and 50")]
public int Quantity { get; set; }
public DateTime DateCreated { get; set; }
public virtual Service Service { get; set; }
public virtual Part Part { get; set; }
}
Additionally, since it's stated that a BasketLine requires Service with optional Part, you may also try modify OnModelCreating() method inside DbContext like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BasketLine>()
.HasOptional(x => x.Part) // can save BasketLine without specifying Part
.WithRequired(x => x.Service); // cannot save BasketLine without using Service
}
Related issues:
Configure One-to-One Relationships in EF Code First
The INSERT statement conflicted with the FOREIGN KEY constraint

AutoMapper .ReverseMap() .Ignore() not working

Having an issue with version 6.1.1. In the below, the result of the reverse map still has the Company object populated. Per this post, which shows what I am doing below, except they are ignoring a property, and I'm ignoring a complex object.
What am I missing?
CreateMap<Item, ItemViewModel>(MemberList.Destination)
.ReverseMap()
.ForMember(x => x.Company, x => x.Ignore())
;
With AutoMapper 6.1 you could use ForPath instead ForMember to ignore complex objects.
See How to ignore property with ReverseMap for further information.
I see not what is wrong, but here is a running sample:
namespace AutomapperTest2
{
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantEducation, ApplicantEducationVM>();
cfg.CreateMap<Applicant, ApplicantVM>().ReverseMap()
.ForMember(x => x.Education, x => x.Ignore());
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
Applicant ap = new Applicant
{
Name = "its me",
Education =
new ApplicantEducation
{
SomeInt = 10,
SomeString = "sampleString"
}
};
// Map
ApplicantVM apVm = Mapper.Map<Applicant, ApplicantVM>(ap);
Applicant apBack = Mapper.Map<ApplicantVM, Applicant>(apVm);
}
#endregion
}
/// Your source classes
public class Applicant
{
public ApplicantEducation Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducation
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
// Your VM classes
public class ApplicantVM
{
public string Description { get; set; }
public ApplicantEducationVM Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducationVM
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
}
}

Complex Automapper Configuration

I'm mapping from an existing database to a DTO and back again use Automapper (4.1.1) and I've hit a few small problems.
I have a (simplified) model for the database table:
public class USER_DETAILS
{
[Key]
public string UDT_LOGIN { get; set; }
public string UDT_USER_NAME { get; set; }
public string UDT_INITIALS { get; set; }
public string UDT_USER_GROUP { get; set; }
public decimal UDT_CLAIM_LIMIT { get; set; }
public string UDT_CLAIM_CCY { get; set; }
}
and a DTO object:
public class User
{
public string Login { get; set; }
public string UserName { get; set; }
public string Initials { get; set; }
public string UserGroup { get; set; }
public double ClaimLimit { get; set; }
public string ClaimCurrency { get; set; }
}
I've created a profile
public class FromProfile : Profile
{
protected override void Configure()
{
this.RecognizePrefixes("UDT_");
this.ReplaceMemberName("CCY", "Currency");
this.SourceMemberNamingConvention = new UpperUnderscoreNamingConvention();
this.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
this.CreateMap<decimal, double>().ConvertUsing((decimal src) => (double)src);
this.CreateMap<USER_DETAILS, User>();
}
}
However, it seems that Automapper doesn't like combining this many settings in the config. Even simplifying the models, I can't get
this.RecognizePrefixes("UDT_");
this.ReplaceMemberName("CCY", "Currency");
to work together, and whilst
this.CreateMap<decimal, double>().ConvertUsing((decimal src) => (double)src);
works ok with the models in the test, it fails when using it against a database.
Is there a way to get all this to work together, or should I fall back to using ForMember(). I was really hoping I could get this working as there are a lot of tables in this system, and I'd rather not have to do each one individually.
You will need to extend this for other types, only tested with strings, I have an extension method that does all the work and looks for unmapped properties.
public class USER_DETAILS
{
public string UDT_LOGIN { get; set; }
public string UDT_USER_NAME { get; set; }
public string UDT_INITIALS { get; set; }
public string UDT_USER_GROUP { get; set; }
// public decimal UDT_CLAIM_LIMIT { get; set; }
public string UDT_CLAIM_CCY { get; set; }
}
public class User
{
public string Login { get; set; }
public string UserName { get; set; }
public string Initials { get; set; }
public string UserGroup { get; set; }
//public double ClaimLimit { get; set; }
public string ClaimCurrency { get; set; }
}
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDestination>
CustomPropertyMapper<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType == sourceType && x.DestinationType == destinationType);
var properties = sourceType.GetProperties();
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
var similarPropertyName =
properties.FirstOrDefault(x => x.Name.Replace("_", "").Replace("UDT", "").ToLower().Contains(property.ToLower()));
if(similarPropertyName == null)
continue;
var myPropInfo = sourceType.GetProperty(similarPropertyName.Name);
expression.ForMember(property, opt => opt.MapFrom<string>(myPropInfo.Name));
}
return expression;
}
}
class Program
{
static void Main(string[] args)
{
InitializeAutomapper();
var userDetails = new USER_DETAILS
{
UDT_LOGIN = "Labi-Login",
UDT_USER_NAME = "Labi-UserName",
UDT_INITIALS = "L"
};
var mapped = Mapper.Map<User>(userDetails);
}
static void InitializeAutomapper()
{
Mapper.CreateMap<USER_DETAILS, User>().CustomPropertyMapper();
}
}
}

Register Custom Generic type in AutoMapper

i want register custo generic type like default generic type that register in autoMaper (like List, Array) in AutoMappper.
i have one generic type in project this Code :
class PagedResult<T>
{
public List<T> PageOfResults { get; set; }
public int TotalItems { get; set; }
}
and Dto Class is:
class StudentDto
{
public int ID { get; set; }
public string Name { get; set; }
}
and VM Model is :
class StudentVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and service class:
class MyServie
{
public PagedResult<StudentDto> Swap()
{
var test2 = new PagedResult<StudentDto>();
test2.PageOfResults = new List<StudentDto>();
test2.PageOfResults.Add(new StudentDto() { ID = 10, Name = "Ten" });
test2.TotalItems = 10;
return test2;
}
}
i want use from AutoMapper Object Manager for register PagedResult<> in automapper but i can not do this
var allMappers = MapperRegistry.AllMappers();
MapperRegistry.AllMappers = () => new IObjectMapper[]{
new IdentifiableMapper(),
}.Concat(allMappers);
var service = new MyServie();
PagedResult<StudentDto> pageableStudentDto = service.Swap();
Mapper.CreateMap<StudentDto, StudentVM>();;
PagedResult<StudentVM> vm = Mapper.Map<PagedResult<StudentDto>, PagedResult<StudentVM>>(pageableStudentDto);
and implement of
public class PageOfMapper : IObjectMapper
{
public bool IsMatch(ResolutionContext context)
{
return typeof(PagedResult<>).IsAssignableFrom(context.SourceType.GetGenericTypeDefinition()) &&
typeof(PagedResult<>).IsAssignableFrom(context.DestinationType.GetGenericTypeDefinition());
//return true;
}
public object Map(ResolutionContext context, IMappingEngineRunner mapper)
{
// please help me in this code for map ******************
return null;
}
}

Automapper map custom collections

Hello. I have a list that looks like this one:
public class PagedList<T> : List<T>
{
public PagedList(IEnumerable<T> collection) : base(collection)
{ }
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
//some other properties
}
and used in repository for paging
public PagedList<TEntity> GetPaged(int page)
{
var pagedEntities = some_query;
return pagedEntities.AsPagedList(totalResults, page, pageSize);
}
The same PagedList is also used in asp mvc view models for paging.
Is it possible to map this collections using Automapper with all the properties TotalItems/CurrentPage/... ?
PagedList<DbItem> dbItems = _repository.GetPages(page);
var viewItems = new PagedList<SomeItemView>();
Mapper.Map(dbItems , viewItems);
Tahnk You !
This worked for me. Are you looking for something more generic?
public class DbItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ViewItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class PagedList<T>: List<T>
{
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
}
class Program
{
static void Main(string[] args)
{
MapItems();
}
public static void MapItems()
{
Mapper.CreateMap<DbItem, ViewItem>();
Mapper.CreateMap<PagedList<DbItem>, PagedList<ViewItem>>()
.AfterMap((s, d) => Mapper.Map<List<DbItem>, List<ViewItem>>(s, d));
var dbList = new PagedList<DbItem>
{
new DbItem {Id = 1, Name = "a"},
new DbItem {Id = 2, Name = "b"}
};
dbList.TotalItems = 2;
dbList.CurrentPage = 1;
dbList.PageSize = 10;
var viewList = Mapper.Map<PagedList<DbItem>, PagedList<ViewItem>>(dbList);
Console.WriteLine(viewList.TotalItems);
Console.WriteLine(viewList.CurrentPage);
Console.WriteLine(viewList.PageSize);
Console.WriteLine(viewList[0].Id + " " + viewList[0].Name);
Console.WriteLine(viewList[1].Id + " " + viewList[1].Name);
Console.ReadLine();
}
}
What you need is a custom type converter
public class PagedListConverter<TIn, TOut> : ITypeConverter<IPagedList<TIn>, IPagedList<TOut>>
{
public IPagedList<TOut> Convert(AutoMapper.ResolutionContext context)
{
var source = (IPagedList<TIn>)context.SourceValue;
var mapped = Mapper.Map<IList<TOut>>(source);
return new StaticPagedList<TOut>(mapped, source.GetMetaData());
}
}
Usage:
Mapper.CreateMap<IPagedList<Company>, IPagedList<CompanyViewModel>>().ConvertUsing<PagedListConverter<Company, CompanyViewModel>>();
For those who have faced the similar problem recently, and as an update to NoPyGod's answer, you can achieve the general mapping using ITypeConverter. According to the official documentation:
AutoMapper also supports open generic type converters with any number of generic arguments:
var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(Source<>), typeof(Destination<>)).ConvertUsing(typeof(Converter<,>)));
The closed type from Source will be the first generic argument, and the closed type of Destination will be the second argument to close Converter<,>.
So the custom type converter would be:
private class Converter<TSource, TDestination>
: ITypeConverter<PagedList<TSource>, PagedList<TDestination>>
{
public PagedList<TDestination> Convert(
PagedList<TSource> source,
PagedList<TDestination> destination,
ResolutionContext context) =>
new PagedList<TDestination>(
context.Mapper.Map<List<TSource>, List<TDestination>>(source));
/* Additional settings comes here. */
}
And then register it:
this.CreateMap(typeof(PagedList<>), typeof(PagedList<>)).ConvertUsing(typeof(Converter<,>));

Resources