Automapper error on simple conversion without CreateMap - asp.net-mvc

I have these 2 models:
public class SiteSettingsViewModel
{
public decimal SubscriptionFee { get; set; }
}
public class SiteSettings
{
public decimal SubscriptionFee { get; set; }
}
and the code:
var model = Mapper.Map<SiteSettings, SiteSettingsViewModel>(settingService.GetSettings());
Which throws the error of:
Trying to map WebApp1.Domain.Site.SiteSettings to WebApp1.WebUI.ViewModel.SiteSettingsViewModel.
Missing type map configuration or unsupported mapping.
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
Why do I need to put the code:
Mapper.CreateMap<SiteSettings, SiteSettingsViewModel>();
To me, this seems like I'm writing monkey code. This is not needed.
Why didn't the 1 line work?

One reason is that it's useful for more complex mapping scenarios where you need to define more specific behaviors. For example (from CodePlex):
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
.ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
.ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));
Another option for simple mapping like what you're doing is to make a generic mapper that takes care of the CreateMap call for you, like this:
public interface IMapper<T1, T2>
{
T1 Map(T2 source);
}
public class Mapper<T1, T2> : IMapper<T1, T2> where T1 : class where T2 : class
{
public Mapper()
{
Mapper.CreateMap<T2, T1>();
}
public T1 Map(T2 source)
{
return Mapper.Map<T2, T1>(source);
}
}
And then you can just instantiate them directly like:
var mapper = new Mapper<SiteSettings, SiteSettingsViewModel>();
Or register them to be injected into your controllers, or wherever else you plan on using them. Hope that helps.

Related

Language-Ext Unions - Target runtime doesn't support default interface implementation

I'm trying to user the Union type in Language-Ext and am getting error: "Target runtime doesn't support default interface implementation" for the method name and "The type or namespace name 'InconsistentFirstName' could not be found"
My code looks like this:
[Union]
public interface Inconsistency
{
public Inconsistency InconsistentFirstName(string localValue, string contactCentreValue);
public Inconsistency InconsistentSurname(string localValue, string contactCentreValue);
public string GetFieldName(Inconsistency inconsistency)
=> inconsistency switch
{
InconsistentFirstName n => "Name",
InconsistenctSurname s => "Surname",
_ => ""
};
}
GetFieldName needs to be added to a separate class file. There is a partial class that is automatically code generated called InconsistencyCon, so what I was trying to achieve can be done with the following code:
[Union]
public abstract partial class Inconsistency
{
public abstract Inconsistency InconsistentFirstName(string localValue, string remoteValue);
public abstract Inconsistency InconsistentSurname(string localValue, string remoteValue);
}
public static partial class InconsistencyCon
{
public static string FieldName(this Inconsistency inconsistency) =>
inconsistency switch
{
InconsistentFirstName n => "Name",
InconsistentSurname s => "Surname",
_ => ""
};
public static string Test(Inconsistency inconsistency)
{
if (inconsistency is InconsistentFirstName n)
{
var a = n.LocalValue;
var b = n.FieldName;
// etc
}
}
}

Can't register Mediatr automatically trough structuremap IoC

I have a web application with multilayer architecture and the classes of handlers and queries in the separate layer from a structuremap configuration. I tried to register all handlers and queries dynamically but I couldn't.When I try to run the application I receive the runtime error:
No default Instance is registered and cannot be automatically determined for type 'IRequestHandler'
There is no configuration specified for IRequestHandler
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.AssemblyContainingType<DefaultRegistry>();
scan.AssemblyContainingType<IMediator>();
scan.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>));
scan.ConnectImplementationsToTypesClosing(typeof(IAsyncRequestHandler<,>));
scan.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(IAsyncRequestHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(IAsyncNotificationHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
scan.With(new ControllerConvention());
});
//For<IExample>().Use<Example
For<SingleInstanceFactory>().Use<SingleInstanceFactory>(ctx => t => ctx.GetInstance(t));
For<MultiInstanceFactory>().Use<MultiInstanceFactory>(ctx => t => ctx.GetAllInstances(t));
For<IMediator>().Use<Mediator>();
For<IUnitOfWork>().Use<UnitOfWork>().LifecycleIs<TransientLifecycle>();
For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(Business.Pipeline.MediatorPipeline<,>));
}
controller
var query = new GetUserQuery(id);var user = _mediator.Send(query);
query
public class GetUserQuery : IRequest<User>
{
public GetUserQuery() { }
public GetUserQuery(int id)
{
Id = id;
}
public int Id { get; set; }
}
handler
public class GetUserHandler : IRequestHandler<GetUserQuery, User>
{
private readonly IUnitOfWork _uow;
public GetUserHandler(IUnitOfWork uow)
{
_uow = uow;
}
public User Handle(GetUserQuery message)
{
return _uow.UserRepository.Get(message.Id);
}
}
You need to add something like
scan.AssemblyContainingType<GetUserHandler>();
To your registry
ANSWER
I've added to Scan method few commands for scanning other projects from the solution, after that, I called method LookForRegistries. If you want scan types and implementations in other projects you need to set the rule of scanning specific dlls in Scan method.
scan.Assembly("NameOfProj.Business");
scan.Assembly("NameOfProj.Persistence");
scan.TheCallingAssembly();
scan.AssemblyContainingType<DefaultRegistry>();
scan.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>));
scan.ConnectImplementationsToTypesClosing(typeof(IAsyncRequestHandler<,>));
scan.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(IAsyncRequestHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(IAsyncNotificationHandler<>));
scan.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
scan.LookForRegistries();
scan.WithDefaultConventions();

Client Side Validation Using Custom Validator FluentValidation MVC

I have seen many examples of using FluentValidation here but none seem to fit my need. I have an existing server side implementation and based on answers here, I am convinced I have to change my implementation to get it work client side. One reason is because I can't set a ValidationType which seems required for the client side. I am having trouble converting the code so I can use it client side as well. I am submitting a list of File objects and want client side validation that the file extensions are either .pdf or .doc.
Global - Many examples here show a much more complicated Configure
protected void Application_Start()
{
FluentValidationModelValidatorProvider.Configure();
}
Model - I've simplified my model to show that I have at least one property and a collection
[Validator(typeof(MyCustomValidator))]
public class MyCustomModel
{
public DateTime SubmitDate { get; set; }
public List<HttpPostedFileBase> MyFiles { get; set; }
}
Model Validator - I have a separate validator for the collection
public class MyCustomModelValidator : AbstractValidator<MyCustomModel>
{
public MyCustomModelValidator()
{
RuleFor(x => x.SubmitDate)
.NotEmpty()
.WithMessage("Date Required");
RuleFor(x => x.MyFiles)
.SetCollectionValidator(new MyFileValidator())
.Where(x => x != null);
}
}
Collection Validator - This should check a file for a valid extension
public class MyFileValidator : AbstractValidator<HttpPostedFileBase>
{
public MyFileValidator()
{
RuleFor(x => x)
.Must(x => x.IsValidFileType())
.WithMessage("Invalid File Type")
}
}
public static bool IsValidFileType(this HttpPostedFileBase file)
{
var extensions = { ".pdf", ".doc" };
return extensions.Contains(Path.GetExtension(file.FileName.ToLower()));
}
Controller - Just showing the basics
[HttpGet]
public ActionResult Index(DefaultParameters parameters)
{
var model = new MyCustomModel();
return this.View(model);
}
[HttpPost]
public ActionResult Submit(MyCustomModel model)
{
if (!ModelState.IsValid)
{
return this.View("Index", model);
}
}
View - I am allowing 5 uploads per submission
#Html.TextBoxFor(m => m.SubmitDate)
#Html.ValidationMessageFor(m => m.TextBoxFor
#for (int i = 0; i < 5; i++)
{
#Html.TextBoxFor(m => m.FileSubmissions[i], new { type = "file" })
#Html.ValidationMessageFor(m => m.FileSubmissions[i])
}
Im not sure why you would change it to clientside? Not all validations should run clientside. Maybe you can get it working using the regex validation method instead of the Must method which is performed clientside according to the docs.

How can I map two tables to one entity in an EntityTypeConfiguration?

I've got a database structure where I've got an Equipment table with the columns Equipment_Id, Field_1, and Field_2. I've got an Equipment_Locale table with the fields Equipment_Id and Desc. The Ids are the same in both tables, and there is a one-to-one relationship between these tables.
I've got the following entity:
public class Equipment
{
public long Id { get; set; }
public string Description { get; set; }
public long Field1 { get; set; }
public long Field2 { get; set; }
}
I've got the following EntityTypeConfiguration:
public class EquipmentMapping : EntityTypeConfiguration<Equipment>
{
public EquipmentMapping()
{
ToTable("EQUIPMENT");
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("EQUIPMENT_ID");
Property(e => e.Field1).HasColumnName("FIELD_1");
Property(e => e.Field2).HasColumnName("FIELD_2");
// TODO: Okay, now I need to get the description in here!
}
}
I need to map the description in there, though, which comes from the EQUIPMENT_LOCALE table's DESC column.
This answer gives me a pretty clear idea on how I could use this if I was defining the mapping in ModelBuilder. However, we've been using files with EntityTypeConfigurations on this project and just having the model builder add those configurations, and I'm not sure how to set up a two table mapping in one of those. How can I accomplish this?
It turns out that the answer I linked which did it in ModelBuilder was really, really close to what I needed to simply put in my EntityTypeConfiguration file. I'd just never used Map() in EntityTypeConfiguration before so I was a bit clueless.
The following seems to work for me:
public class EquipmentMapping : EntityTypeConfiguration<Equipment>
{
public EquipmentMapping()
{
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("EQUIPMENT_ID");
Property(e => e.Field1).HasColumnName("FIELD_1");
Property(e => e.Field2).HasColumnName("FIELD_2");
Property(e => e.Description).HasColumnName("DESC");
Map(m =>
{
m.Properties(e => new
{
e.Id,
e.Field1,
e.Field2
});
m.ToTable("EQUIPMENT");
});
Map(m =>
{
m.Properties(e => new
{
e.Id,
e.Description
});
m.ToTable("EQUIPMENT_LOCALE");
});
}
}
You're going to need a navigation property in your parent:
public virtual Equipment_Locale Equipment_Locale { get; set; }
Then you can add to equipment configuration mapping like:
HasRequired(p => p.Equipment_Locale )
.WithMany()
.HasForeignKey(p => p.Equipment_LocaleId )
.WillCascadeOnDelete(false);
See here for relationship mapping: https://msdn.microsoft.com/en-us/data/jj591620.aspx

Geospatial Point Mapping in Fluent NHibernate

I'm struggling to get Fluent NHibernate to play nice with SQL Server's Geospatial types. I want to store a geographic point in my Place class, but I keep getting an NHibernate configuration error when I run my ASP.NET MVC app:
Method 'SetParameterValues' in type 'NHibernate.Spatial.Type.GeometryType' from
assembly 'NHibernate.Spatial, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' does not have an implementation.
Update: This is caused by an out-dated NHibernate.Spatial DLL. Referencing the latest version (2.2+) solves the problem. Kudos to psousa for leading me to a solution.
Place class:
using System;
using GisSharpBlog.NetTopologySuite.Geometries;
using NHibernate.Validator.Constraints;
namespace MyApp.Data.Entities
{
public class Place
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual Point Location { get; set; }
}
}
Fluent Place Mapping:
using MyApp.Data.Entities;
using FluentNHibernate.Mapping;
using NHibernate.Spatial.Type;
namespace MyApp.Data.Mappings
{
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
ImportType<GisSharpBlog.NetTopologySuite.Geometries.Point>();
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location)
.CustomType(typeof(GeometryType));
}
}
}
Fluent NHibernate Configuration:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
.ShowSql()
.Dialect("NHibernate.Spatial.Dialect.MsSql2008GeographyDialect,NHibernate.Spatial.MsSql2008"))
.ExposeConfiguration(BuildSchema)
.Mappings(x => x.FluentMappings.AddFromAssembly(typeof(UserMap).Assembly)
.Conventions.AddFromAssemblyOf<ColumnNullabilityConvention>());
You're using a Geography dialect but using a CustomType of Geometry on your mapping. You should use a custom type of Geography. Something like:
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location).CustomType(typeof(MsSql2008GeographyType)); //for SQL2008
}
}
Also, there's something else that you may need to do. If your spatial column has an SRID different from 0 (zero), and if you want to skip NH xml mappings, you'll need to declare a custom type like this:
public class Wgs84GeographyType : MsSql2008GeographyType
{
protected override void SetDefaultSRID(GeoAPI.Geometries.IGeometry geometry)
{
geometry.SRID = 4326;
}
}
And then use it on your mapping:
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location).CustomType(typeof(Wgs84GeographyType));
}
}
UPDATE:
You should be referencing NHibernate.Spatial.MsSql2008.dll, and I would advise you to use the strongly-typed Dialect method in your database configuration.
.Dialect<MsSql2008GeographyDialect>()

Resources