structure-map property injection for Attribute class with external dependency doesnt work for me - structuremap

I am new to structure map. When I use property Injection for a Custom validation class the property is always null.
I think i am missing some thing. Any help in this regards will be highly appreciated.
I don't want to use System.Web.Mvc.DependencyResolver.Current.GetService(); to get the instance of logger in the AddressInfoValidator.
we are using MVC 5, FluentValidation.Mvc(5.1.0.0), and Structure map(2.6.3.0) for injecting dependencies.
I have a simple loging facade and a Myloger class:
public class LogingFacade : ILogingFacade
{
private IMyLoger loger;
LogingFacade(IMyLoger myLoger)
{
this.loger = myLoger;
}
public void SaveToLog()
{
this.loger.SendError();
}
}
public class MyLoger : IMyLoger
{
public void SendError()
{
//send to DB
}
}
The validator implimentation is as follows, I have used property injection because the class below will used as an Attribute:
public class AddressInfoValidator : AbstractValidator<AddressInfo>, IValidatorInterceptor
{
public AddressInfoValidator()
{
this.RuleFor(x => x.StreetAddress).NotEmpty().Length(1, 50);
this.RuleFor(x => x.UnitNumber).Length(0, 20);
this.RuleFor(x => x.City).NotEmpty().WithMessage(UIErrorMessages.MSG_UI_00001).Length(1, 50).WithMessage(UIErrorMessages.MSG_UI_00002);
this.RuleFor(x => x.State).NotEmpty();
this.RuleFor(x => x.ZipCode).NotEmpty().Length(1, 20).Matches("\\d{5}");
}
[SetterProperty]
public ILogingFacade Logger { get; set;}
public ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result)
{
try
{
result.Errors.ToList().ForEach(r =>
{
this.Logger.SaveToLog(new ValidationError { PropertyName = r.PropertyName, AttemptedValue = r.AttemptedValue, ErrorMessage = r.ErrorMessage });
});
}
catch (System.AggregateException)
{
// TODO log the exception
}
return result;
}
public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
{
return validationContext;
}
}
The address model is :
[Validator(typeof(AddressInfoValidator))]
public class AddressInfo
{
public string StreetAddress{ get; set;}
public string UnitNumber{ get; set;}
public string ZipCode{ get; set;}
public string City
public string State{ get; set;}
}
Structure map registry called from application on start in global.asax:
[CLSCompliant(false)]
public class ServiceUiWebRegistry : Registry
{
public ServiceUiWebRegistry()
{
this.For<IMyLoger>().Use<IMyLoger>();
this.For<ILogingFacade>().Use<LogingFacade>();
this.FillAllPropertiesOfType<ILogingFacade>();
}
}

Related

How to separate business logic layer using ModelState wrapper class in .net core mvc

I am developing Web Applications using ASP.Net MVC 5 (.net framework), now trying develop my next Project using .NET Core 3.1 with Entity framework Core (with Code first approach).
I am trying separate business logic in a separate Wrapper class like:
public interface IValidationDictionary
{
void AddError(string key, string errorMessage);
bool IsValid { get; }
}
public class ModelStateWrapper : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ModelStateWrapper(ModelStateDictionary modelState)
{
_modelState = modelState;
}
#region IValidationDictionary Members
public void AddError(string key, string errorMessage)
{
_modelState.AddModelError(key, errorMessage);
}
public bool IsValid
{
get { return _modelState.IsValid; }
}
#endregion
}
In the EmployeeRepo class:
private Models.IValidationDictionary _modelState;
public EmployeeRepo(Models.IValidationDictionary modelState)
{
_modelState = modelState;
}
public int Create(Models.Employee ObjToCreate)
{
if (!Validate(ObjToCreate))
return 0;
_context.Employee.Add(ObjToCreate);
_context.SaveChanges();
return ObjToCreate.Id;
}
protected bool Validate(Models.Employee objToValidate)
{
if (objToValidate.Name.Trim().Length == 0)
_modelState.AddError("Name", "Name is required.");
if (null == objToValidate.DOB)
_modelState.AddError("DOB", "DOB is required");
return _modelState.IsValid;
}
In the Controller:
private Repository.IEmployeeRepo repo;
public EmployeesController(ApplicationDbContext context)
{
_context = context;
repo = new Repository.EmployeeRepo(new Models.ModelStateWrapper(this.ModelState));
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([Bind("Name,DOB,Department")] Employee employee)
{
var respId = repo.Create(employee);
if (0 != respId.Id)
{
return RedirectToAction(nameof(Details), new { id = respId.Id });
}
return View(employee);
}
I am expecting ModelState errors to be update in the controller which is added by the wrapper class, but model validation error not updating in the Controller.
Thanks for your time and for any response.
With Best Regards,
Raju Mukherjee
You just want to use a custom verification method to verify these two
fields, right?
The method you wrote is too complicated.
To bind the corresponding error message, you need to add your custom verification attribute above the corresponding fields like [Required].
In fact, you only need to inherit the ValidationAttribute method and rewrite the IsValid method.
public class Employee
{
public int Id { get; set; }
[CustomValidate]
public string Name { get; set; }
[CustomValidate]
public string DOB { get; set; }
public string Department{ get; set; }
}
Custom Method:
public class CustomValidateAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null || string.IsNullOrEmpty(value.ToString().Trim()))
{
return new ValidationResult(validationContext.DisplayName + " is required.");
}
return ValidationResult.Success;
}
}
Create:
[HttpPost]
public IActionResult Create([Bind("Name,DOB,Department")]Employee employee)
{
if (ModelState.IsValid)
{
_context.Employee.Add(employee);
_context.SaveChanges();
return RedirectToAction(nameof(Details), new { id = employee.Id });
}
return View(employee);
}
Here is the test result:

Unable to retrieve data from database using repository pattern

I'm currently learning interfaces and repository pattern and have difficulties with it. I have domain class "VehicleMake"
public class VehicleMake
{
public VehicleMake()
{
}
[Key]
[Column(Order = 1)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public virtual ICollection<VehicleModel> VehicleModels { get; set; }
}
I also have an Interface "IVehicleRepository"
public interface IVehicleRepository
{
IEnumerable<VehicleMake> All { get; }
}
and a class "VehicleRepository" which implements that interface
public class VehicleRepository : IVehicleRepository
{
private readonly VehicleDbContext _context;
public VehicleRepository(VehicleDbContext context)
{
_context = context;
}
public IEnumerable<VehicleMake> All => _context.VehicleMakes;
}
My DbContext class is following
public class VehicleDbContext : DbContext
{
public VehicleDbContext() : base("VehicleDbContext")
{
}
public DbSet<VehicleMake> VehicleMakes { get; set; }
public DbSet<VehicleModel> VehicleModels { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
And my "VehicleController" is following
public class VehicleController : Controller
{
private readonly IVehicleRepository _vehicleRepository;
public VehicleController()
{
}
public VehicleController(IVehicleRepository vehicleRepository)
{
_vehicleRepository = vehicleRepository;
}
// GET: VehicleMakes
public ActionResult Index()
{
return View(_vehicleRepository.All);
}
}
So the problem is that when I try in my controller to retrieve "Vehicle Makes", I get null reference exception and I don't why. Did I implemented this properly? If not, what is the proper way to do this?
You are getting NullReferenceException because it's calling your parameterless constructor where you don't have initialization of _vehicleRepository field. Your initialization is in constructor where you are passing IVehicleRepository object.
I believe, you don't have dependedency injection framework registered for correctly getting instance. So, you can simply initialize _vehicleRepository in default constructor :
public class VehicleController : Controller
{
private readonly IVehicleRepository _vehicleRepository;
public VehicleController()
{
_vehicleRepository = new VehicleRepository();
}
// GET: VehicleMakes
public ActionResult Index()
{
return View(_vehicleRepository.All);
}
}
Change your VehicleRepository to support default constructor in following way :
public class VehicleRepository : IVehicleRepository
{
private readonly VehicleDbContext _context;
public VehicleRepository() : this(new VehicleDbContext())
{
}
public VehicleRepository(VehicleDbContext context)
{
_context = context;
}
public IEnumerable<VehicleMake> All => _context.VehicleMakes;
}

How Can I Use Custom Validation Attributes on Child Models of a DB Entity?

Summary:
I want a data annotation validator to reference another property in the same class (TitleAuthorAndPublishingConfiguration).
However, DB.SaveChanges() is not being called on this class directly. Rather it is being called on the parent of this class (WebsiteConfiguration).
Therefore validationContext.ObjectType is returning WebsiteConfiguration and I am unable to refer to properties of TitleAuthorAndPublishingConfiguration within the data annotation validator.
WebsiteConfiguration.cs
public class WebsiteConfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public TitleAuthorAndPublishingConfiguration TitleAuthorAndPublishing { get; set; }
public BookChaptersAndSectionsConfiguration BookChaptersAndSections { get; set; }
public SocialMediaLoginsConfiguration SocialMediaLogins { get; set; }
public TagGroupsConfiguration TagGroups { get; set; }
}
public class TitleAuthorAndPublishingConfiguration
{
public string BookTitle { get; set; }
public bool IsPublished { get; set; }
// how do I access a property of current model when calling DB.SaveChanges() on parent?
[RequiredIfOtherFieldIsEnabled("IsPublished")]
public string Publisher { get; set; }
}
// ... and other sub models...
ApplicationDbContext.cs
DbSet<WebsiteConfiguration> WebsiteConfiguration {get;set;}
Example Update Code
public void SeedWebsiteConfiguration()
{
var titleAuthorAndPublishingConfiguration = new TitleAuthorAndPublishingConfiguration()
{
// seed values
};
var bookChaptersAndSectionsConfiguration = new BookChaptersAndSectionsConfiguration()
{
// seed values
};
var socialMediaLoginConfiguration = new SocialMediaLoginsConfiguration()
{
// seed values
};
var tagGroupsConfiguration = new TagGroupsConfiguration()
{
// seed values
};
var websiteConfiguration = new WebsiteConfiguration()
{
TitleAuthorAndPublishing = titleAuthorAndPublishingConfiguration,
BookChaptersAndSections = bookChaptersAndSectionsConfiguration,
SocialMediaLogins = socialMediaLoginConfiguration,
TagGroups = tagGroupsConfiguration
};
DB.WebsiteConfiguration.Add(websiteConfiguration);
DB.SaveChanges();
}
Validator Code
public class RequiredIfOtherFieldIsEnabledAttribute : ValidationAttribute
{
private string _ifWhatIsEnabled { get; set; }
public RequiredIfOtherFieldIsEnabledAttribute(string IfWhatIsEnabled)
{
_ifWhatIsEnabled = IfWhatIsEnabled;
}
protected override ValidationResult IsValid(object currentPropertyValue, ValidationContext validationContext)
{
var isEnabledProperty = validationContext.ObjectType.GetProperty(_ifWhatIsEnabled);
if (isEnabledProperty == null)
{
return new ValidationResult(
string.Format("Unknown property: {0}", _ifWhatIsEnabled)
);
}
var isEnabledPropertyValue = (bool)isEnabledProperty.GetValue(validationContext.ObjectInstance, null);
if (isEnabledPropertyValue == true)
{
if (String.IsNullOrEmpty(currentPropertyValue.ToString()))
{
return new ValidationResult(String.Format("This field is required if {0} is enabled", isEnabledProperty));
}
}
return ValidationResult.Success;
}
}
Questions
Is there a way for me to access child model properties from validationContext?
Am I misguided in my approach? Is there a better way to store multiple models as part of a larger model in a single DB table?
I was hoping not to have multiple config tables and calls to the DB. (There are 4 child models in this example, but there may be 10+ in the next app.)
The setup above meets my needs in so many ways. But I don't want to give up the functionality of DataAnnotations on the sub models!
Bonus Question
I have come across a few posts like this one:
How can I tell the Data Annotations validator to also validate complex child properties?
But that is 4 years old, and I'm wondering if anything has changed since then.
Am I trying to do something that is basically impossible (or at least very difficult)?
Am I trying to do something that is basically impossible (or at least
very difficult)?
No, there is a very simple solution that integrates perfectly with the framework and technologies using DataAnnotations.
You can create a custom ValidationAttribute that is called by EF Validation and call Validator.TryValidateObject inside. This way, when CustomValidation.IsValid is called by EF you launch child complex object validation by hand and so on for the whole object graph. As a bonus, you can gather all errors thanks to CompositeValidationResult.
i.e.
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
public class Program
{
public static void Main() {
var person = new Person {
Address = new Address {
City = "SmallVille",
State = "TX",
Zip = new ZipCode()
},
Name = "Kent"
};
var context = new ValidationContext(person, null, null);
var results = new List<ValidationResult>();
Validator.TryValidateObject(person, context, results, true);
PrintResults(results, 0);
Console.ReadKey();
}
private static void PrintResults(IEnumerable<ValidationResult> results, Int32 indentationLevel) {
foreach (var validationResult in results) {
Console.WriteLine(validationResult.ErrorMessage);
Console.WriteLine();
if (validationResult is CompositeValidationResult) {
PrintResults(((CompositeValidationResult)validationResult).Results, indentationLevel + 1);
}
}
}
}
public class ValidateObjectAttribute: ValidationAttribute {
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
var results = new List<ValidationResult>();
var context = new ValidationContext(value, null, null);
Validator.TryValidateObject(value, context, results, true);
if (results.Count != 0) {
var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName));
results.ForEach(compositeResults.AddResult);
return compositeResults;
}
return ValidationResult.Success;
}
}
public class CompositeValidationResult: ValidationResult {
private readonly List<ValidationResult> _results = new List<ValidationResult>();
public IEnumerable<ValidationResult> Results {
get {
return _results;
}
}
public CompositeValidationResult(string errorMessage) : base(errorMessage) {}
public CompositeValidationResult(string errorMessage, IEnumerable<string> memberNames) : base(errorMessage, memberNames) {}
protected CompositeValidationResult(ValidationResult validationResult) : base(validationResult) {}
public void AddResult(ValidationResult validationResult) {
_results.Add(validationResult);
}
}
public class Person {
[Required]
public String Name { get; set; }
[Required, ValidateObject]
public Address Address { get; set; }
}
public class Address {
[Required]
public String Street1 { get; set; }
public String Street2 { get; set; }
[Required]
public String City { get; set; }
[Required]
public String State { get; set; }
[Required, ValidateObject]
public ZipCode Zip { get; set; }
}
public class ZipCode {
[Required]
public String PrimaryCode { get; set; }
public String SubCode { get; set; }
}

Problem with Automapper and Windsor

I have a problem with Automapper when I try use custom resolver which uses dependency injection.
I have the following model:
public class User : Entity
{
public virtual string Name { get; set; }
public virtual Country Country { get; set; }
}
public class Country : Entity
{
public virtual string Name { get; set; }
}
and the following view model:
public class RegistrationViewModel
{
[Required]
public string Name { get; set; }
public int CountryId { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
in order to map I use the following code:
Mapper.Map(registrationViewModel, user);
earlier I register the following:
Mapper.Reset();
container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<ISession>().
UsingFactoryMethod(() => NHibernateSessionFactory.RetrieveSession()).
LifeStyle.Is(LifestyleType.Transient));
container.Register(Component.For(typeof(LoadingEntityResolver<>)).ImplementedBy(typeof(LoadingEntityResolver<>)).LifeStyle.Transient);
Mapper.Initialize(x =>
{
x.AddProfile<BasicProfile>();
x.ConstructServicesUsing(container.Resolve);
});
My BasicProfile is the following:
public class BasicProfile : Profile
{
public const string VIEW_MODEL = "MyBasicProfile";
public override string ProfileName
{
get { return VIEW_MODEL; }
}
protected override void Configure()
{
CreateMaps();
}
private void CreateMaps()
{
CreateMap<RegistrationViewModel, User>()
.ForMember(dest => dest.Country, _ => _.ResolveUsing<LoadingEntityResolver<Country>>().FromMember(src => src.CountryId))
);
}
}
The custom resolver is done in the following way:
public class LoadingEntityResolver<TEntity> : ValueResolver<int, TEntity>
where TEntity: Entity
{
private readonly ISession _session;
public LoadingEntityResolver(ISession session)
{
_session = session;
}
protected override TEntity ResolveCore(int source)
{
return _session.Load<TEntity>(source);
}
}
When the mapping code is being run I get the following exception:
AutoMapper.AutoMapperMappingException : Trying to map ViewModels.RegistrationViewModel to Models.User.
Using mapping configuration for ViewModels.RegistrationViewModel to Models.User
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> AutoMapper.AutoMapperMappingException : Trying to map ViewModels.RegistrationViewModel to LModels.Country.
Using mapping configuration for ViewModels.RegistrationViewModel to Models.User
Destination property: Country
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> System.ArgumentException : Type 'Mapping.LoadingEntityResolver`1[Models.Country]' does not have a default constructor
I have no idea what may be wrong. It is probably something with constructing the resolver. When I try the following there is no problem:
var resolver = container.Resolve<LoadingEntityResolver<Country>>();
Assert.IsInstanceOf<LoadingEntityResolver<Country>>(resolver);
I would be greatfull for any help.
Best regards
Lukasz
You have some pretty hefty DI stuff going on here :-) I would avoid having AutoMapper resolve entities from a database or whatever. Makes the code difficult to understand and following objects lifetime could become a nightmare.
Anyway, to fix your problem simply swap the order from (wrong):
Mapper.Initialize(x =>
{
x.AddProfile<BasicProfile>();
x.ConstructServicesUsing(container.Resolve);
});
to (correct):
Mapper.Initialize(x =>
{
x.ConstructServicesUsing(container.Resolve);
x.AddProfile<BasicProfile>();
});

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

Resources