how to map properties of a member object of an entity to the table field in EF Code First - entity-framework-4.1

i m using EF Code First, and want to map an Entity class Person to entity table personTable as follows,
i have a an Entity Class
public class Person
{
public Guid Id
{
get;
set;
}
public string Email
{
get;
set;
}
public int Property
{
get;
set;
}
public Name Name
{
get;
set;
}
}
and a class for type of Property Name
public class Name
{
public string FirstName
{
get;
set;
}
public string FullName
{
get
{
return string.Format("{0} {1} {2}", FirstName, MiddleName, LastName);
}
}
public string LastName
{
get;
set;
}
public string MiddleName
{
get;
set;
}
}
i want to map person class to person table as follows
Person.Id => personTable.ID
Person.Name.FirstName ->personTable.FirstName
Person.Name.MiddleName => personTable.MiddleName
Person.Name.LastName => personTable.LastName
and so on....
where Person.Name is an object of type Name Class

Name is a complex type and you must EF tell this, otherwise it will consider Name as an entity and Person.Name as a navigation property. Mapping with Fluent API:
modelBuilder.ComplexType<Name>();
modelBuilder.Entity<Person>()
.Property(p => p.Name.FirstName)
.HasColumnName("FirstName");
modelBuilder.Entity<Person>()
.Property(p => p.Name.MiddleName)
.HasColumnName("MiddleName");
modelBuilder.Entity<Person>()
.Property(p => p.Name.LastName)
.HasColumnName("LastName");

Related

nPoco V3 - many to many not working

I have users which have many roles
public class User
{
public int Id {get;set;}
public string Name {get;set;}
public List<Role> Roles {get;set;}
}
public class Roles
{
public int Id {get;set;}
public string Key{get;set;}
}
public class UserRoles
{
public int UserId {get;set;}
public int RoleId {get;set;}
}
what I try to achieve is getting a user with all its roles in one query, but so far I failed.
For Mapping I use a custom Conventionbased mapper (I can provide the code, but it's rather big)
I tried FetchOneToMany and I tried Fetch as described here
https://github.com/schotime/NPoco/wiki/One-to-Many-Query-Helpers
https://github.com/schotime/NPoco/wiki/Version-3
But Roles is always empty.
Role and User by itself are mapped correctly and I did try to specify the relation like
For<User>().Columns(x =>
{
x.Many(c => c.Roles);
x.Column(c => c.Roles).ComplexMapping();
}, true);
Again it didn't help, roles is empty.
I have no idea what I'm missing.
Any ideas?
ComplexMapping and relationship mapping(1-to-n, n-to-n) are two different things.
ComplexMapping is for mapping nested objects for data that usually resides in the same table where there is a one-to-one relationship. For something like this:
public class Client
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public Client()
{
Address = new Address();
}
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Telephone { get; set; }
public string Country{ get; set; }
}
If you're using a convention-based mapper your override would look something like this:
For<Client>().Columns(x =>
{
x.Column(y => y.Address).ComplexMapping();
});
One thing to watch for when using a convention-based mapper; you have to enable ComplexMapping in your scanner with the following code:
scanner.Columns.ComplexPropertiesWhere(y => ColumnInfo.FromMemberInfo(y).ComplexMapping);
Otherwise ComplexMapping() calls in your overrides will simply be ignored.
One-to-Many mapping would work like this (see NPoco on Github for more):
For<One>()
.TableName("Ones")
.PrimaryKey(x => x.OneId)
.Columns(x =>
{
x.Column(y => y.OneId);
x.Column(y => y.Name);
x.Many(y => y.Items).WithName("OneId").Reference(y => y.OneId);
}, true);
For<Many>()
.TableName("Manys")
.PrimaryKey(x => x.ManyId)
.Columns(x =>
{
x.Column(y => y.ManyId);
x.Column(y => y.Value);
x.Column(y => y.Currency);
x.Column(y => y.OneId);
x.Column(y => y.One).WithName("OneId").Reference(y => y.OneId, ReferenceType.OneToOne);
}, true);

