Best Practice for fill ViewModel with different Repositories - asp.net-mvc

I'm trying to find out the best way to doing this code
(because I think, my way locks not good):
I've tried to make it as easy to understand the problem.
public ActionResult Index()
{
var user=new User();
user.load(1);
return View(user);
}
load(int id )
{
//pseudocode:
//1. load user from repository1
//2. load address from repository2
//3.load payments from repository3
}

Here you go
We would first create a UserModelService as below
public class UserModelService
{
public User Load(int id)
{
var userRepository = new UserRepository();
var user = userRepository.Load(id);
var addressRepository = new AddressRepository();
user.Address = addressRepository.LoadForUserId(id);
return user;
}
}
We would then modify the original code in controller as below
public ActionResult Index()
{
var userModelSerice =new UserModelService();
var user = userModelService.load(1);
return View(user);
}
All the remaining code reference above is as below
public class AddressRepository
{
public Address LoadForUserId(int id)
{
// Load the address for given user id
}
}
public class UserRepository
{
public User Load(int id)
{
// Load and return user
}
}
public class User
{
public Address Address { get; set; }
}
public class Address
{
}
Now in your controller action, instead of creating a new instance of UserModelService, you can have it injected through constructor. Similar principle can be applied to inject the repositories into UserModelService but that would be another big discussion so I would cut myself short here.

Related

Get recent inserted Id and send in to another controllers view

EDITED:
I have Created CRUD Functions for each Modals and now i am trying to get recent Inserted Id and use it in different view.
Here is what I have tried so far
I have created 2 classes(Layer based) for CRUD function for each ContextEntities db to practice pure OOP recursive approach and following is the code.
1. Access Layer
ViolatorDB
public class ViolatorDB
{
private TPCAEntities db;
public ViolatorDB()
{
db = new TPCAEntities();
}
public IEnumerable<tbl_Violator> GetALL()
{
return db.tbl_Violator.ToList();
}
public tbl_Violator GetByID(int id)
{
return db.tbl_Violator.Find(id);
}
public void Insert(tbl_Violator Violator)
{
db.tbl_Violator.Add(Violator);
Save();
}
public void Delete(int id)
{
tbl_Violator Violator = db.tbl_Violator.Find(id);
db.tbl_Violator.Remove(Violator);
Save();
}
public void Update(tbl_Violator Violator)
{
db.Entry(Violator).State = EntityState.Modified;
Save();
}
public void Save()
{
db.SaveChanges();
}
}
2. Logic Layer
ViolatorBs
public class ViolatorBs
{
private ViolatorDB objDb;
public ViolatorBs()
{
objDb = new ViolatorDB();
}
public IEnumerable<tbl_Violator> GetALL()
{
return objDb.GetALL();
}
public tbl_Violator GetByID(int id)
{
return objDb.GetByID(id);
}
public void Insert(tbl_Violator Violator)
{
objDb.Insert(Violator);
}
public void Delete(int id)
{
objDb.Delete(id);
}
public void Update(tbl_Violator Vioaltor)
{
objDb.Update(Vioaltor);
}
}
And Finally using Logic Layer functions in presentation Layer.Here insertion is performed as:
public class CreateViolatorController : Controller
{
public TPCAEntities db = new TPCAEntities();
private ViolatorBs objBs;
public CreateViolatorController()
{
objBs = new ViolatorBs();
}
public ActionResult Index()
{
var voilator = new tbl_Violator();
voilator=db.tbl_Violator.Add(voilator);
ViewBag.id = voilator.VID;
return View();
}
[HttpPost]
public ActionResult Create(tbl_Violator Violator)
{
try
{
if (ModelState.IsValid)
{
objBs.Insert(Violator);
TempData["Msg"] = "Violator Created successfully";
return RedirectToAction("Index");
}
else
{
return View("Index");
}
}
catch (Exception ex)
{
TempData["Msg"] = "Failed..." + ex.Message + " " + ex.ToString();
return RedirectToAction("Index");
}
}
}
Now here is the main part how do i get perticuller inserted id in another controller named Dues while performing insertion ?
In sqlqery I would have used ##IDENTITY but in Entity Framework I'm not sure.
I'm new to mvc framework any suggestion or help is appreciated Thanks in Advance.
Once you save your db context the id is populated back to your entity by EF automatically.
for example.
using(var context = new DbContext())
{
var employee = new Employee(); //this has an id property
context.Employees.Add(employee);
context.SaveChanges();
var id = employee.id; // you will find the id here populated by EF
}
You dont need to add and save your table as you have done this already in your voilatorDB class just fetch the last id like following
var id = yourTableName.Id;
db.yourTableName.find(id);
Or you can simply write one line code to achive that using VoilatorBs class function
GetbyID(id);

