How to map properly with Automapper? - asp.net-mvc

I'm working on an application which uses Automapper, Unit Of Work, Entity Framework 5, ASPNET MVC 4, WebApi and Windsor Castle from Nuget.
I'm not sure if I should map this or it should be mapped by Automapper automatically
public IEnumerable<StoreDto> Get()
{
return Uow.Stores.GetAll().OrderBy(s => s.Name);
}
Uow.Stores.GetAll().OrderBy(s => s.Name) returns an IOrderedQueryable<Store>.
I'm receiving the error message
Cannot convert expresion type IOrderedQueryable to type IEnumerable
Should I do a foreach and convert each object returned by GetAll to StoreDto with Mapper.Map method of Automapper? Wouldn't Automapper convert it for me?
This is how I'm using Automapper and how I'm registering it. Please let me know if something should/can be improved.
AutomapperInstaller.cs:
public class AutoMapperInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
Mapper.Initialize(x => x.ConstructServicesUsing(container.Resolve));
RegisterProfilesAndResolvers(container);
RegisterMapperEngine(container);
}
private void RegisterMapperEngine(IWindsorContainer container)
{
container.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));
}
private void RegisterProfilesAndResolvers(IWindsorContainer container)
{
// register value resolvers
container.Register(AllTypes.FromAssembly(Assembly.GetExecutingAssembly()).BasedOn<IValueResolver>());
// register profiles
container.Register(AllTypes.FromThisAssembly().BasedOn<Profile>());
var profiles = container.ResolveAll<Profile>();
foreach (var profile in profiles)
{
Mapper.AddProfile(profile);
}
}
WebWindsorInstaller:
internal class WebWindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component
.For<RepositoryFactories>()
.ImplementedBy<RepositoryFactories>()
.LifestyleSingleton());
container.Register(Component
.For<IRepositoryProvider>()
.ImplementedBy<RepositoryProvider>()
.LifestylePerWebRequest());
container.Register(Component
.For<IProjUow>()
.ImplementedBy<ProjUow>()
.LifestylePerWebRequest());
container.Register(Classes
.FromAssemblyContaining<Api.StoresController>()
.BasedOn<IHttpController>()
.If(t => t.Name.EndsWith("Controller"))
.LifestylePerWebRequest());
RegisterMapping(container, store);
}
private void RegisterMapping(IWindsorContainer container, IConfigurationStore store)
{
Mapper.CreateMap<Store, StoreDto>();
}
}
Store.cs:
public class Store
{
public Store()
{
this.Branches = new List<Branch>();
}
public int Id { get; set; }
public string Name { get; set; }
public System.Data.Spatial.DbGeography Location{ get; set; }
public virtual ICollection<Branch> Branches{ get; set; }
}
StoreDto.cs (same as Store by now)
public class StoreDto
{
public StoreDto()
{
this.Branches = new List<BranchDto>();
}
public int Id { get; set; }
public string Name { get; set; }
public System.Data.Spatial.DbGeography Location{ get; set; }
public virtual ICollection<BranchDto> Branches{ get; set; }
}
And this is how I'm calling the installer in IocConfig.cs:
Container = new WindsorContainer()
.Install(new AutoMapperInstaller())
.Install(new ControllersInstaller());
I'm not sure if I should include there
.AddFacility<FactorySupportFacility>()

Related

Populate a model and access it anywhere on the app