Convert lambda expression to Json in MVC

I get an error ("Cannot convert lambda expression to type 'string' because it is not a delegate type") during converting the lambda expression in controller. I have 3 entities in as below:
Entities:
public class Student
{
public int ID { get; set; }
public string Course { get; set; }
public int CityID { get; set; }
public virtual City City { get; set; }
}
public class City
{
public int ID { get; set; }
public string Name { get; set; }
public int RegionID { get; set; }
public virtual Region Region { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class Region
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
Controller:
public ActionResult Index_Read([DataSourceRequest] DataSourceRequest request)
{
var dataContext = repository.Students;
var students = dataContext.ToDataSourceResult(request, m => new
{
ID = m.ID,
Course = m.Course,
City = m.City.Name, //I can get City name and show it in View.
MyRegionName = m.City.Region.Name //I can get region name and assign it to
//"MyRegionName" parameter in JSON. However in View I cannot get it using "MyRegionName" paremeter
});
return Json(students, JsonRequestBehavior.AllowGet);
}
View:
#model IEnumerable<Student>
#(Html.Kendo().Grid<Student>()
.Name("Grid")
.Columns(columns =>
{
columns.Bound(m => m.ID);
columns.Bound(m => m.Course);
columns.Bound(m => m.City);
columns.Bound(m => m.MyRegionName);
})
.Pageable()
.Sortable()
.Filterable()
.Scrollable()
.Groupable()
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Index_Read", "Student"))
)
)
Here is the point that may cause the problem in the Controller and View:
City = m.City.Name, //I can get City name and show it in View.
MyRegionName = m.City.Region.Name //I can get region name and assign it to the "MyRegionName" parameter in JSON. However in View I cannot get it using "MyRegionName" paremeter.
May it be related to that there is City parameter in the Student entity. But there is no MyRegionName property in the City entity.
I assume this happens because there is no property called MyRegionName for the Student class.
You have two options
1) Create a ViewModel that looks like your Student model but make it has such property. Also make sure inside the projection function of the ToDataSourceResult you create those new ViewModel types instead of anonymous object.
2) Just use a Template column. e.g.
columns.Template(#<text></text>).Title("MyRegionName").ClientTemplate("#=MyRegionName#");

Razor, MVC4, #html.dropdownlistfor problems

I'm trying to create a drop down list that populates from a database. I have:
public class Employee
{
[Key]
public int Id { get; set; }
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
[Required]
public String JobTitle { get; set; }
}
public class Project
{
[Key]
public int Id { get; set; }
[Required]
public String ProjectName { get; set; }
[Required]
public String CompanyName { get; set; }
}
public class ProjectHour
{
[Key]
public int Id { get; set; }
[Required]
public decimal Hours { get; set; }
[Required]
public DateTime Date { get; set; }
public virtual ICollection<Employee> employeeId { get; set; }
public virtual ICollection<Project> projectId { get; set; }
}
What I want is to create a form that will create new project hours associated with a project and an employee. I'm trying to use dropdownlists to display the employees and the projects on the create form. Obviously, I'm completely new at this, but what I have so far is:
[HttpPost]
public ActionResult CreateProjectHour(ProjectHour newProjectHour)
{
using (var db = new TimesheetContext())
{
IEnumerable<SelectListItem> emp = db.Employees
.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.LastName
});
ViewBag.EmployeeId = emp;
db.ProjectHours.Add(newProjectHour);
db.SaveChanges();
}
return RedirectToAction("ProjectHourList");
}
}
And on the form:
#model TimesheetMVC.Models.ProjectHour
...
#Html.DropDownListFor(model => model.employeeId, (SelectList)ViewBag.EmployeeId)
Which is apparently horribly wrong. Any help would be much appreciated...!
Don't use the same name EmployeeId. You need 2 things to create a dropdown list in ASP.NET MVC: a scalar property that will hold the selected value and a collection property that will contain the possible values. But since you are using the ViewBag (which I totally recommend against) you could do the following:
ViewBag.Employees = emp;
and in your view:
#Html.DropDownListFor(
model => model.employeeId,
(IEnumerable<SelectListItem>)ViewBag.Employees
)
But as I said this is not at all an approach that I recommend. I recommend using view models. So define an IEnumerable<SelectListItem> property on your view model:
public IEnumerable<SelectListItem> Employees { get; set; }
and in your controller populate this view model property and then make your view strongly typed to the view model.
Or you could just do a
in the controller
SelectList selectList = new SelectList(db.Employees, "Id", "LastName");
ViewBag.EmployeeList = selectList;
and in the View
#Html.DropDownBoxFor(model => model.id_Employee, ViewBag.EmployeeList as SelectList)
I find this approach easier.
EmployeeId is an IEnumerable<SelectListItem>, not a SelectList.
Therefore, your cast cannot work.
You need to explicitly create a SelectList.