How to pass Dbset object as a parameter to any user define function in entity framework code first?

I want to pass Dbset Object to my method.
This is my Method:
public static class MyClass
{
public static Floor ReturnObject(this DbSet<Floor> floor, int Id)
{
var context = floor.passContext() as MyDBContext;
var data = context.floors.Where(a =>a.Id == Id).FirstOrDefault();
return ad;
}
}
public class MyDBContext: DbContext
{
public DbSet<Floor> floors { get; set; }
}
public static DbContext passContext<TEntity>(this DbSet<TEntity> dbSet)
where TEntity : class
{
//code....
}
Now i want to use my method of static class MyClass that is ReturnObject.
I want to use ReturnObject method in my Controller.
I know i can declare it as private member like this in my controller:
This is my controller:
public class MyController
{
private DbSet<Floor> floor;//but is this a good way???
public ActionResult Index(int Id)
{
var data=MyClass.ReturnObject(????,Id)//what should come in place of question mark??
}
}
How should i pass my First Parameter to ReturnObject Method???
Change your ReturnObject method. It should only take Id as parameter and filter data than return object of Floor.
public static Floor ReturnObject(int Id)
{
using(MyDBContext context = new MyDBContext())
{
var data = context.floors.Where(a =>a.Id == Id).FirstOrDefault();
return ad;
}
}
And when you call it than only pass id as paramter
var data=MyClass.ReturnObject(Id);
This will return object of floor which will be stored in data.

AutoMapper+xUnit: Missing type map configuration or unsupported mapping

