I don't know exactly the term of my problem, maybe thats the reason why I can not find such relevant issues in here.
Say, I have these models.
public abstract class Member
{
public string ID { get; set; }
public string Username { get; set; }
public int MemberType { get; set; } //1 if person; 2 if business
//public string MemberName { get; set; }
}
public class Person : Member
{
public string LastName { get; set; }
public string FirstName{ get; set; }
}
public class Business : Member
{
public string BusinessName { get; set; }
public string TaxNo { get; set; }
}
Right now, I do not have a problem on accessing those models using fluent API.
Now, what I want to achieve is to add a property on my Member class which is MemberName, which will be derived from LastName and FirstName property of Person class if MemberType is 1 and BusinessName of Business class if MemberTYpe = 2.
Is there an easy way to achieve this kind of thing?
Thanks
This can be done with a bit of abstract magic.
On Member have this:
public abstract string MemberName { get; }
Then on Person you override as:
public override string MemberName
{
get{ return FirstName + " " + LastName; }
}
on Business you override as such:
public override string MemberName
{
get{ return BusinessName; }
}
Now, any instanceof any class which inherits Member, when calling the MemberName property will return the right representation. This is all basic inheritance/OOP/Polymorphism whatever you want to call it.
Related
I have those 2 Models
public class BranchEmployees
{
public int ID { get; set; }
[Required, Column(Order = 0), Key]
public string ApplicationUserID { get; set; }
[Required, Column(Order = 1), Key]
public int BranchID { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<Branch> Branch { get; set; }
}
public class Branch
{
public int ID { get; set; }
public string BranchName { get; set; }
[Required]
public string ApplicationUserID { get; set; }
public ApplicationUser User { get; set; }
public virtual ICollection<BranchEmployees> BranchEmployees { get; set; }
}
public class ApplicationUser
{
//rest of the code
}
UPDATE
I have everything set up but what I want is the query that gets me the Employees whose IDs are in the branch employees table
, I'm using entity framework code first with MVC 5 , how do I do it ?
Assuming that your ApplicationUser class will have a navigational property called BranchEmployees, here is the query that gets me the Employees whose IDs are in the branch employees table
List<ApplicationUsers> employeeNames =
dbContext
.ApplicationUsers
.Where(au => au.BranchEmployees
.Count() > 0).ToList();
Also, can you provide whole model including ApplicationUser? I also wonder why you do not prefer BranchEmployees to inherit from ApplicationUser.
You don't need a class that indicates a many-to-many relation between two tables when you do code-first. The key here is to create virtual properties of those classes. Lets say you have a class Student and class Course. Students can be in many Courses and Courses can have many Students. To generate a database using these models the classes should look like this:
public class Student
{
private ICollection<Course> _courses;
public Student()
{
this._courses = new HashSet<Course>();
}
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public virtual ICollection<Course> Courses
{
get { return this._courses; }
set { this._courses = value; }
}
}
And for Course:
public class Course
{
private ICollection<Student> _students;
public Course()
{
this._students = new HashSet<Student>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students
{
get { return this._students; }
set { this._students = value; }
}
}
I hope that this can help you solve your issue.
I have defined a (poco?) class in my domain project:
public class Club
{
public Club()
{
ContactPersons = new HashSet<ContactPerson>();
}
public int Id { get; set; }
[Required]
[StringLength(64)]
public string Name { get; set; }
public virtual ICollection<ContactPerson> ContactPersons { get; set; }
}
public class ContactPerson
{
public virtual int Id { get; set; }
[StringLength(64)]
public virtual string FirstName { get; set; }
[StringLength(64)]
public virtual string LastName { get; set; }
}
In my MVC project I have my clubcontroller:
public ActionResult Create(CreateClubViewModel model)
{
Club club = new Club();
model.Initialize(club);
IClubDb clubDb = DependencyResolverHelper.IClubDbService;
clubDb.Create(club); // create club in db
}
public ActionResult Display(string domain)
{
try
{
IClubDb clubDb = DependencyResolverHelper.IClubDbService;
Club club = clubDb.Get(domain);
return View(club);
}
catch (Exception) // user is not logged iin
{
return View();
}
}
Finally, in my DB project I create and retrieve the club,
public Club Get(string name)
{
return DataContext.Clubs
//.Include(x => x.ContactPersons)
.Single(r => r.Name == name);
}
public int Create(Club club)
{
DataContext.Clubs.Add(club);
return DataContext.SaveChanges();
}
I have tried everything to get EF to lazy load the ContactPersons of my club object when I call the Get club in the Display method but ContactPersons has always a length of zero. However, if I eager load contact persons using the .include (I have commented this part out), then obviously ContactPersons contains a number of contacts.
I am not sure what I am doing wrong:
I have followed the guidelines for defining poco classes: http://msdn.microsoft.com/en-us/library/dd468057.aspx
I have a public parameter less constructor (but not protected constructor)
I have lazyloading enabled
I think I am missing a concept, the poco club class is also my domain entity which I insert into DB. What am I doing wrong? Whay I can't get lazy loading to work?
try
ContactPersons.ToList();
this will force all entities to be loaded.
see Entity framework, when call ToList() it will load FK objects automatically?
It seems that your LazyLoading performs when your dbContext is closed. So it will not load.
You use ContactPerson in view, am i right?
Did you forget to include the foreign key in your entity?
public class ContactPerson
{
public virtual int Id { get; set; }
[StringLength(64)]
public virtual string FirstName { get; set; }
[StringLength(64)]
public virtual string LastName { get; set; }
public int ClubId { get; set; }
[ForeignKey("ClubId")]
public virtual Club Club { get; set; } // if you need
}
I have multiple classes that I need to map into 1 class:
This is the source that I'm mapping from(view model):
public class UserBM
{
public int UserId { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string State { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
}
This is how the destination class is(domain model):
public abstract class User
{
public int UserId { get; set; }
public virtual Location Location { get; set; }
public virtual int? LocationId { get; set; }
}
public class Location
{
public int LocationId { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string State { get; set; }
public virtual int CountryId { get; set; }
public virtual Country Country { get; set; }
}
This is how my automapper create map currently looks:
Mapper.CreateMap<UserBM, User>();
Based on the documents on automapper codeplex site, this should be automatic but it doesn't work. Address, Address2, etc is still null. What should my createmap look like?
The reason is because AutoMapper can't map all those flat fields to a Location object by convention.
You'll need a custom resolver.
Mapper.CreateMap<UserBM, User>()
.ForMember(dest => dest.Location, opt => opt.ResolveUsing<LocationResolver>());
public class LocationResolver : ValueResolver<UserBM,Location>
{
protected override Location ResolveCore(UserBMsource)
{
// construct your object here.
}
}
However, i dont like this. IMO, a better way would be to encapsulate those properties in your ViewModel into a nested viewmodel:
public class UserBM
{
public int UserId { get; set; }
public LocationViewModel Location { get; set; }
}
Then all you have to do is define an additional map:
Mapper.CreateMap<User, UserBM>();
Mapper.CreateMap<LocationViewModel,Location>();
Then it will all work.
You should try and make use of AutoMapper conventions, where possible. And it's certainly possible to make your ViewModel more hierachical, to match the destinations hierachy.
EDIT strange this question has been asked by you.. seems to be a same question - i guess i am missing something...
Check this SQ Question
*
Define two mappings, both mapping from the same source to different
destinations
*
I think you need to make the property names similar to LocationAddress and LocationAddress2 on UserBM for their automatic projection to work, but I may be wrong.
Check out their page on Flattening they have property names that have both property names of the source concatenated like I indicated.
Simply follow the naming convention in your target class and prefix the address properties with Location since that's the property name in the source class:
public class UserBM
{
public int UserId { get; set; }
public string LocationAddress { get; set; }
public string LocationAddress2 { get; set; }
public string LocationAddress3 { get; set; }
public string LocationState { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
}
I have this code :
public class OrderModel
{
public List<Order> Orders { get; set; }
}
public class Order
{
public string Code { get; set; }
public DateTime CreationDate { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
I'd like get a List<MyClass> MyClass look like :
public class MyClass
{
public string OrderCode { get; set; }
public string OrderCreationDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Automapper can help me for this ? if no other solution to avoid loop ?
Thanks,
To do DTO flattening with automapper looks at this post and also this. They should answer your question.
If you don't want to use automapper I would use a simple Linq. Something like this
var myClassList = (from p in OrderModel.Orders select new MyClass()
{
OrderCode = p.Code,
OrderCreationDate = p.CreationDate,
FirstName = p.Customer.FirstName,
LastName = p.Customer.LastName
}).ToList();
I am using EF4 CTP5. Here are my POCOs:
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
Is there a way to get Address to be a ComplexType for the Order class? After playing around with this, I'm guessing not, but maybe there's a way I haven't seen.
EDIT: In response to Shawn below, I gave it my best shot:
//modelBuilder.Entity<Order>().Ignore(o => o.BillingAddress);
//modelBuilder.Entity<Order>().Ignore(o => o.ShippingAddress);
modelBuilder.Entity<Order>()
.Property(o => o.BillingAddress.City).HasColumnName("BillingCity");
Fails at runtime with error "The configured property 'BillingAddress' is not a declared property on the entity 'Order'." Trying to use Ignore() doesn't work. Next, the Hanselman article is CTP4, but the CTP5 equivalent is:
modelBuilder.Entity<Order>().Map(mapconfig =>
{
mapconfig.Properties(o => new {
o.Id
, o.Total
, o.BillingAddress.City
});
mapconfig.ToTable("Orders");
});
Fails with error "Property 'BillingAddress.City' of type 'Order' cannot be included in its mapping."
I give up. Maybe the final release will have something like this. Or maybe I need to switch to NHibernate =)
All you need to do is to place ComplexTypeAttribute on Address class:
[ComplexType]
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
Alternatively, you can achieve this by fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Address>();
}
But you cannot have Address type as to be both an Entity and a Complex Type, it's one way or another.
Take a look at this blog post where I discuss this at length:
Associations in EF Code First CTP5: Part 1 – Complex Types
If you want Address to be in the same table as Order, you're going to have to tell EF that in the DbContext OnModelCreating override.
Take a look here: http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx