I'm looking for the best approach for such scenario:
I'd like to create WebApi which returns to the client some object like:
{
id: 1,
name: "name1",
type: "type1"
}
I can retrieve such data from different data providers (document dbs) which can have different data structures like:
First source:
{
id: 1,
name: "name1",
type: "type1"
}
Second source:
{
productId: 1,
productName: "product",
productType: "type"
}
Third source:
{
itemId: 1,
itemName: "name",
itemType: "type"
}
What will be the best approach to make it easy to extend with next data providers? I'd like to add that I was thinking about JSON.NET lib as always. So I believe I'm looking for examples of different json mappings depend on data providers? Anyone can help with some example? Let me add also that it's just 'read-only' scenario, so I mean that WebApi calls different dbs => deserialize to some object => eventually manipulates on the object itself => send over http.
Automapper and three different dtos would be the most correct way imo. But if you want to do it a really simple way you could just create a single class with all the different properties and have corresponding properties use the same backing variable
class Item
{
string _id;
public string id
{
get
{
return _id;
}
set
{
_id = value;
}
}
public string productId
{
get
{
return _id;
}
set
{
_id = value;
}
}
public string itemId
{
get
{
return _id;
}
set
{
_id = value;
}
}
string _name;
public string name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public string productName
{
get
{
return _name;
}
set
{
_name = value;
}
}
public string itemName
{
get
{
return _name;
}
set
{
_name = value;
}
}
string _type;
public string type
{
get
{
return _type;
}
set
{
_type = value;
}
}
public string productType
{
get
{
return _type;
}
set
{
_type = value;
}
}
public string itemType
{
get
{
return _type;
}
set
{
_type = value;
}
}
}
One more possible way to do that is use serialization settings with custom contract resolver object which overrides ResolvePropertyName method.
You can use AutoMapper to solve this problem.
http://automapper.org/
https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
Try the sample below
public class ReturnObject
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
public class Source1
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
public class Source2
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public string ProductType { get; set; }
}
public class Source3
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string ItemType { get; set; }
}
AutoMapper Profile
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
//Same properties
CreateMap<Source1, ReturnObject>();
//Difference properties
CreateMap<Source2, ReturnObject>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(f => f.ProductId))
.ForMember(dest => dest.Name, opt => opt.MapFrom(f => f.ProductName))
.ForMember(dest => dest.Type, opt => opt.MapFrom(f => f.ProductType));
CreateMap<Source3, ReturnObject>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(f => f.ItemId))
.ForMember(dest => dest.Name, opt => opt.MapFrom(f => f.ItemName))
.ForMember(dest => dest.Type, opt => opt.MapFrom(f => f.ItemType));
}
}
Related
I use to find AutoMapper very simple to use. I am struggling with the new version. I have two types:
namespace VehicleMVC.Models
{
public class CarModel
{
public int id { get; set; }
public string make { get; set; }
public string model { get; set; }
}
}
and:
namespace Business
{
public class Car
{
private int _id;
private string _make;
private string _model;
public int id
{
get { return _id; }
set { _id = value; }
}
public string make
{
get { return _make; }
set { _make = value; }
}
public string model
{
get { return _model; }
set { _model = value; }
}
}
}
I have tried this in CarController:
public CarController()
{
service = new Service.Service();
//Mapper.Initialize(cfg => cfg.CreateMap<Business.Car,CarModel>());
//Mapper.Initialize(cfg => cfg.CreateMap<List<CarModel>, List<Business.Car>>());
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Business.Car, CarModel>();
} );
config.AssertConfigurationIsValid();
}
private CarModel getCarModel(Business.Car BusinessCar)
{
CarModel CarModel = AutoMapper.Mapper.Map<CarModel>(BusinessCar);
return CarModel;
}
The error I get is: An exception of type 'System.InvalidOperationException' occurred in AutoMapper.dll. Additional information: Mapper not initialized. Call Initialize with appropriate configuration. but was not handled in user code. What is wrong?
Once you have created your configuration, you must initialize the mapper with it:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Business.Car, CarModel>();
};
config.AssertConfigurationIsValid();
Mapper.Initialize(config);
I have two types. One in the business layer:
namespace Business
{
public class Car
{
private int _id;
private string _make;
private string _model;
public int id
{
get { return _id; }
set { _id = value; }
}
public string make
{
get { return _make; }
set { _make = value; }
}
public string model
{
get { return _model; }
set { _model = value; }
}
}
}
and the other in the Data layer (Entity Framework):
namespace Data
{
using System;
using System.Collections.Generic;
public partial class Car
{
public Car()
{
this.facttables = new HashSet<facttable>();
}
public int id { get; set; }
public string make { get; set; }
public string model { get; set; }
public virtual ICollection<facttable> facttables { get; set; }
}
}
Here is the code I get from the service layer:
namespace Data
{
public class VehicleDAO : IVehicleDAO
{
private static readonly ILog log = LogManager.GetLogger(typeof(VehicleDAO));
MapperConfiguration config;
public VehicleDAO ()
{
Mapper.Initialize(cfg => cfg.CreateMap<Business.Car, Data.Car>());
config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Business.Car, Data.Car>()
.ForMember(dto => dto.facttables, opt => opt.Ignore());
//.ForMember(d => d.id, opt => opt.MapFrom(c => c.id))
//.ForMember(d => d.make, opt => opt.MapFrom(c => c.make))
//.ForMember(d => d.model, opt => opt.MapFrom(c => c.model));
});
config.AssertConfigurationIsValid();
}
public Data.Car Select(int id)
{
Data.Car car;
using (VehicleEntities VehicleDatabase = new VehicleEntities())
{
car = VehicleDatabase.Cars.Where(c => c.id == id).ToList().Single();
Business.Car cars = AutoMapper.Mapper.Map<Business.Car>(car);
}
return car;
}
The exception is: {"Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\nCar_70BD8401A87DAAD8F5F0EC35BCAE5C9E6EE2D6CB5A1AFCE296B313D8AD87D2E9 -> Car\r\nSystem.Data.Entity.DynamicProxies.Car_70BD8401A87DAAD8F5F0EC35BCAE5C9E6EE2D6CB5A1AFCE296B313D8AD87D2E9 -> Business.Car"}. What is wrong? I have marked the line that causes the exception (third from last line).
Automapper only maps in the direction you created the mapping in. CreateMap<Business.Car, Data.Car> creates a mapping from Business.Car to Data.Car. It looks like you are trying to map from Data.Car to Business.Car, which means you need to CreateMap<Data.Car, Business.Car>
Mapper.Initialize(cfg => cfg.CreateMap<Data.Car, Business.Car>());
config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Data.Car, Business.Car>();
});
config.AssertConfigurationIsValid();
I'm having problems in a project asp.net mvc razor using kendo-grid. Data Model is as following:
public partial class Usuario
{
private string _id;
public virtual string Id
{
get
{
return this._id;
}
set
{
this._id = value;
}
}
private string _idPerfil;
public virtual string IdPerfil
{
get
{
return this._idPerfil;
}
set
{
this._idPerfil = value;
}
}
private string _idEntidad;
public virtual string IdEntidad
{
get
{
return this._idEntidad;
}
set
{
this._idEntidad = value;
}
}
private int? _idDepArea;
public virtual int? IdDepArea
{
get
{
return this._idDepArea;
}
set
{
this._idDepArea = value;
}
}
private Char _forzarCambioClave;
public virtual Char ForzarCambioClave
{
get
{
return this._forzarCambioClave;
}
set
{
this._forzarCambioClave = value;
}
}
private short? _idDirectivaSeguridad;
public virtual short? IdDirectivaSeguridad
{
get
{
return this._idDirectivaSeguridad;
}
set
{
this._idDirectivaSeguridad = value;
}
}
private string _hash;
public virtual string Hash
{
get
{
return this._hash;
}
set
{
this._hash = value;
}
}
private Char _requiereRol;
public virtual Char RequiereRol
{
get
{
return this._requiereRol;
}
set
{
this._requiereRol = value;
}
}
private string _idEstado;
public virtual string IdEstado
{
get
{
return this._idEstado;
}
set
{
this._idEstado = value;
}
}
private long _idPersona;
public virtual long IdPersona
{
get
{
return this._idPersona;
}
set
{
this._idPersona = value;
}
}
private Estado _estado;
public virtual Estado Estado
{
get
{
return this._estado;
}
set
{
this._estado = value;
}
}
private DirectivaSeguridad _directivaSeguridad;
public virtual DirectivaSeguridad DirectivaSeguridad
{
get
{
return this._directivaSeguridad;
}
set
{
this._directivaSeguridad = value;
}
}
private Entidad _entidade;
public virtual Entidad Entidad
{
get
{
return this._entidade;
}
set
{
this._entidade = value;
}
}
private Persona _persona;
public virtual Persona Persona
{
get
{
return this._persona;
}
set
{
this._persona = value;
}
}
private IList<Rol> _roles = new List<Rol>();
public virtual IList<Rol> Roles
{
get
{
return this._roles;
}
}
private IList<LogMasterAuditoria> _logsMasterAuditoria = new List<LogMasterAuditoria>();
public virtual IList<LogMasterAuditoria> LogsMasterAuditoria
{
get
{
return this._logsMasterAuditoria;
}
}
}
Controller managing model is:
public class UsuariosController : BaseController
{
private Usuario usuario = new Usuario();
public ActionResult Cargar([DataSourceRequest]DataSourceRequest request)
{
return Json(backend.Usuarios.ToDataSourceResult(request));
}
...
}
View is:
<div>
#(Html.Kendo().Grid<NetInfinity.BackendCore.Usuario>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.Id).Filterable(false).Width(100).Title(#Usuarios.Id);
columns.Bound(c => c.Persona.Nombres).Width(80).Title(#Usuarios.Nombre);
columns.Bound(c => c.Estado.Descripcion).Width(60).Title(#Usuarios.NombrePerfil);
columns.Bound(c => c.Persona.TipoIdentidad.FormatoPresentacion).Width(20).Title(#Usuarios.IdTipo);
columns.Bound(c => c.Persona.NumeroIdentificacion).Width(30).Title(#Usuarios.IdNumero);
columns.Command(c => c.Custom("Edit").Text("Editar"));
columns.Command(c => c.Custom("Remove").Text("Eliminar"));
})
.ToolBar(t => { t.Excel(); })
.Navigatable()
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.Editable(e => e.Mode(GridEditMode.PopUp))
.Resizable(resize => resize.Columns(true))
.HtmlAttributes(new { style = "height:430px;" })
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
.Model(model => model.Id(c => c.Id))
.Read(read => read.Action("Cargar", "Usuarios"))
)
)
</div>
Problem it seems raises when Json serialization from Controller is executed because no data is shown into the grid and table has rows. Maybe there is a problem serializing objects from model like virtual properties, eg: virtual Persona object and others. Please need help on how to put it work.
Have you tried to debug it and get exception message + stack trace to detect the error? Debugger is a good tool to avoid "maybe". Moreover, ASP.NET should definitely return error description if such config has been turned on in Web.config.
Please, find the error by debugging and post the exception details here.
Or change your Web.config such that you have the following value:
<configuration>
<system.web>
<customErrors mode="Off" />
</system.web>
</configuration>
Refresh the page and you will get your error's description.
Detect the error and fix it or post it here and we will help you.
Okay, I'm hoping I am just somehow overlooking the obvious. I have the following code situation below. For some reason, the SequenceNo property is still getting mapped even though I'm calling Ignore(). I'm using the latest. I also had tested it with two different classes in the same project and it seemed to work, so what's wrong with this scenario then?
This is the domain object:
public class CableID
{
private string _panelID1;
public string PanelID1
{
get { return _panelID1; }
private set { _panelID1 = value; }
}
private string _panelID2;
public string PanelID2
{
get { return _panelID2; }
private set { _panelID2 = value; }
}
private int _sequenceNo;
public int SequenceNo
{
get { return _sequenceNo; }
private set { _sequenceNo = value; }
}
private DateTime _inService;
public DateTime InService
{
get { return _inService; }
set { _inService = value; }
}
private string _id;
public string ID
{
get { return _id; }
private set { _id = value; }
}
public CableID(string panelID1, string panelID2, int sequenceNo)
{
this.PanelID1 = panelID1;
this.PanelID2 = panelID2;
this.SequenceNo = sequenceNo;
this.ID = string.Format("({0}-{1}){2}", this.PanelID1, this.PanelID2, this.SequenceNo);
}
public CableID(string id)
{
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException("id");
this.ID = id;
}
}
And here is the DTO Object:
public class CableIDDTO
{
private string _panelID1;
public string PanelID1
{
get { return _panelID1; }
set { _panelID1 = value; }
}
private string _panelID2;
public string PanelID2
{
get { return _panelID2; }
set { _panelID2 = value; }
}
private int _sequenceNo;
public int SequenceNo
{
get { return _sequenceNo; }
set { _sequenceNo = value; }
}
private string _id;
public string ID
{
get { return _id; }
set { _id = value; }
}
public CableIDDTO()
{ }
public CableIDDTO(string panelID1, string panelID2, int sequenceNo)
{
this.PanelID2 = panelID1;
this.PanelID1 = panelID2;
this.SequenceNo = sequenceNo;
this.ID = string.Format("({0}-{1}){2}", this.PanelID2, this.PanelID1, this.SequenceNo);
}
}
And finally the AutoMapper use-case:
CableID cableID = new CableID("A1", "B1", 2);
Mapper.CreateMap<CableID, CableIDDTO>()
.ForMember(dest => dest.SequenceNo, opt => opt.Ignore());
CableIDDTO dto = Mapper.Map<CableID, CableIDDTO>(cableID);
dto.SequenceNo = 2 when since I had set the Ignore() it should be 0.
This is because AutoMapper is finding this CableIDDTO constructor:
public CableIDDTO(string panelID1, string panelID2, int sequenceNo)
and calling it, setting sequenceNo. I'm not exactly sure how or why it's doing that--i'll continue to dig.
You can fix this by calling .ConstructUsing and telling AutoMapper to use the no-args constructor:
Mapper.CreateMap<CableID, CableIDDTO>()
.ConstructUsing((Func<CableID, CableIDDTO>)(src => new CableIDDTO()))
.ForMember(dest => dest.SequenceNo, opt => opt.Ignore());
Upon further research, this looks like a feature in AutoMapper that tries to match up source property names with destination constructors. Since your destination type (CableIDDTO) had a constructor that perfectly matched up with several property names on the source (panelID1, panelID2, sequenceNo), that constructor was called.
Another way to disable this feature is to call DisableConstructorMapping:
Mapper.Configuration.DisableConstructorMapping()
Its first time when I'm using ORM.
Here is my code:
public class EwidencjonowanaRzeczMap : ClassMap<EwidencjonowanaRzecz>
{
public EwidencjonowanaRzeczMap()
{
Id(c => c.Id);
References(m => m.WagaRef);
}
}
public class WagaMap : ClassMap<Waga>
{
public WagaMap()
{
Id(m => m.Id);
Map(m => m.WagaPrzedmiotu);
HasMany(m => m.ListaRzeczy).Inverse().Cascade.All();
}
}
public class EwidencjonowanaRzecz
{
public virtual int Id { get; set; }
public virtual Waga WagaRef { get; set; }
}
public class Waga
{
public virtual int Id { get; set; }
public virtual String WagaPrzedmiotu { get; set; }
public virtual IList<EwidencjonowanaRzecz> ListaRzeczy { get; set; }
public Waga()
{
ListaRzeczy = new List<EwidencjonowanaRzecz>();
}
}
Here is my function to select records:
public IList<T> SelectAllFromTable<T>()
{
IList<T> czyDanePoprawne = null;
try
{
_sesja = NHibernateHelper.CreateSessionFactory().OpenSession();
using (_sesja.BeginTransaction())
{
czyDanePoprawne = _sesja.CreateCriteria(typeof(T)).List<T>();
}
}
catch (Exception)
{
CzyNieWystapilyBledy = true;
}
return czyDanePoprawne;
}
Tables:
ewidencjonowanarzecz [ Id - PK , IdWagi - FK (Waga.Id)]
waga [ Id - PK , WagaPrzedmiotu ]
Every type Id's - INT(3)
WagaPrzedmiotu is VARCHAR(10)
How to read to list these 2 fields?:
Id (ewidencjonowanarzecz) and WagaPrzedmiotu (waga)
In my mapping:
EwidencjaList = ModelBazy.SelectAllFromTable<EwidencjonowanaRzecz>();
is empty.
What is wrong with my mapping?
If you want to select the table record you can try this :
_sesja = NHibernateHelper.CreateSessionFactory().OpenSession();
var tabledata = _sesja.CreateQuery("from EwidencjonowanaRzecz").List<EwidencjonowanaRzecz>();
If you want to check whether if it fetches the values, try this
string value = tabledata.WagaPrzedmiotu;