I'm fairly new to razor pages Asp.Net MVC Razor pages,
I created a model with the user fields and wanted to fill it with data once i logged in and access that same data in other pages by calling on the model instead of making another db call
This is my code currently
Model:
public class Utilizador
{
[Key]
public int id { get; set; }
[Required, MaxLength(100)]
public string username { get; set; }
[Required, MaxLength(100)]
public string email { get; set; }
[Required, MaxLength(100)]
public string password { get; set; }
}
then i added a sigleton to Program.cs
builder.Services.AddSingleton<Utilizador>();
You can store your data in the Session or Cache during the first login verification, and then you can call the data directly from the Session without calling the database again.
My test code:
Utilizador.cs:
public class Utilizador
{
public int id { get; set; }
public string username { get; set; }
public string test { get; set; }
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddScoped<Utilizador>();
services.AddSession();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// other middleware
app.UseSession();
// other middleware
}
Test1.cshtml.cs:
public class Test1Model : PageModel
{
private readonly Utilizador _Utilizador;
private Utilizador model { get; set; }
public Utilizador getValue { get; set; }
public Test1Model(Utilizador Utilizador)
{
_Utilizador=Utilizador;
}
public async Task<IActionResult> OnGetAsync(int id)
{
if (id == 1)
{
_Utilizador.username = "MyTest";
_Utilizador.test = "Success";
}
else
{
_Utilizador.username = "MyTest";
_Utilizador.test = "Fail";
}
model = _Utilizador;
HttpContext.Session.Set<Utilizador>(id.ToString(),model);
getValue=HttpContext.Session.Get<Utilizador>(id.ToString());
return Page();
}
}
Get data directly from the corresponding page:
Test1.cshtml:
#page
#model CacheTest.Pages.TestPage.Test1Model
<div>#Model.getValue.username</div>
<div>#Model.getValue.test</div>
Test2.cshtml.cs:
public class Test2Model : PageModel
{
public Utilizador getValue { get; set; }
public void OnGet(int id)
{
getValue = HttpContext.Session.Get<Utilizador>(id.ToString());
}
}
Test2.cshtml:
#page
#model CacheTest.Pages.TestPage.Test2Model
<div>#Model.getValue.username</div>
<div>#Model.getValue.test</div>
Test Result
In Test1, store first and then read:
Directly read the data stored in the session in Test2:
You could try with EF Core,the official document:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/page?view=aspnetcore-6.0&tabs=visual-studio
https://learn.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/sql?view=aspnetcore-6.0&tabs=visual-studio

Use DTOs and AutoMapper in a simple ASP.NET Core MVC with EF Core application

