UrlHelper in .net web api builder - c#

I'm building a web api and I have for example this controller,
[RoutePrefix("api/v{version}/profile")]
public class ProfileController : BaseController
{
private readonly IPersonService _personService;
private readonly IPersonDtoBuilder _builder;
public ProfileController(IPersonService personService
IPersonDtoBuilder builder)
{
_personService = personService;
_builder = builder;
}
[HttpGet]
[Route("")]
public IHttpActionResult Get()
{
var person = _personService.GetPerson(UserId);
if (person == null)
return NotFound();
var model = _builder.Build(person);
return Ok(model);
}
Being the IPersonDtoBuilder and PersonDtoBuilder
public interface IPersonDtoBuilder
{
PersonDto Build(UrlHelper urlHelper, Person person);
}
public class PersonDtoBuilder : IPersonDtoBuilder
{
public PersonDto Build(UrlHelper urlHelper, Person person)
{
var model = new PersonDto
{
Links = new List<Link>
{
new Link
{
Rel = "update",
Href = urlHelper.Link("UpdateProfile", null),
Title = "Update"
}
},
FirstName = person.FirstName,
MiddleName = person.MiddleName,
LastName = person.Surname
};
return model;
}
}
}
The personDto is a model that derives from Resources just for the sake of having a property Links in all of the derived classes.
[DataContract]
public class PersonDto : Resource
{
[DataMember(Name = "firstName")]
public string FirstName { get; set; }
[DataMember(Name = "middleName")]
public string MiddleName { get; set; }
[DataMember(Name = "lastName")]
public string LastName { get; set; }
}
[DataContract]
public abstract class Resource
{
[DataMember(Name = "_links")]
public IEnumerable<Link> Links { get; set; }
}
My problem is when I'm trying to construct this links in the builder, as I don't have access to the Url.Link method.
I was trying to pass it as a parameter
PersonDto Build(UrlHelper urlHelper, Person person)
and somehow initialize it in the basecontroller
But I'm still not able to initialize it properly. How should I do it?
Thanks
Edit: After reading Silvermind comment I review my basecontroller and figured out how it works
public abstract class BaseController : ApiController
{
UrlHelper _urlHelper;
protected UrlHelper TheUrlHelper
{
get
{
if (_urlHelper == null)
{
_urlHelper = new UrlHelper(this.Request);
}
return _urlHelper;
}
}
public long UserId { get; set; }
}
Not sure if it's the cleanest way passing the helper to the build methods of all the classes but it seems to fix this issue

Related

Is there a better way to test this ASP.NET MVC application?

I have created mocks for both IUserService and IDataResult. The test works fine but instead of pulling from the database, I created a user object to test this getcustomerlogin method. Is there a better way to test this case? Can we test this method with actual data from the database?
This is the testing code:
namespace UnitTesting
{
public class Tests
{
[Test]
public void login_unit_test()
{
// arrange
var userinput = new UserForLogin()
{
email = "testmail#mail.com",
password = "123456"
};
var userobject = new User()
{
Email= "testmail#mail.com",
Password = "123456"
};
var mockIdataResult = new Mock<IDataResult<User>>();
mockIdataResult.Setup(i => i.Success).Returns(true);
mockIdataResult.Setup(i => i.Data).Returns(userobject);
var mockIUserService = new Mock<IUserService>();
mockIUserService.Setup(i => i.getByEmail(userinput)).Returns(mockIdataResult.Object);
var authscontroller = new AuthsController(mockIUserService.Object);
// action
IActionResult result = authscontroller.getcustomerlogin(userinput);
var okResult = result as OkObjectResult;
// assert
Assert.AreEqual(200, okResult.StatusCode);
}
}
}
This is the login function we are trying to test.
namespace WEBAPII.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthsController : ControllerBase
{
IUserService _userService;
public AuthsController(IUserService userService)
{
_userService = userService;
}
[HttpPost("login")]
public IActionResult getcustomerlogin(UserForLogin userForLogin)
{
var user = _userService.getByEmail(userForLogin);
if (user.Success)
{
if (!(user.Data.Email == userForLogin.email &&
user.Data.Password == userForLogin.password))
{
return BadRequest(user);
}
return Ok(user);
}
return BadRequest(user);
}
[HttpPost("logindadmin")]
public IActionResult adminlogin(UserForLogin userForLogin)
{
var admin = _userService.getAdmin(userForLogin);
if (admin.Success)
{
return Ok(admin);
}
return BadRequest(admin);
}
}
}
This is the IUserService interface that is set inside Authscontroller
namespace Business.Abstract
{
public interface IUserService
{
List<User> GetAll();
User GetById(int userId);
void Add(User user);
IDataResult<User> getByEmail(UserForLogin userForLogin);
IDataResult<User> getAdmin(UserForLogin userForLogin);
}
}
This is UserForLogin class that takes user information parameters.
public class UserForLogin
{
public string email { get; set; }
public string password { get; set; }
}
This is the User class that we store our information:
namespace Entities.Concrete
{
public class User : IEntity
{
public int id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime CreatedAt { get; set; }
public string roles { get; set; }
}
}
To test your functionality with actual data from the database, you need to implement an integration test instead of a unit test.
You can create a new test database or a replica of the existing production server and populate some test data into it.
Use EF or ADO.Net for DB operations in place of macking.