I cannot figure this one out. I have a N-Tier ASP.MVC application and I am writing my first Unit Test and it seems to fail on my AutoMapper configuration. I have used AutoMapper a million times and never had any problems using it.
I'm sure I am missing something simple, but I have been staring at this for 24 hours now.
Class Library: APP.DOMAIN
public class User : IEntity<int>
{
public int Id { get; set; }
[StringLength(20), Required]
public string UserName { get; set; }
}
Class Library: APP.SERVICE
References App.Domain
public class UserViewModel
{
public int Id { get; set; }
public string UserName { get; set; }
}
I have my AutoMapper bootstrapper in the service layer.
public static class AutoMapperBootstrapper
{
public static void RegisterMappings()
{
Mapper.CreateMap<User, UserViewModel>();
}
}
UserService.cs
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public List<UserViewModel> GetUsers()
{
var users = _userRepository.GetAll();
if (users == null)
{
throw new Exception("No users found.");
}
return Mapper.Map<List<UserViewModel>>(users); // FAILS ON AUTOMAPPER
}
}
ASP.MVC Layer: APP.WEB
References App.Service
private void Application_Start(object sender, EventArgs e)
{
// Register AutoMapper
AutoMapperBootstrapper.RegisterMappings();
Mapper.AssertConfigurationIsValid();
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Unit Test Layer:
public class TestUserRepository :IUserRepository
{
public IEnumerable<User> GetAll()
{
var users = new List<User>()
{
new User { Id = 1, UserName = "Mary"},
new User { Id = 2, UserName = "Joe"}
};
return users;
}
}
public class UserServiceTest
{
private IUserService _userService;
private readonly IUserRepository _userRepository;
public UserServiceTest()
{
_userRepository = new TestUserRepository();
}
[Fact]
public void GetUsers_Should_Return_Correct_Number_Of_Users()
{
// Arrange
_userService = new UserService(_userRepository);
// Act
var result = _userService.GetUsers(); // FAILS ON AUTOMAPPER
// Assert
Assert.True(result.Any(u => u.UserName == "Mary"));
}
}
Failing Test Message:
*** Failures ***
Exception
AutoMapper.AutoMapperMappingException: AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.
Mapping types:
User -> UserViewModel
App.Data.Model.User -> App.Service.ViewModels.UserViewModel
Destination path:
List`1[0]
Source value:
App.Data.Model.User
at App.Service.Services.UserService.GetUsers() in D:\Repositories\App\App.Service\Services\UserService.cs:line 36
at App.Tests.Service.Tests.UserServiceTest.GetUsers_Should_Return_Correct_Number_Of_Users() in D:\Repositories\App\App.Tests\Service.Tests\UserServiceTest.cs:line 34
A little late to the party but have you tried setting the mapping before running the test?
public class UserServiceTest
{
public UserServiceTest()
{
// register the mappings before running the test
AutoMapperBootstrapper.RegisterMappings();
}
...
}
What we would need to do is Inject Custom Mapper Mock as given below. Add all those custom profiles that you have used for that particular class that you are unit testing and inject ConfigureMapper() in the Constructor of that class which is expecting IMapper Object
public IMapper ConfigureMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CustomProfile>();
cfg.AddProfile<UserCustomProfile>();
cfg.AddProfile<UserWorkProfile>();
});
return config.CreateMapper();
}
Hope this solves the issue.
I'm not sure what the problem is, it's been a while since I've last used AutoMapper, but I'm quite sure that the following will work:
return users.Select(Mapper.Map<UserViewModel>);
I have a problem with this line:
var authorDTO = mapper.Map<AuthorCreationDTO>(AuthorinsideDB);
So I change the version of Autormapper
from:
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
to
Version="6.0.0"
and it worked.

Generic repository pattern and multiple selects

I am trying to learn the repository pattern and looking at a generic repository I cannot see how to handle customized select statements. For example, using this article the author uses a select by ID and a select all.
public interface IGenericRepository<T> where T:class
{
IEnumerable<T> SelectAll();
T SelectByID(object id);
void Insert(T obj);
void Update(T obj);
void Delete(object id);
void Save();
}
Later the article the IGenericRepository interface is implemented using Northwind. Then that is used to create a Customer controller.
public class CustomerController : Controller
{
private IGenericRepository<Customer> repository = null;
public CustomerController()
{
this.repository = new GenericRepository<Customer>();
}
...
This would handle selecting a list of all Customers or for one Customer by ID but where I get stuck is some more real world examples like "select all Customers for a client" or "select all Customers for a region". Plus, you could have another controller based on a different entity that would filter on different attributes. I assume I'm missing something basic. If the user interface needed to present the Customer entity (or any other entity) by various filters, how would this be done by sticking with one generic repository?
Here you go; to handle any select criteria apart from the Id, you can add Where method
like below
public interface IGenericRepository<T> where T:class
{
IEnumerable<T> SelectAll();
T SelectByID(object id);
IEnumerable<T> Where(Expression<Func<T,bool>> predicate)// this one
void Insert(T obj);
void Update(T obj);
void Delete(object id);
void Save();
}
Now in the Where method implementation do it like this
public IEnumerable<T> Where(Expression<Func<T,bool>> predicate)
{
return _objectSet.Where(predicate).AsEnumerable();
}
Here _objectSet in created in repository constructor like this :
public Repository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<T>();
}
public CustomerController()
{
_context = new NorthwindEntities();
_reporsitory = new Repository<Customer>(_context);
}
Use of Where method like
reporsitory.Where(c=>c.Country=="Canada").ToList();
For full reference see this project on codeplex (download /browse source code)
https://efgenericrepository.codeplex.com/SourceControl/latest
I think the implementation of the GenericRepository should somehow be able to return the IQueryable of current entity, like adding Get() method.
protected IQueryable<T> Get() // Notice that the access modifier is protected.
{
return table;
}
Then you could just create a derived class from GenericRepository and add a select method that accepts the Filter class.
public class CustomerRepository : GenericRepository<Customer>
{
public IEnumerable<T> SelectAll(CustomerFilter filter){ .. }
}
The filter class contains 2 filters.
public class CustomerFilter
{
public int? ClientId { get; set; }
public int? RegionId { get; set; }
}
Then the SelectAll implementation would be.
public IEnumerable<T> SelectAll(CustomerFilter filter)
{
var query = Get();
if (filter == null)
{
return query;
}
if (filter.ClientId.HasValue)
{
query = query.Where(q => q.ClientId == filter.ClientId.Value);
}
if (filter.RegionId.HasValue)
{
query = query.Where(q => q.RegionId == filter.RegionId.Value);
}
return query;
}
In the controller, calling it like.
public ActionResult Get(int? clientId, int? regionId)
{
var filter = new CustomerFilter { ClientId = clientId, RegionId = regionId };
var customers = _repository.SelectAll(filter);
return View();
}
You might need to see this post as your reference.
An approach I've seen in one asp.net mvc based mission critical app, is to use the generic interface as defined in the question. Then there is an abstract class that implements that interface. And there is one more repository class that inherits the abstract class, which has all methods specific to that class.
public interface IGenericRepository<T> where T:class
{
...
}
public abstract class GenericRepository<T> : IGenericRepository where T:class
{
...
}
And the CustomerRepository class
public class CustomerRepository : GenericRepository<Customer>
{
//add method specific to Customer like select Customers in specific country
}
And in the controller
public class CustomerController : Controller
{
private CustomerRepository repository = null;
public CustomerController()
{
this.repository = new CustomerRepository();
}
...

Intranet Application - Data Access in separate project

I have an MVC intranet application which uses EF 6. I have setup the DataAccess project in a separate class library which has EF 6 referenced. I have an entity which implements an interface:
public interface IAuditable
{
DateTime CreatedDateTime { get; set; }
string CreatedBy { get; set; }
}
public class Collection : IAuditable
{
// Properties
}
However, in the SaveChanges method I obviously don't have access to HttpContext.Current.User.Identity.Name as it is in a separate class library, so I was wondering how one would set this in SaveChanges?
public override int SaveChanges()
{
var addedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added);
foreach (var dbEntityEntry in addedEntries)
{
var entity = dbEntityEntry.Entity as IAuditable;
if (entity != null)
{
entity.CreatedDateTime = DateTime.Now;
// how do I set entity.CreatedBy = HttpContext.Current.User.Identity.Name?
}
}
return base.SaveChanges();
}
Edit
Following on from #CodeCaster solution, I have the following:
[BreezeController]
public class BreezeController : ApiController
{
private readonly BTNIntranetRepository _repository;
public BreezeController(BTNIntranetRepository repository)
{
_repository = repository;
_repository.LoggedInUser = HttpContext.Current.User.Identity.Name;
}
// Methods
}
But HttpContext.Current.User is null
This can be solved in many ways.
You're not really showing relevant code, but you can for example give the library class you expose a public string LoggedInUser (or ActingUser or give it a name) property which you set when instantiating it:
public class SomeController : Controller
{
private IDataSource _dataSource;
public SomeController(IDataSource dataSource)
{
_dataSource = dataSource;
_dataSource.LoggedInUser = HttpContext.Current.User.Identity.Name
}
}
You can then simply use that property in your IDataSource.SaveChanges() method:
public override int SaveChanges()
{
// ...
entity.CreatedBy = this.LoggedInUser;
}

Resources