I'm new to ASP.NET Core and I have built an ASP.NET Core MVC with EF Core appplication using Code First approach when creating the database.
Now, I want to use DTOs and AutoMapper in this simple app.
In the code below you may find the Employee.cs from Models folder:
public class Employee
{
[Key]
public int EmployeeId { get; set; }
[Column(TypeName ="nvarchar(250)")]
[Required(ErrorMessage ="This field is required.")]
[DisplayName("Full Name")]
public string FullName { get; set; }
[Column(TypeName = "varchar(10)")]
[DisplayName("Emp. Code")]
public string EmpCode { get; set; }
[Column(TypeName = "varchar(100)")]
public string Position { get; set; }
[Column(TypeName = "varchar(100)")]
[DisplayName("Office Location")]
public string OfficeLocation { get; set; }
}
Below you may find the EmployeeController.cs file:
public class EmployeeController : Controller
{
private readonly EmployeeContext _context;
public EmployeeController(EmployeeContext context)
{
_context = context;
}
// GET: Employee
public async Task<IActionResult> Index()
{
return View(await _context.Employees.ToListAsync());
}
// GET: Employee/Create
public IActionResult AddOrEdit(int id = 0)
{
if (id == 0)
return View(new Employee());
else
return View(_context.Employees.Find(id));
}
// POST: Employee/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEdit([Bind("EmployeeId,FullName,EmpCode,Position,OfficeLocation")] Employee employee)
{
if (ModelState.IsValid)
{
if (employee.EmployeeId == 0)
_context.Add(employee);
else
_context.Update(employee);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(employee);
}
// GET: Employee/Delete/5
public async Task<IActionResult> Delete(int? id)
{
var employee =await _context.Employees.FindAsync(id);
_context.Employees.Remove(employee);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
Additionally, you may find below the Startup.cs file:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<EmployeeContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DevConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Employee}/{action=Index}/{id?}");
});
}
}
What changes should I make to my app in order to use DTOs and AutoMapper?
Please let me know whether you need other files from the app.
Thanks.
You can do following steps.
Create your EmployeeDTO.cs
public class EmployeeDTO
{
public int EmployeeId { get; set; }
public string FullName { get; set; }
public string EmpCode { get; set; }
public string Position { get; set; }
public string OfficeLocation { get; set; }
}
Install the corresponding NuGet package
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Note : If we install the AutoMapper.Extensions.Microsoft.DependencyInjection package, it will automatically install the AutoMapper package for us since it references it.
Create MappingProfile.cs
Add using AutoMapper;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Employee, EmployeeDTO>();
CreateMap<EmployeeDTO, Employee>();
}
}
Configure the services. Let’s do it in the Startup.cs class.
services.AddAutoMapper(typeof(Startup));
First, we inject the mapper object into the controller. Then, we call the Map()method, which maps the Employee object to the EmployeeDTO object.
public class EmployeeController : Controller
{
private readonly EmployeeContext _context;
private readonly IMapper _mapper;
public EmployeeController(EmployeeContext context,, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
// GET: Employee
public async Task<IActionResult> Index()
{
List<EmployeeDTO> employees = _mapper.Map<List<Employee>, List<EmployeeDTO>>(await _context.Employees.ToListAsync());
return View(employees);
}
}

Internal server error in dot net mvc: unable to resolve service

I am trying to use MVC with a controller a view a model and the database.
But I premise I do not know how to use entity framework in my case to connect the model and the database. So I get this runtime error:
"InvalidOperationException: Unable to resolve service for type 'WebCoreFly.Models.FlightsList' while attempting to activate 'WebCoreFly.Controllers.HomeController"
My code consists in:
the controller code:
public class HomeController : Controller
{
private FlightsList l;
public HomeController(FlightsList theList)
{ l = theList; }
public ViewResult Index()
{
return View(l.Flights);
}
}
the Model Code for Flights:
public partial class Flights
{
public long ID { get; set; }
public long Id_Destination { get; set; }
public string Id_Source { get; set; }
public string Nome { get; set; }
public string Company { get; set; }
public System.DateTime Time { get; set; }
public string Id_Plane { get; set; }
public Nullable<System.DateTime> TimeOfArrival { get; set; }
}
and the model for FlightsList:
public class FlightsList
{
private FlyDBContext context;
public FlightsList(FlyDBContext ctx)
{
context = ctx;
}
public IQueryable<Flights> Flights => context.Flights;
}
finally I have defined my dbcontext:
public class FlyDBContext : DbContext
{
public FlyDBContext(DbContextOptions<FlyDBContext> options)
: base(options)
{
}
public DbSet<WebCoreFly.Models.Passengers> Passengers { get; set; }
public DbSet<WebCoreFly.Models.Bookings> Bookings { get; set; }
public DbSet<WebCoreFly.Models.Flights> Flights { get; set; }
}
And in my startup code, I configure services to accept my dbcontext with a link to Existing SQL Database called Fly:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<FlyDBContext>(options =>
options.UseSqlServer(#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Fly;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"));
}
And this is the Configure method of startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
My question is: who is responsible to pass to my controller the flights list? and since the flightlist is tied to the dbcontext, is the problem somehow related to the fact I did not use entityframework? (For semplicity Idid not post the code for my view, but I can do it if necessary)
the issue is coming because of the dependency Injection. Here How the application would know that Flights List object requires in Home Controller. To fix this you have to configure it in StartUp.Cs class Configure Service method.
services.AddSingelton<FlightsList>();
There are various ways to configure it like Transient, AddScoped etc.
It is better if you use repository pattern here. Register here the Interface. like following.
services.AddSingelton<IRepository, FlightsList>();
In controller side.
public class HomeController : Controller
{
private IRepository l;
public HomeController(IRepository theList)
{ l = theList; }
public ViewResult Index()
{
return View(l.Flights);
}
}
Hope it will help.

How to eager load child entities using repository pattern

I have an entity named Tour which can have many Agents. I am able to add agents, but I cannot remove them.
// _repo is injected....
var tour = _repo.GetById(tourId);
tour.AddAgent(new Agent(tour.TourId));
When I attempt to call the Tour.RemoveAgent() method nothing is actually removed. I set a breakpoint inside the Tour.RemoveAgent() method I see that the _agents property has a count of 0.
tour.RemoveAgent(agentId); // This doesn't work because _agents is empty
Do I have to do something special for EF to populate the _agents property when I retrieve the Tour from my repository?
UPDATE: PROBLEM SOLVED (thanks to Paul's answer)
I decided to just create a Repository unique to each aggregate, that way it is easy to define exactly what needs to be included using the Include() function. This is an example where I inherit from the GenericRepository<T> class (which is also included at the bottom of this question).
public class TourRepository : GenericRepository<Tour>
{
public TourRepository(IDatabaseFactory databaseFactory) : base (databaseFactory)
{
}
public override Tour GetById(Guid id)
{
return dataContext.Tours
.Include(x => x.Agents)
.Single(x => x.TourId == id);
}
}
Tour Class
public partial class Tour
{
public Guid TourId { get; private set; }
protected virtual List<Agent> _agents { get; set; }
public Tour()
{
TourId = Guid.NewGuid();
_agents = new List<Agent>();
}
public void AddAgent(Agent agent)
{
_agents.Add(agent);
}
public void RemoveAgent(Guid agentId)
{
_agents.RemoveAll(a => a.AgentId == agentId);
}
}
Agent Class
public partial class Agent
{
public Guid AgentId { get; private set; }
public Guid TourId { get; private set; }
public Tour Tour { get; private set; }
public Agent(Guid tourId)
{
TourId = tourId;
AgentId = Guid.NewGuid();
}
}
OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// AGENTS ============================
modelBuilder.Entity<Agent>()
.HasKey(x => x.AgentId)
.Property(p => p.AgentId);
modelBuilder.Entity<Agent>()
.HasRequired(p => p.Tour)
.WithMany(t => t.Agents);
// TOURS =============================
modelBuilder.Entity<Tour>()
.HasKey(x => x.TourId)
.Property(x => x.TourId);
}
Repository Class
public class GenericRepository<T> : IRepository<T> where T : class {
private MyContext dataContext;
private readonly IDbSet<T> dbset;
public GenericRepository(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected MyContext DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
// ... stuff removed for brevity ...
public T GetById(Guid id)
{
return dbset.Find(id);
}
}
Try making protected virtual List _agents { get; set; } public
public virtual List<Agent> _agents { get; set; }
You can also eager load by doing something like this:
_databaseContext.Tours.Include(x => x.Agents).Single(x => x.TourId == tourId)
you can read more here: http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx

Can automapper map a foreign key to an object using a repository?

I'm trying out Entity Framework Code first CTP4. Suppose I have:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Mother { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
}
public class ChildEdit
{
public int Id { get; set; }
public string Name { get; set; }
public int MotherId { get; set; }
}
Mapper.CreateMap<Child, ChildEdit>();
Mapping to the Edit model is not a problem. On my screen I select the mother through some control (dropdownlist, autocompleter, etc) and the Id of the mother gets posted in back:
[HttpPost]
public ActionResult Edit(ChildEdit posted)
{
var repo = new TestContext();
var mapped = Mapper.Map<ChildEdit, Child>(posted); // <------- ???????
}
How should I solve the last mapping? I don't want to put Mother_Id in the Child object. For now I use this solution, but I hope it can be solved in Automapper.
Mapper.CreateMap<ChildEdit, Child>()
.ForMember(i => i.Mother, opt => opt.Ignore());
var mapped = Mapper.Map<ChildEdit, Child>(posted);
mapped.Mother = repo.Parents.Find(posted.MotherId);
EDIT
This works, but now I have to do that for each foreign key (BTW: context would be injected in final solution):
Mapper.CreateMap<ChildEdit, Child>();
.ForMember(i => i.Mother,
opt => opt.MapFrom(o =>
new TestContext().Parents.Find(o.MotherId)
)
);
What I'd really like would be:
Mapper.CreateMap<int, Parent>()
.ForMember(i => i,
opt => opt.MapFrom(o => new TestContext().Parents.Find(o))
);
Mapper.CreateMap<ChildEdit, Child>();
Is that possible with Automapper?
First, I'll assume that you have a repository interface like IRepository<T>
Afterwards create the following class:
public class EntityConverter<T> : ITypeConverter<int, T>
{
private readonly IRepository<T> _repository;
public EntityConverter(IRepository<T> repository)
{
_repository = repository;
}
public T Convert(ResolutionContext context)
{
return _repository.Find(System.Convert.ToInt32(context.SourceValue));
}
}
Basically this class will be used to do all the conversion between an int and a domain entity. It uses the "Id" of the entity to load it from the Repository. The IRepository will be injected into the converter using an IoC container, but more and that later.
Let's configure the AutoMapper mapping using:
Mapper.CreateMap<int, Mother>().ConvertUsing<EntityConverter<Mother>>();
I suggest creating this "generic" mapping instead so that if you have other references to "Mother" on other classes they're mapped automatically without extra-effort.
Regarding the Dependency Injection for the IRepository, if you're using Castle Windsor, the AutoMapper configuration should also have:
IWindsorContainer container = CreateContainer();
Mapper.Initialize(map => map.ConstructServicesUsing(container.Resolve));
I've used this approach and it works quite well.
Here's how I did it: (using ValueInjecter)
I made the requirements a little bigger just to show how it works
[TestFixture]
public class JohnLandheer
{
[Test]
public void Test()
{
var child = new Child
{
Id = 1,
Name = "John",
Mother = new Parent { Id = 3 },
Father = new Parent { Id = 9 },
Brother = new Child { Id = 5 },
Sister = new Child { Id = 7 }
};
var childEdit = new ChildEdit();
childEdit.InjectFrom(child)
.InjectFrom<EntityToInt>(child);
Assert.AreEqual(1, childEdit.Id);
Assert.AreEqual("John", childEdit.Name);
Assert.AreEqual(3, childEdit.MotherId);
Assert.AreEqual(9, childEdit.FatherId);
Assert.AreEqual(5, childEdit.BrotherId);
Assert.AreEqual(7, childEdit.SisterId);
Assert.AreEqual(0, childEdit.Sister2Id);
var c = new Child();
c.InjectFrom(childEdit)
.InjectFrom<IntToEntity>(childEdit);
Assert.AreEqual(1, c.Id);
Assert.AreEqual("John", c.Name);
Assert.AreEqual(3, c.Mother.Id);
Assert.AreEqual(9, c.Father.Id);
Assert.AreEqual(5, c.Brother.Id);
Assert.AreEqual(7, c.Sister.Id);
Assert.AreEqual(null, c.Sister2);
}
public class Entity
{
public int Id { get; set; }
}
public class Parent : Entity
{
public string Name { get; set; }
}
public class Child : Entity
{
public string Name { get; set; }
public Parent Mother { get; set; }
public Parent Father { get; set; }
public Child Brother { get; set; }
public Child Sister { get; set; }
public Child Sister2 { get; set; }
}
public class ChildEdit
{
public int Id { get; set; }
public string Name { get; set; }
public int MotherId { get; set; }
public int FatherId { get; set; }
public int BrotherId { get; set; }
public int SisterId { get; set; }
public int Sister2Id { get; set; }
}
public class EntityToInt : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType.IsSubclassOf(typeof(Entity)) && targetType == typeof(int);
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName + "Id";
}
protected override bool AllowSetValue(object value)
{
return value != null;
}
protected override object SetValue(object sourcePropertyValue)
{
return (sourcePropertyValue as Entity).Id;
}
}
public class IntToEntity : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType == typeof(int) && targetType.IsSubclassOf(typeof(Entity));
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName.RemoveSuffix("Id");
}
protected override bool AllowSetValue(object value)
{
return (int)value > 0;
}
protected override object SetValue(object sourcePropertyValue)
{
// you could as well do repoType = IoC.Resolve(typeof(IRepo<>).MakeGenericType(TargetPropType))
var repoType = typeof (Repo<>).MakeGenericType(TargetPropType);
var repo = Activator.CreateInstance(repoType);
return repoType.GetMethod("Get").Invoke(repo, new[] {sourcePropertyValue});
}
}
class Repo<T> : IRepo<T> where T : Entity, new()
{
public T Get(int id)
{
return new T{Id = id};
}
}
private interface IRepo<T>
{
T Get(int id);
}
}
It's possible to define the foreign key in EF this way as well:
[ForeignKey("MotherId")]
public virtual Parent Mother { get; set; }
public int MotherId { get; set; }
In this case, It's not necessary to do an extra query to find the Mother. Just Assign the ViewModel's MotherId to the Model's MotherId.

Resources