How can i make AutoMapper generate this code automatically?

I'm new using Automapper i searched a lot for a right answer but it seems i can't get it in my mind correctly so:
i have this block of code
var user = usersDao.FindById(HttpContext.Current.User.Identity.GetUserId());
var userDto = Mapper.Map<ApplicationUser, UserDto>(user);
if (user.Student != null)
{
userDto.Account = Mapper.Map<Student, StudentDto>(user.Student);
}
if (user.Teacher != null)
{
userDto.Account = Mapper.Map<Teacher, TeacherDto>(user.Teacher);
}
userDto.Account.User = null;
so what i'm trying to do is i have this property in my UserDto class
AccountDto Account
both StudentDto and TeacherDto inherited From it
so i want to make automapper to do self auto mapping to it, if it is from StudentDto or TeacherDto to Account
here is the classes
public class StudentDto : AccountDto
{
}
public class TeacherDto: AccountDto
{
}
public class AccountDto
{
public UserDto User { get; set; }
public string UserId { get; set; }
}
public class UserDto
{
public AccountDto Account { get; set; }
}
is there a simple solution?

Using generic class with constraints to query from DocumentDB

I tried to query an object from azure cosmos document DB. UserRepository is made generic so that the consumer can define its own User object when using the repository as long as it implements IUser interface.
Here is the UserRepository
public class UserRepository<T> : IUserRepository<T> where T : class, IUser
{
private readonly CosmosDbOptions _cosmosDBOptions;
private readonly DocumentDbHelper _documentDBHelper;
public UserRepository(
IOptions<CosmosDbOptions> cosmosDBOptions,
DocumentDbHelper documentDBHelper)
{
_cosmosDBOptions = cosmosDBOptions.Value;
_documentDBHelper = documentDBHelper;
}
public T GetUserByEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
return default(T);
var user = _documentDBHelper
.GetItemsAsync<T>(u => ((T)u).Email == email, _cosmosDBOptions.DocumentDb.UserCollection, email)
.Result
.OrderBy(u => u.Modified)
.FirstOrDefault();
return user;
}
...
}
Here is what the DocumentDbHelper looks like
public class DocumentDbHelper
{
private readonly DocumentClient _client;
private readonly DocumentDbOptions _documentDBOptions;
public DocumentDbHelper(IOptions<CosmosDbOptions> documentDBOptions)
{
_cosmosDBOptions = documentDBOptions.Value.DocumentDb;
var serializerSettings = new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateParseHandling = DateParseHandling.DateTimeOffset
};
_client = new DocumentClient(
new Uri(_cosmosDBOptions.Endpoint), _cosmosDBOptions.AuthKey, serializerSettings);
}
public async Task<IEnumerable<T>> GetItemsAsync<T>(
Expression<Func<T, bool>> predicate, string collectionId, string partitionKey)
{
var query = _client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(_cosmosDBOptions.DatabaseId, collectionId),
new FeedOptions { PartitionKey = new PartitionKey(partitionKey) })
.Where(predicate)
.AsDocumentQuery();
var results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
...
}
And here is how the UserRepository being used
public class UsersController : Controller
{
private readonly IUserRepository<User> _userRepository;
public UsersController(IUserRepository<User> userRepository)
{
_userRepository = userRepository;
}
public IActionResult Get(string email)
{
var user = _userRepository.GetUserByEmail(email);
return Ok(user);
}
}
Just to clarify again, the UserRepository is made generic so that other consumer can do something like this.
public class Users2Controller : Controller
{
private readonly IUserRepository<User2> _userRepository;
public Users2Controller(IUserRepository<User2> userRepository)
{
_userRepository = userRepository;
}
public IActionResult Get(string email)
{
var user = _userRepository.GetUserByEmail(email);
return Ok(user);
}
}
The problem is when executing UsersController.Get and it reaches GetItemsAsync of DocumentDbHelper, this method returns null, even though there is an object with a valid email address as passed to the controller action.
But when I change the constraints of UserRepository from where T : class, IUser to where T : User like below,
public class UserRepository<T> : IUserRepository<T> where T : User
the object is retrieved successfully.
Why is this? Is it possible to make this work by having the interface constraint?
Try to use it as mentioned below, this should work.
public class UserRepository<T> : IUserRepository<T> where T : IUser
I found the root cause.
Here is the snippet of User class:
public class User : IUser
{
[JsonProperty("id")]
public string DocumentId { get; set; } = "";
[JsonProperty("created")]
public DateTimeOffset Created { get; set; }
[JsonProperty("modified")]
public DateTimeOffset Modified { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
...
}
And here is how the IUser interface used to look:
public interface IUser
{
string DocumentId { get; set; }
DateTimeOffset Created { get; set; }
DateTimeOffset Modified { get; set; }
string Email { get; set; }
...
}
The problem is related to this https://github.com/Azure/azure-documentdb-dotnet/issues/317. Once I added JsonProperty attribute to the IUser interface props like below:
public interface IUser
{
[JsonProperty("id")]
string DocumentId { get; set; }
[JsonProperty("created")]
DateTimeOffset Created { get; set; }
[JsonProperty("modified")]
DateTimeOffset Modified { get; set; }
[JsonProperty("email")]
string Email { get; set; }
...
}
it works perfectly.

Using Controller.Content Outside MVC Controller c#

I'm creating a static class with static methods for helping the controllers to do their job. When build the application I get the following error:
Error 40 'System.Web.Mvc.Controller.Content(string)' is inaccessible due to its protection level"
Any idea how to solve this problem?
Notes:
It's a c# mvc aplication
public static ActionResult GetAlbumJSON(AlbumVO album)
{
return Controller.Content(
JsonConvert.SerializeObject(new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
})
);
}
Content method is protected internal, so you can't use it outside of controller.
Controller.Content Method. Most probably your static class violates SRP principle. Let him do his job (initializing, serializing,...) and controller - controller's job - return result to the client.
protected internal ContentResult Content(string content)
It would look smth like:
public static class MyHelper
{
public static object GetAlbum(AlbumVO album)
{
return new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
};
}
}
public class AlbumController : Controller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var convertedResult = MyHelper.GetAlbum(album);
return Json(convertedResult);
}
}
Also I'd advice to take a look at AutoMapper for creating client response objects
I think this is valid case for a view-model for a JSON result since you do want a separation between the Domain model and the data sent back to the client. Using a view model also gives you a proper place to put this mapping between the domain model and the view (the JSON) so you don't need to delegate to a helper class.
public class AlbumModel
{
[JsonProperty(PropertyName = "max_car")]
public int MaxChars { get; private set; }
[JsonProperty(PropertyName = "trans_img")]
public string TransparentImage { get; private set; }
[JsonProperty(PropertyName = "product_type")]
public string ProductType { get; private set; }
[JsonProperty(PropertyName = "obj")]
public AlbumInfo Object { get; private set; }
[JsonProperty(PropertyName = "show_description")]
public bool ShowProductDescription { get; private set; }
public AlbumModel(AlbumVO album)
{
MaxChars = album.maxCharsProjecName;
TransparentImage = album.Transparent_Image;
ShowProductDescription = album.Show_Product_Description;
ProductType = "Album";
Object = new AlbumInfo(album);
}
}
The AlbumInfo class provides additional mappings for your JSON result, which becomes the "obj" property sent back to the client.
public class AlbumInfo
{
// ... define properties here
public AlbumInfo(AlbumVO album)
{
// ... map properties here
}
}
And your controller becomes nice and clean:
public class AlbumController : Conrtoller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var model = new AlbumModel(album);
return Json(model);
}
}

