Convert a string value to an entity in Automapper - viewmodel

I am trying to figure out how to use Automapper when my entity has a field of type entity.
I've got 3 classes like these:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
My class Contact contain an element of type Company.
I've also created a ViewModel to trasfer some infos to my view:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
[DisplayName("Contact")]
public string Name { get; set; }
[DisplayName("Company")]
public string Company { get; set; }
}
In my viewmodel I've defined a field Company of type string. This is going to contain a value the user will chose from a dropdown (list of companies).
I've defined a bootstrapper when my App starts:
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x => {
x.CreateMap<Domain.Contact, ViewModels.ContactViewModel>();
x.CreateMap<ViewModels.ContactViewModel, Domain.Contact>()
});
}
}
When I try to remap my ViewModel to my entity I get a conversion error (AutoMapper.AutoMapperMappingException).
Automapper can't figure out how to convert my Company (string) into an object of type Company, member of Contact.
Is it possible to define a rule so that Automapper know how to transform the string (company) into the code of my Company object, member of Contact?

You need to use a Resolver. Something like:
public class CompanyTypeResolver : ValueResolver<string, Company>
{
protected override Company ResolveCore(string name)
{
return new Company {Name = name};
}
}
Then in mapping code you call it like:
.ForMember(dto => dto.Company, opt => opt.ResolveUsing<CompanyTypeResolver>().FromMember(src => src.Name))

Related

ASP.NET BOILERPLATE: how to dynamically create master entity pages with CRUD operations and also presentation layer

I have around 50 master tables that requires simple and straight forward CRUD operations, my tables are already available in the sql database.
My question is how to make it generic so that I dont need to create manually each individual page for master tables. I saw some ABP CRUDEntityAscyn classes in Boilerplate framework, but I am wondering how to bring it at Presentation layer (.cshtml).
If you need to create an application service that will have Create, Update, Delete, Get, GetAll methods for a specific entity, you can inherit from CrudAppService (or AsyncCrudAppService if you want to create async methods) class to create it easier. CrudAppService base class is generic which gets related Entity and DTO types as generic arguments and is extensible which allows you to override functionality when you need to customize it.
public class Task : Entity, IHasCreationTime
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreationTime { get; set; }
public TaskState State { get; set; }
public Person AssignedPerson { get; set; }
public Guid? AssignedPersonId { get; set; }
public Task()
{
CreationTime = Clock.Now;
State = TaskState.Open;
}
}
[AutoMap(typeof(Task))]
public class TaskDto : EntityDto, IHasCreationTime
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreationTime { get; set; }
public TaskState State { get; set; }
public Guid? AssignedPersonId { get; set; }
public string AssignedPersonName { get; set; }
}
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{
}
}
public interface ITaskAppService : IAsyncCrudAppService<TaskDto>
{
}
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>, ITaskAppService
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{
}
}
calling webapi from client code:
var _editionService = abp.services.app.edition
_editionService.deleteEdition({
id: edition.id
}).done(function () {
getEditions();
abp.notify.success(app.localize('SuccessfullyDeleted'));
});
read for more > https://aspnetboilerplate.com/Pages/Documents/Application-Services#crudappservice-and-asynccrudappservice-classes

ASP.NET Web API deep model binding

I've noticed (even in Web API 2.1) that deep parameter types get filled (processed by the model binder) only on the first level. That is :
public class Person
{
public string Name { get; set; }
public PersonDetails Details { get; set; }
}
public class PersonDetails
{
public string Address { get; set; }
public int Age { get; set; }
}
// ...
public class PersonController : ApiController
{
[HttpPost]
public void ProcessPerson(Person person)
{
// person.Name is filled in correctly
// person.Details.Address and person.Details.Age are not filled in correctly. That is, they have default values (null and 0)
}
}
Is there a simple solution for this problem, except flatting out the Person class like so ?
public class PersonData
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
}
Later edit 1 :
If I flatten the Person class I get all the data correctly
The request is made by POST (and not GET) because I need to ensure there is no caching and since the operation alters state it would be semantically incorrect to use GET

My Model creates an extra key for the database why?

Note: Technoligies in use are ASP.Net MVC 3, Entity, SQL Server Management Studio
Problem?
It seems that when I run, the context as: public class DatabaseInit : DropCreateDatabaseAlways<LocationAppContext>
That it creates the database, but my service assignments table has an extra foreign key called
ServiceAssignment_Service when it shouldn't.
My service assignment model is as such:
namespace LocationApp.Models
{
public class ServiceAssignment
{
public int id { get; set; }
public int locationID { get; set; }
public int ServiceID { get; set; }
public virtual Location Location { get; set; }
public virtual ServiceAssignment Service { get; set;}
}
}
and the service model is as such:
namespace LocationApp.Models
{
public class Service
{
public Service()
{
this.ServiceAssignments = new HashSet<ServiceAssignment>();
}
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public bool active { get; set; }
public string icon { get; set; }
public virtual ICollection<ServiceAssignment> ServiceAssignments { get; set; }
}
}
with that said, the relation ship is simple:
service assignments have many location id's and service id's.
why is this extra foriegn key being generated? the curent keys, that should e there is:
PK: Main PK for the table
FK 1: Location_ServiceAssignment
FK 2: Service_ServiceAssignment
Those are their, how ever this third one is baffling....
The second part is: If a location of id 2 has a service id of 2,3,6,7 How do I get all service id's returned, such that I can pass the object to a service query to get all information on the service based on the ID?
Update:
Context Class:
namespace LocationApp.DAL
{
public class LocationAppContext : DbContext
{
public DbSet<Content> Contents { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<ServiceAssignment> ServiceAssignments { get; set; }
public DbSet<Service> Services { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Location>().HasMany(sa => sa.ServiceAssignments);
modelBuilder.Entity<Service>().HasMany(sa => sa.ServiceAssignments);
}
}
}
I think you have to tell EF that Service.ServiceAssignments is the inverse navigation property of ServiceAssignment.Service and that Location.ServiceAssignments is the inverse of ServiceAssignment.Location. Right now with your mapping you only specify that Location or Service has many ServiceAssignments. EF will consider the navigation properties in ServiceAssignment as the ends of separate relationships.
Try instead the mapping:
modelBuilder.Entity<Location>()
.HasMany(l => l.ServiceAssignments)
.WithRequired(sa => sa.Location)
.HasForeignKey(sa => sa.LocationID);
modelBuilder.Entity<Service>()
.HasMany(s => s.ServiceAssignments)
.WithRequired(sa => sa.Service)
.HasForeignKey(sa => sa.ServiceID);
You can probably remove this mapping altogether as an alternative because EF should detect the right relationships by convention.
So, use either no mapping (=mapping by convention) or the full mapping (=specifying both ends of the relationships). Just the 50%-mapping you have used is likely the problem.

Specified member type is not supported linq

Can anyone tell me what kind of error is this?
The specified type member 'OrderDetails' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
What's wrong here in this code?
return storeDB.Albums.OrderByDescending(a=>a.OrderDetails.Count()).Take(count).ToList()‌​
Since you are using EF code first try to add a configuration class to map the one to many relationship between Album and OrderDetails. The following will be a sample configuration for the Album.
public class AlbumConfiguration : EntityTypeConfiguration<Album>
{
public AlbumConfiguration()
{
HasKey(a => a.Id);
HasMany(album => album.Orders).WithOptional(order => order.Album).
HasForeignKey(order => order.AlbumId);
}
}
and your OrderDetails should be changed as follows
OrderDetail
{
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public int AlbumId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public virtual Album Album { get; set; }
public virtual Order Order { get; set; }
}
and your Album class should have a virtual List of OrderDetails.
and finally in your DbContext class add the configuration by overrinding the OnModelCreating method. sample class would be
public class YourContext : DbContext
{
// your DBSets and contructors, etc
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AlbumConfiguration());
base.OnModelCreating(modelBuilder);
}
}
Try this may be this is the cause of your exception

Fluent Nhibernate & Linq (Property Not Found)

I'm trying to get a web app working based on the S#arp Architecture. At the moment I have a the below code for my entity.
[Serializable]
public abstract class EventBase : Entity
{
#region Properties
[DomainSignature]
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual AgeRange Ages { get; set; }
public virtual int Rating { get; set; }
public virtual decimal Price { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string Website { get; set; }
public virtual EventState State { get; set; }
#endregion
protected EventBase() {}
protected EventBase(string name, string description)
{
// ReSharper disable DoNotCallOverridableMethodsInConstructor
Name = name;
Description = description;
Price = 0;
State = EventState.New;
// ReSharper restore DoNotCallOverridableMethodsInConstructor
}
}
This is mapped using Fluent NHibernate as follows
public class EventBaseMap : AutoMap<EventBase>
{
public EventBaseMap()
{
Id(x => x.ID).WithUnsavedValue(-1).GeneratedBy.Identity();
Component<AgeRange>(x => x.Ages, m =>
{
m.Map(x => x.From).TheColumnNameIs("AgeFrom");
m.Map(x => x.To).TheColumnNameIs("AgeTo");
});
JoinedSubClass<Music>("EventId", sub =>
{
sub.Map(x => x.Headliner);
});
}
}
I created a very simple repository using the very useful S#arp base repository classes.
public interface IEventRepository : INHibernateRepositoryWithTypedId<EventBase, int>
{
List<EventBase> FindByName(string searchPhase);
}
public class EventRepository : NHibernateRepository<EventBase>, IEventRepository
{
public List<EventBase> FindByName(string searchPhase)
{
return Session.Linq<EventBase>().Where(x => x.Name == searchPhase).ToList();
}
}
I can create entities in the db and return all records. When I try to test the FindByName method I get the below error.
NHibernate.QueryException: could not
resolve property: Name of:
Model.Events.EventBase
Does anyone have any ideas? Is it a problem with my mapping?
Thanks,
This is using the Auto-mapping feature. I thought you only explicitly map properties that you want to override or that don't meet the conventions?
If I add an explicit mapping this solves the issue but I am still not sure why.

Resources