FindBy method Entity framework - asp.net-mvc-3

I have this account Model:
public class Account :IAggregateRoot
{
public Account()
{
}
public Account(Guid accountId)
{
Id = accountId;
}
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
}
and this repository class :
public class Repository<T> : IRepository<T> where T : class, IAggregateRoot
{
private readonly DbSet<T> _entitySet;
public T FindBy(T entity)
{
return _entitySet.Find(entity);
}
}
and now when I want to get an entity by Id , for example :
public AccountViewModel GetAccountBy(Guid accountId)
{
var account = new Account(accountId);
_unitOfWork.AccountRepository.FindBy(account);
var accountView = account.ConvertToAccountView();
return accountView;
}
I got this Error :
The specified parameter type is not valid. Only scalar types, such as System.Int32, System.Decimal, System.DateTime, and System.Guid, are supported.
my action to call GetAccountBy is like this:
public ActionResult Edit(Guid accountId)
{
var account = _accountService.GetAccountBy(accountId);
return View(account);
}
what is problem with this ? Any help is much appreciated.

You're not calling the DBSet.Find() method correctly.
As the documentation states you need to pass
The values of the primary key for the entity to be found
you don't pass in an instance of the entity, you pass in the key values that identify an entity. From your example you don't need to create a new instance of account:
var account = new Account(accountId);
_unitOfWork.AccountRepository.FindBy(account);
you just need to pass the accountId to FindBy()
_unitOfWork.AccountRepository.FindBy(accountId);
Here's your code amended:
public class Repository<T> : IRepository<T> where T : class, IAggregateRoot
{
private readonly DbSet<T> _entitySet;
public T FindBy(params Object[] keyValues)
{
return _entitySet.Find(keyValues)
}
}
public AccountViewModel GetAccountBy(Guid accountId)
{
_unitOfWork.AccountRepository.FindBy(accountId);
var accountView = account.ConvertToAccountView();
return accountView;
}

You can call the DbSet.Find(params object[] keyValues) Method only with System.Int32 and System.Guid as the error message indicates. (well and System.Decimal, System.DateTime probably for composite keys)
The method will not look for a Id or PK in your Model and use it automatically (when you pass Account, the method will not use Account.Id) - as its using "the primary key value" http://msdn.microsoft.com/en-us/library/gg696418(v=vs.103).aspx
Consider passing a predicate as suggested in FindBy Id method in EntityFramework
If you Models always have an Id of Type Guid, then maybe you can pass the Id directly:
public T FindBy(T entity)
{
return _entitySet.Find(entity.Id);
}
Hope this helps.

Related

MassTransit Sub-Class when published is not consumed

I am trying to create Generic Implementation to publish messages with MassTransit.
BasePublisher
public abstract class BasePublisher
{
private readonly IPublishEndpoint publishEndpoint;
public BasePublisher(IPublishEndpoint publishEndpoint)
{
this.publishEndpoint = publishEndpoint;
}
public Task Publish(IntegrationBaseEvent message)
{
return publishEndpoint.Publish(message);
}
}
IntegrationBaseEvent
public class IntegrationBaseEvent
{
public IntegrationBaseEvent(Guid id, string name, DateTime createdDate)
{
Id = id;
Name = name;
CreationDate = createdDate;
}
public IntegrationBaseEvent()
{
Id = Guid.NewGuid();
CreationDate = DateTime.UtcNow;
}
public Guid Id { get; private set; }
public string Name { get; set; }
public DateTime CreationDate { get; private set; }
}
And I've created events using the abstraction IntegrationBaseEvent.
public class BusinessCreatedEvent : IntegrationBaseEvent
{
public Guid BusinessId { get; set; }
public string BusinessName { get; set; }
}
With the interface below, I am trying to publish the message but it not at all consumed
public interface IPublisher
{
Task Publish(IntegrationBaseEvent message);
}
It is consumed only when I create a separate publisher for the inherited event BusinessCreatedEvent something like below
public interface ISubscriptionPublisher
{
Task Publish(BusinessCreatedEvent message);
}
I don't want to create a different publisher and just want to use the abstract publisher like below.
public class BusinessCreatedEventHandler
{
private readonly IPublisher _publisher;
public BusinessCreatedEventHandler(IPublisher publisher)
{
_publisher = publisher;
}
public Task Handle(string id, string name)
{
var message = new BusinessCreatedEvent
{
BusinessId = id,
BusinessName = name
};
_publisher.Publish(message);
return Task.CompletedTask;
}
}
Any ideas to make it generic, please?
MassTransit leverages generics extensively, and limits types to the generic type specified. Since you're specifying the base interface as the generic type in the call to Publish, that base interface type is all that is published.
Your question is similar to the this one, and my answer – and the solution is the same. Convert it to object and use the Publish(object message) overload instead. That way, MassTransit will use the object type (MassTransit will call message.GetType() and properly dispatch the message type.
Or, you could make your interface include a generic method as well:
public interface IPublisher
{
Task Publish<T>(T message)
where T : class, IntegrationBaseEvent;
}

Cannot map Interface to a class with AutoMapper, but manually I can

I currently work on a small ASP.NET Core Web API Project. In my "VehicleModelsController" I have a class "VehicleModel":
public class VehicleModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid VehicleMakeId { get; set; }
}
In the controller I also have a method for retrieving single vehicle model:
// GET api/<controller>/Guid
[HttpGet("{id}")]
public async Task<IActionResult> GetModelById(Guid id)
{
var model = await Service.GetVehicleModelById(id);
if (model == null)
return NotFound();
var vehicleModel = new VehicleModelsController.VehicleModel()
{
Id = model.Id,
Name = model.Name,
VehicleMakeId = model.VehicleMakeId,
};
return Ok(vehicleModel);
}
As you can see in this method I am calling "GetVehicleModelById" method in my Service which is defined as follows:
public async Task<IVehicleModel> GetVehicleModelById(Guid vehicleModelId)
{
return await Repository.GetModelById(vehicleModelId);
}
As you can see, it returns vehicle model object of type "IVehicleModel" which is an interface defined as follows:
public interface IVehicleModel
{
Guid Id { get; set; }
string Name { get; set; }
Guid VehicleMakeId { get; set; }
}
This everything works when I'm doing manual mapping of IVehicleModel interface to a VehicleModel class in controller, as you already seen above, but when I try to do mapping with AutoMapper in controller, like this:
var vehicleModel = AutoMapper.Mapper.Map<VehicleModelsController.VehicleModel>(model);
I get an error:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Why is that so? Why can't I do the same with Automapper, what I already done manually?
I have defined mapping in my Mapping Profile class:
CreateMap<VehicleModelsController.VehicleModel, IVehicleModel>().ReverseMap();
so that is not a problem.
EDIT
This is my Mapping Profile class:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<VehicleModelsController.VehicleModel, IVehicleModel>().ReverseMap();
}
}
In "ConfigureServices" method in "Startup.cs" class I have:
services.AddAutoMapper();
EDIT #2
This is exact error that I get:
AutoMapper Mapping Exception
From Automapper Source, now you can pass an assembly containing your automapper profile implementation or any type from that assembly.
Also mentioned in ReadMe page
To use, with an IServiceCollection instance and one or more assemblies:
services.AddAutoMapper(assembly1, assembly2 /*, ...*/);
or marker types:
services.AddAutoMapper(type1, type2 /*, ...*/);
So you can pass Startup.cs as marker type as below
services.AddAutoMapper(typeof(Startup));
Or your assembly containing Automapper implementation class.

Set a ASP.NET WEB.API model property to be read-only for consumers of the API?

What's the best way of ensuring that a property of a model can only be set by the ASP.NET WEB.API service? To a consumer of the service, that property is read-only.
For example:
public class MyModel
{
[Required]
public string CanBeSetByConsumer { get; set; }
// Can only be set by the service
public int Id { get; set; }
}
public class MyModelController : ApiController
{
public MyModel Get(int id)
{
// get MyModel by Id
return new MyModel();
}
public MyModel Post(MyModel myData)
{
// save myData to a store and generate an ID
// return myData with ID populated with a 201 Created
}
}
In the above example, the consumer of the API can POST:
{
"CanBeSetByConsumer" : "SomeValue"
}
The consumer can also GET:
{
"Id" : 1234,
"CanBeSetByConsumer" : "SomeValue"
}
What I would like to do is return a 400 BAD REQUEST if the client POSTs:
{
"Id" : 1234,
"CanBeSetByConsumer" : "SomeValue"
}
Here is one way to do it. Note that the POST model does not contain the Id property.
public class MyGetModel
{
[Required]
public string CanBeSetByConsumer { get; set; }
public int Id { get; set; }
}
public class MyPostModel
{
[Required]
public string CanBeSetByConsumer { get; set; }
}
public class MyModelController : ApiController
{
public MyGetModel Get(int id)
{
// get MyModel by Id
return new MyGetModel();
}
public MyGetModel Post(MyPostModel myData)
{
// save myData to a store and generate an ID
// return myGetData with ID populated with a 201 Created
}
}
Then if you have a lot of shared properties, you can have both of these inherit from an abstract class MyModel.
Another way to do it could be to add an action filter to the post action. In that action filter class, you would override the OnActionExecuting method, inspect the POST values collection for a value under the Id key, and set your 400 BAD REQUEST response there.
public class PreventIdValueAttribute
: System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// check request for id value, and if present,
// set the result to a 400 bad request HttpResponseMessage
}
}
[PreventIdValue]
public MyModel Post(MyModel myData)
{
// save myData to a store and generate an ID
// return myData with ID populated with a 201 Created
}
Note that with the second option, your MyModel instance will still have an Id value in the Post action, but its value will be zero.

EmitMapper Flattering Config NullReferenceException

I am using EmitMapper with Flattering Configuration form EmitMapper samples to map my Entities to DTOs.
The problem is the I am getting NullReferenceException in the case if the source value is the property of the object, which is null, for example:
public class User
{
public Guid Id { get; set; }
public Company Company { get; set; }
}
public class Company
{
public Guid Id { get; set; }
}
public class UserDTO
{
public Guid Id { get; set; }
public Guid CompanyId{ get; set; }
}
I am mapping User to UserDTO: var mapper = ObjectMapperManager.DefaultInstance.GetMapper<User, UserDTO>(
new FlatteringConfig()
);
var dto = mapper.Map(new User());
When EmitMapper will try to get CompanyId, it will call the getter of Company object Id property, but it is null.
Any help will be much appriciated.

Convert a string value to an entity in Automapper

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))

Resources