Is there a way to override a property in an interface when the interface is inherited?

Environment:
MVC4
C#
VS2010
Problem:
I have an interface that I am considering my "Base" Interface that is defined as follows:
public interface ITestModel
{
TestViewModel model { get; set; }
void PopulateNewReferralRequestModel(Int32 ReferralTypeID, Int32 profileid, string UniqueKeyValues);
void Save();
}
[MetadataType(typeof(MetaData_TestViewModel))]
public partial class TestViewModel
{
public Int32 id { get; set; }
public string name { get; set; }
public string comments { get; set; }
}
public class MetaData_TestViewModel
{
[Required(ErrorMessage = "ID is required")]
[DisplayName("Primary Key")]
public Int32 id { get; set; }
[Required(ErrorMessage = "Name is required")]
[DisplayName("New Name")]
public string name { get; set; }
[DisplayName("Comments")]
public string comments { get; set; }
}
Using the interface:
If I do the following everything works as I want:
public class BaseViewModel : ITestModel
{
private TestViewModel _model;
public TestViewModel model
{
get { return _model; }
set { _model = value; }
}
public void PopulateNewReferralRequestModel(Int32 ReferralTypeID, Int32 profileid, string UniqueKeyValues)
{
model = new TestViewModel();
model.id = ReferralTypeID;
model.name = "TEst";
model.comments = "This is a comment";
}
public void Save()
{
int i = 1;
return;
}
}
Now I want to inherit the "Base" Interface but change the definition of model property with a definition that is inherited from the TestViewModel as follows:
public interface ITestModelB : ITestModel
{
new TestViewModelB model { get; set; }
}
public class TestViewModelB : TestViewModel
{
public int anotherfield { get; set; }
public string anotherstringfield { get; set; }
}
And use the interface as follows:
public class AViewModel : ITestModelB
{
private TestViewModelB _model;
public TestViewModelB model
{
get
{
return _model;
}
set
{
_model = value;
}
}
public void PopulateNewReferralRequestModel(Int32 ReferralTypeID, Int32 profileid, string UniqueKeyValues)
{
model = new TestViewModelB();
model.comments = "New model created";
model.id = 1;
model.name = "Referral Type 1";
model.anotherfield = profileid;
model.anotherstringfield = UniqueKeyValues;
}
public void Save()
{
int i = 1;
return;
}
}
The problem is when I build I get the following error:
Error 1 'InterfaceTest.Models.AViewModel' does not implement interface member 'InterfaceTest.Models.ITestModel.model'. 'InterfaceTest.Models.AViewModel.model' cannot implement 'InterfaceTest.Models.ITestModel.model' because it does not have the matching return type of 'InterfaceTest.Models.TestViewModel'.
Why I want to do this:
I have a referral system that I am trying to build where all referrals have the same base properties and actions. Each referral type also has properties and actions specific to the referral type. I want the referrals to flow through the same controllers so that when a new referral is added, I only have to create a new interface to handle the referral.
Here is what I want to be able to do in my controllers:
ITestModel model = businessLogic.GetModel(referraltypeid);
model.PopulateNewReferralRequestModel(1, 1234, "DRC Policy, 12345678, 2/14/2014");
I would use a method in my businessLogic class to determine which model type to return based on the referral type. All models would implement the ITestModel interface. Doing this would allow me to handle all referrals through the same controller rather than having to have multiple controllers for each referral type.
What I need
Can anyone tell me how to overcome the error I'm getting when I build or suggest another solution for what I want to accomplish?
Any help is greatly appreciated!
You can get rid of an compile error using explicit interface implementation
public class AViewModel : ITestModelB
{
private TestViewModelB _model;
public TestViewModelB model
{
get
{
return _model;
}
set
{
_model = value;
}
}
private TestViewModel newmodel;
TestViewModel ITestModel.model
{
get { return newmodel; }
set { newmodel = value; }
}
public void PopulateNewReferralRequestModel(Int32 ReferralTypeID, Int32 profileid, string UniqueKeyValues)
{
model = new TestViewModelB();
model.comments = "New model created";
model.id = 1;
model.name = "Referral Type 1";
model.anotherfield = profileid;
model.anotherstringfield = UniqueKeyValues;
}
public void Save()
{
int i = 1;
return;
}
}
Or maybe use generic interface
public interface ITestModel<Tmodel>
{
Tmodel model { get; set; }
void PopulateNewReferralRequestModel(Int32 ReferralTypeID, Int32 profileid, string UniqueKeyValues);
void Save();
}

Categories

Resources