I'm very new in MVC 5 and EF6 and need help please.
I have two classes and one single View, I can get the values of class Person but class Address is always null.
I tried use constructor and Bind in PersonController/Create
My classes:
public partial class Person
{
[Key]
public int PersonID { get; set; }
[Required]
[StringLength(40)]
public string Name { get; set; }
[StringLength(40)]
public string Email { get; set; }
public virtual Address AddressDI { get; set; }
}
public class Address
{
[Key]
public int AddressID { get; set; }
[Required]
public string City { get; set; }
[Required]
public string Street { get; set; }
[Required]
public virtual Person PersonDI { get; set; }
}
Controller
public class ContatoController : Controller
{
private readonly Address _address;
public PersonController()
{
_address = new Address();
}
HttpPost]
ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = #"Name,Email,AddressID,City,")] Person person, Address address)
{
if (ModelState.IsValid)
{
db.Person.Add(person);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
/* Other Actions and stuffs */
}
This is my 3 days pain :(
I don't know what exactly you're trying to achieve here. If you want to post a person class then just post the person:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
db.Person.Add(person);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
and in the view, something like:
#model Partner
#Html.BeginForm("Create","Contato", FormMethod.Post){
#Html.TextboxFor(p => Model.AddressDI.City)
}
Also it is a common practice to send data across controller actions in wrapper classes referred to as viewModels. ViewModels usually contain everything that needs to be posted or displayed at a given moment in the view, as well as potential parameters for the view.
something like:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ContatoViewModel viewModel)
{
var person = new Person();
person.Name = viewModel.Name;
person.Email = viewModel.Email;
person.AddressDI = viewModel.AddressDI
if (ModelState.IsValid)
{
db.Person.Add(person);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel);
}
Being honest I can't say more without seeing what you're doing in the view.
More on MVVM pattern: https://msdn.microsoft.com/en-us/library/hh848246.aspx
There are tools available such as Jimmy Bogard's AutoMapper which help with copying the values between the view models and the classes.
I have a situation where I need to map a single property as a combination of multiple source properties based on some conditions.
Destination :
public class Email
{
public Email() {
EmailRecipient = new List<EmailRecipient>();
}
public string Subject{get; set;}
public string Body {get; set;}
public virtual ICollection<EmailRecipient> EmailRecipient { get; set; }
}
public class EmailRecipient
{
public int EmaiId { get; set; }
public string RecipientEmailAddress { get; set; }
public int RecipientEmailTypeId { get; set; }
public virtual Email Email { get; set; }
}
Source:
public class EmailViewModel
{
public List<EmailRecipientViewModel> To { get; set; }
public List<EmailRecipientViewModel> Cc { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
public class EmailRecipientViewModel
{
public string RecipientEmailAddress { get; set; }
}
I want Mapper.Map<EmailViewModel,Email>()
Here I would like to map my Email.EmailRecipient as a combination of EmailViewModel.To and EmailViewModel.Cc.
However the condition is, Email.EmailRecipient.RecipientEmailTypeId will be 1 for To and 2 for Cc
Hope my question is clear.
One possible way to achieve this is to create a map that uses a specific method for this conversion. The map creation would be:
Mapper.CreateMap<EmailViewModel, Email>()
.ForMember(e => e.EmailRecipient, opt => opt.MapFrom(v => JoinRecipients(v)));
Where the JoinRecipients method would perform the conversion itself. A simple implementation could be something like:
private ICollection<EmailRecipient> JoinRecipients(EmailViewModel viewModel) {
List<EmailRecipient> result = new List<EmailRecipient>();
foreach (var toRecipient in viewModel.To) {
result.Add(new EmailRecipient {
RecipientEmailTypeId = 1,
RecipientEmailAddress = toRecipient.RecipientEmailAddress
});
}
foreach (var ccRecipient in viewModel.Cc) {
result.Add(new EmailRecipient {
RecipientEmailTypeId = 2,
RecipientEmailAddress = ccRecipient.RecipientEmailAddress
});
}
return result;
}
I'm a huge opponent of converters, mostly because for other people in your project, things will just happen 'like magic' after the mapping call.
An easier way of handling this would be to implement the property as a method that converts other properties on the viewmodel to the required formatting. Example:
public class EmailViewModel
{
public ICollection<EmailRecipient> EmailRecipient {
get {
return To.Union(Cc);
}
}
public List<EmailRecipientViewModel> To { get; set; }
public List<EmailRecipientViewModel> Cc { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
Now automapper automatically maps from EmailRecipient property to EmailRecipient property, and if someone is trying to figure out how it happens, they just need to look on the viewmodel.
Editing this some years later: Just as a warning, doing things this way means that every time you call EmailRecipient, you incur the o(n) task of unioning the To and Cc fields. This is fine if you're only dealing with one email, but if you're reusing the viewmodel and someone sticks it in a loop with say, every other email in the system, it might be a huge performance issue. In that case I'd go with the accepted answer so that you dodge this potential performance pitfall.
public class PageRoleService
{
public void SetRoles(Page page, User activeUser)
{
var rb = page.Project.ProjectType.GetRoleFor(activeUser.UserType);
page.RolesForPage.Add(activeUser, rb);
var managers = GetAllManagersOf(activeUser);
foreach (var m in managers)
{
page.RolesForPage.Add(m, rb);
}
}
}
public class Project : Entity
{
public ProjectType ProjectType { get; set; }
public IList<Page> Pages { get; set; }
}
public class Page : Entity
{
public string Name { get; set; }
public Project Project { get; set; }
public IDictionary<User, RoleBehaviour> RolesForPage { get; set; }
}
public class ProjectType : Entity
{
public IQueryProcessor QueryProcessor { get; set; }
public IList<RoleBehaviour> RoleBehaviours { get; set; }
public RoleBehaviour GetRoleFor(USerType userType)
{
var behaviour = return QueryProcessor.Execute(new GetRolesByUserAndProjectTypeQuery() {
ProjectType = this,
UserType = userType
});
// Filter behaviour attributes for project type properties, business rules, etc...
// FilterBehaviour(behaviour);
return behaviour;
}
}
public class GetRolesByUserAndProjectTypeQuery
{
public UserType UserType { get; set; }
public ProjectType ProjectType { get; set; }
}
public class GetRolesByUserAndProjectTypeQueryHandler
{
public Db Db { get; set; }
public RoleBehaviour Execute(GetRolesByUserAndProjectTypeQuery query)
{
return Db.FirstOrDefault(r => r.UserType == query.UserType && r.ProjectType == query.projectType);
}
}
public class RoleBehaviour : Entity
{
public Role ROleForArea1 { get; set; }
public Role ROleForArea2 { get; set; }
public UserType UserType { get; set; }
public ProjectType ProjectType { get; set; }
public IDictionary<string, string> Attributes { get; set; }
}
public enum UserType
{
A,
B,
C,
D
}
public class Role : Entity
{
public IList<string> Permissions { get; set; }
}
I don't use repository, no need data abstraction, I use CQRS for crud operations. (CreateProjectCommand, GetRolesByUserAndProjectTypeQuery, etc..)
Users related a lot of project and page. Users have more than role for each Page Entity and is dynamically created when user (client) request to fetch All projects page or single page item.
My Page Role Service determinates page roles for active user and its managers. My MVC Controller use PageRoleService.
PageRoleService is Application Service or Domain Service or .....?
QueryProcessor in Entity (ProjectType) is invalid approach? How can handle this/their problems without lazy or eager loading?
RoleBehaviour is Entity or Value Object?
PageRoleService is a service or business logic in domain?
I know that I'm some years later, but:
I would put away the base class Entity, because it looks that this are just Dtos returned by the queryhandler (infact GetRolesByUserAndProjectTypeQueryHandler.Execute returns a RoleBehaviour).
Given this, I think that:
PageRoleService is a simple service that completes a Dto, hence it looks a kind of factory
Given that ProjectType here has two different roles (a Dto and Entity, and this is against CQRS), if:
it's a Dto, then use a service/factory/ORM to load extra data on it
it's an Entity, try to load all the data that's needed by it. This because there're great changes that you'll need it on the way to execute your command (great explanation about DDD and entities).
The object has it's own identity? Has it an Id that, even if things will change, remains the same? Looking at it, it looks just a Dto, with nothing really interesting (at business level).
see 1.
I think I may be using generic interfaces inappropriately (but not sure so please tell me). I have a small inheritance hierarchy for horse racing. There are 3 primary interfaces : IMeeting + IRace + IRunner which I have reduced for the example. The meeting contains races which contains runners. I have used generics to make runtime decisions on the concrete types but it looks ugly, the WriteData method param has to declare the type for IMeeting which has to declare the type for IRace etc e.g.
static void WriteData(IMeeting<IRace<IRunner, string>> meeting)
Here is the little example:
class Program
{
static void Main(string[] args)
{
IMeeting<IRace<IRunner, string>> meeting = new Meeting<IRace<IRunner, string>>();
IRace<IRunner, string> slrace = new SL_Race<IRunner, string>();
IRunner slrunner = new SL_Runner();
slrace.Runners.Add(slrunner);
meeting.Races.Add(slrace);
WriteData(meeting);
}
static void WriteData(IMeeting<IRace<IRunner, string>> meeting)
{
// Write to db or whatever
}
}
public interface IMeeting<T_Race>
{
string Course { get; set; }
string CourseId { get; set; }
List<T_Race> Races { get; set; }
}
public class Meeting<T_Race> : IMeeting<T_Race>
{
public string Course { get; set; }
public string CourseId { get; set; }
public List<T_Race> Races { get; set; }
public Meeting()
{
Races = new List<T_Race>();
}
}
public interface IRace<T_Runner, T_Going>
{
T_Going Going { get; set; }
List<T_Runner> Runners { get; set; }
}
public interface ISL_Race<T_Runner, T_Going> : IRace<T_Runner, T_Going>
{
// Extended behaviour
string Time { get; set; }
string RaceId { get; set; }
string Info { get; set; }
uint MaxOR { get; set; }
}
public class SL_Race<T_Runner, T_Going> : ISL_Race<T_Runner, T_Going>
{
// IRace
public T_Going Going { get; set; }
public List<T_Runner> Runners { get; set; }
// ISL_RACE
public string Time { get; set; }
public string RaceId { get; set; }
public string Info { get; set; }
public uint MaxOR { get; set; }
public SL_Race()
{
Runners = new List<T_Runner>();
}
}
public interface IRunner
{
string Name { get; set; }
}
public class SL_Runner : IRunner
{
public string Name { get; set; }
}
In my real world app there are a few different types of concrete runner and races. I am trying to create a relevant meeting at runtime. In my mind IMeeting must have a declaration for IRaces but the concrete race type can't be known until runtime and same for the runners. My real world app also has more generic parameters and I end up with ugly looking method signatures that have to be aware of the types up the hierarchy e.g.
public List<IMeeting<IRP_Race<IRP_Runner, Going>>> ExtractMeetingList(String dayResultPage)
So am I using generics inappropriately? I could remove all generics by moving the generic properties down to concrete classes and specify them as non generic e.g. I could move IRace.Runners to the SL_Race class, but it seems to me it should be in IRace as a race interface should have runners.
Thanks for any input.
**edit - having thought about it I should probably remove the generics and just create a subclass that has the required types that will be known at compile time e.g.
public interface IMeeting
{
string Course { get; set; }
string CourseId { get; set; }
}
// This is the new subclass with the list of concrete races "ISL_RACE"
public class ISL_Meeting : IMeeting
{
List<ISL_Race> Races { get; set; }
}
public class SL_Meeting : ISL_Meeting
{
public string Course { get; set; }
public string CourseId { get; set; }
List<ISL_Race> Races {get; set;}
public SL_Meeting()
{
Races = new List<ISL_Race>();
}
}
Breeze doesn't expand TPH entities correctly.
When using expand in breeze if you are using TPH expand will only work for the first entity, the others properties will be null. If I change the entity not to use inheritances it works fine. I've also tested returning each entity separately in an expand query that also worked fine.
//client side code
var getResidentById = function (id, obs) {
var query = EntityQuery.from('Residents')
.where('id', '==', id)
.expand('user, currentUnit, leases, leases.unit, leases.leaseStatus');
return manager.executeQuery(query).then(function (data) {
if (obs) {
obs(data.results[0])
}
}, queryFailed);
};
//Controler Endpoint
[HttpGet]
public IQueryable<Resident>
{
return _context.Context.UserDetails.OfType<Resident>();
}
//Model
public class UserDetail : EntityBase<int>, IArchivable, IHasPhoto, IDeactivatableEntity, IUpdatable
{
public bool IsArchived { get; set; }
public int LastUpdatedById { get; set; }
public UserProfile LastUpdatedBy { get; set; }
public DateTimeOffset LastUpdatedDate { get; set; }
public string PhotoUri { get; set; }
public bool IsInactive { get; set; }
}
public abstract class UserBelongsToApartmentComplex : UserDetail, IBelongsToApartmentComplex
{
public int ApartmentComplexId { get; set; }
public virtual ApartmentComplex ApartmentComplex { get; set; }
public virtual bool IsInSameComplexAs(IRelatedToApartmentComplex otherEntity)
{
return ApartmentComplexId == otherEntity.ApartmentComplexId;
}
}
public class Staff : UserBelongsToApartmentComplex
{
public string Title { get; set; }
}
public class Admin : UserDetail
{
public string AccessLevel { get; set; }
}
public class Resident : UserBelongsToApartmentComplex
{
public string Pets { get; set; }
public bool HasInsurance { get; set; }
public virtual IList<Lease> Leases { get; set; }
public int? CurrentUnitId { get; set; }
public virtual Unit CurrentUnit { get; set; }
public Resident()
{
Leases = new List<Lease>();
}
}
//response data from sever from endpoint public IQueryable Residents()
[{"$id":"1","$type":"RadiusBlue.Core.Models.Resident, RadiusBlue.Core","Pets":"Sadie, a westie","HasInsurance":false,"Leases":[{"$id":"2","$type":"RadiusBlue.Core.Models.Lease, RadiusBlue.Core","Start":"2012-05-23T00:00:00.000","End":"2013-05-23T00:00:00.000","UnitId":2,"Unit":{"$id":"3","$type":"RadiusBlue.Core.Models.Unit, RadiusBlue.Core","Building":"B","Floor":2,"ModelName":"Tera","RentAmount":2500.00,"NumberOfBeds":1,"NumberOfBaths":3,"UnitName":"102A","IsInactive":true,"Inhabitants":[],"ApartmentComplexId":1,"ApartmentComplex":{"$id":"4","$type":"RadiusBlue.Core.Models.ApartmentComplex, RadiusBlue.Core","Name":"The Stratford","StreetAddress":"100 S Park Ave","City":"Winter Park","StateId":10,"ZipCode":"32792","PropertyManagementCompanyId":1,"IsInactive":false,"TimeZoneId":"Eastern Standard Time","TimeZone":{"$id":"5","$type":"System.TimeZoneInfo, mscorlib","Id":"Eastern Standard Time","DisplayName":"(UTC-05:00) Eastern Time (US & Canada)","StandardName":"Eastern Standard Time","DaylightName":"Eastern Daylight Time","BaseUtcOffset":"-PT5H","AdjustmentRules":[{"$id":"6","$type":"System.TimeZoneInfo+AdjustmentRule, mscorlib","DateStart":"0001-01-01T00:00:00.000","DateEnd":"2006-12-31T00:00:00.000","DaylightDelta":"PT1H","DaylightTransitionStart":{"$id":"7","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":4,"Week":1,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false},"DaylightTransitionEnd":{"$id":"8","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":10,"Week":5,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false}},{"$id":"9","$type":"System.TimeZoneInfo+AdjustmentRule, mscorlib","DateStart":"2007-01-01T00:00:00.000","DateEnd":"9999-12-31T00:00:00.000","DaylightDelta":"PT1H","DaylightTransitionStart":{"$id":"10","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":3,"Week":2,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false},"DaylightTransitionEnd":{"$id":"11","$type":"System.TimeZoneInfo+TransitionTime, mscorlib","TimeOfDay":"0001-01-01T02:00:00.000","Month":11,"Week":1,"Day":1,"DayOfWeek":"Sunday","IsFixedDateRule":false}}],"SupportsDaylightSavingTime":true},"Users":[{"$ref":"1"}],"Groups":[],"IsArchived":false,"ApartmentComplexId":1,"Id":1},"Id":2},"ResidentId":3,"Resident":{"$ref":"1"},"LeaseStatusId":4,"LeaseStatus":{"$id":"12","$type":"RadiusBlue.Core.Models.LeaseStatus, RadiusBlue.Core","Description":"Lost","Id":4},"Id":1},{"$id":"13","$type":"RadiusBlue.Core.Models.Lease, RadiusBlue.Core","Start":"2013-05-24T00:00:00.000","End":"2014-05-24T00:00:00.000","UnitId":1,"Unit":{"$id":"14","$type":"RadiusBlue.Core.Models.Unit, RadiusBlue.Core","Building":"A","Floor":2,"ModelName":"Aqua","RentAmount":2000.00,"NumberOfBeds":2,"NumberOfBaths":1,"UnitName":"101A","IsInactive":true,"Inhabitants":[{"$ref":"1"}],"ApartmentComplexId":1,"ApartmentComplex":{"$ref":"4"},"Id":1},"ResidentId":3,"Resident":{"$ref":"1"},"LeaseStatusId":1,"LeaseStatus":{"$id":"15","$type":"RadiusBlue.Core.Models.LeaseStatus, RadiusBlue.Core","Description":"Active","Id":1},"Id":2}],"CurrentUnitId":1,"CurrentUnit":{"$ref":"14"},"ApartmentComplexId":1,"ApartmentComplex":{"$ref":"4"},"Id":3,"User":{"$id":"16","$type":"RadiusBlue.Core.Models.UserProfile, RadiusBlue.Core","UserName":"vjiawon#gmail.com","FirstName":"Vishal","LastName":"Jiawon","Age":27,"PhoneNumber":"123 456 7890","IsInactive":false,"UserDetail":{"$ref":"1"},"GroupMembers":[],"MaintenanceRequests":[],"Id":3},"IsArchived":false,"LastUpdatedById":1,"LastUpdatedDate":"0001-01-01T00:00:00.000+00:00","IsInactive":false,"CreatedById":1,"CreatedDate":"0001-01-01T00:00:00.000+00:00"}]
I do not doubt that there is a bug in BreezeJS somewhere.
I can report that, at least as of v.1.3.4, Breeze can expand multiple navigation properties of a TPH class ... and not just on the first entity returned.
I just modified the "can navigate to AccountType eagerly loaded with expand" test in inheritanceTests.js in DocCode so that (a) it also expands the Status navigation and (b) the tests are performed on the 3rd entity returned rather than the 1st.
The query is something like this:
var em = newEm(); // clean, empty EntityManager
return EntityQuery.from('bankRootTPHs').take(3)
.expand('AccountType, Status'))
.using(em).execute().then(success).fail(handleFail);
...
function success(data) {
var entity = data.results[data.results.length-1]; // get the last one (the 3rd)
var type = data.query.entityType.shortName;
if (!entity) {
ok(false, "a query failed to return a single " + type);
}
// more tests
// I just set a breakpoint and inspected
// entity.accountType() and entity.status()
// Both returned the expected related entities
}
I see that both the related AccountType and the related Status are available from the entity.
So something else is wrong.
Questions about your Example
First I am compelled to observe that you have a lot of expands. I count 5 related entities. That can hurt performance. I know we're not talking about that but I'm calling it out.
Second, the super class UserDetail is concrete but the intermediate derived class UserBelongsToApartmentComplex is abstract. You have inheritance class hierarchies that go concrete/abstract/concrete. The queried type, Residents is one such class. And a class at every level maps to the "UserDetail" table, yes?
I'm pretty sure we didn't test for that scenario ... which is pretty uncommon. I wasn't even sure that worked! For now I have to take your word for it that EF allows such a construct.
It would seem that BreezeJS is confused about it. We'll take a look.