unidirectional many-to-many relationship with Code First Entity Framework

I am new to EF, and trying to get many-to-many unidirectional relationship with code first approach. For example, if I have following two classes (not my real model) with be a N * N relationship between them, but no navigation property from "Customer" side.
public class User {
public int UserId { get; set; }
public string Email { get; set; }
public ICollection TaggedCustomers { get; set; }
}
public class Customer {
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
The mapping code looks like ...
modelBuilder.Entity()
.HasMany(r => r.TaggedCustomers)
.WithMany(c => c.ANavgiationPropertyWhichIDontWant)
.Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("CustomerId");
m.ToTable("BridgeTableForCustomerAndUser");
});
This syntax force me to have "WithMany" for "Customer" entity.
The following url, says "By convention, Code First always interprets a unidirectional relationship as one-to-many."
Is it possible to override it, or should I use any other approach?
Use this:
public class User {
public int UserId { get; set; }
public string Email { get; set; }
// You must use generic collection
public virtual ICollection<Customer> TaggedCustomers { get; set; }
}
public class Customer {
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And map it with:
modelBuilder.Entity<User>()
.HasMany(r => r.TaggedCustomers)
.WithMany() // No navigation property here
.Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("CustomerId");
m.ToTable("BridgeTableForCustomerAndUser");
});

Navigation property is not saved when using independent association approach on EF 4.1

Following is a save routine on existing record. What's wrong with this? I'm using independent association
There's no error emitted, however, the Country_CountryId field on Person table didn't change, everything else are properly persisted. What's wrong on the following code/approach?
public JsonResult SaveUpdate(Person p)
{
p.Country = new Country { CountryId = new Guid("EF0CD98E-7138-4757-866E-ADC3C8D216DA") };
using (var db = new TheDbContext())
{
db.Entry(p).State = System.Data.EntityState.Modified;
db.SaveChanges();
}
}
Here's my mapper:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Person>().HasRequired(x => x.Country)
.WithMany(x => x.Persons)
.Map(x => x.MapKey("Country_CountryId"));
modelBuilder.Entity<Person>().Property(x => x.RowVersion).IsRowVersion();
modelBuilder.Entity<Country>().HasKey(x => x.CountryId);
}
Here's my models:
public class Country
{
public virtual Guid CountryId { get; set; }
public virtual string CountryCode { get; set; }
public virtual string CountryName { get; set; }
public virtual IList<Person> Persons { get; set; }
}
public class Person
{
public virtual Guid PersonId { get; set; }
public virtual string Username { get; set; }
public virtual string Firstname { get; set; }
public virtual string Lastname { get; set; }
public virtual byte[] RowVersion { get; set; }
public virtual Country Country { get; set; }
}
Use this instead and it will work:
public JsonResult SaveUpdate(Person p)
{
var country = new Country { CountryId = new Guid("EF0CD98E-7138-4757-866E-ADC3C8D216DA") };
using (var db = new TheDbContext())
{
db.People.Attach(p);
db.Countries.Attach(country);
db.Entry(p).State = System.Data.EntityState.Modified;
p.Country = country;
db.SaveChanges();
}
}
Independent associations require special care because each association itself has its own state which must be configured.