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#");
Related
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);
I am having an issue using an AutoMapper (version 5.1.1) projection combined with a Linq OrderBy Child property expression. I am using Entity Framework Core (version 1.0.0). I am getting the following error:
"must be reducible node"
My DTO objects are as follows
public class OrganizationViewModel
{
public virtual int Id { get; set; }
[Display(Name = "Organization Name")]
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
public virtual int OrganizationGroupId { get; set; }
public virtual string OrganizationGroupName { get; set; }
public virtual int StrategyId { get; set; }
public virtual string StrategyName { get; set; }
public virtual OrganizationGroupViewModel OrganizationGroup { get; set; }
}
public class OrganizationGroupViewModel
{
public virtual int Id { get; set; }
[Display(Name = "Organization Group Name")]
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
}
My corresponding entity models are as follows:
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public string TimeZone { get; set; }
public bool Active { get; set; }
//FKs
public int OrganizationGroupId { get; set; }
public int StrategyId { get; set; }
//Navigation
public virtual OrganizationGroup OrganizationGroup { get; set; }
public virtual Strategy Strategy { get; set; }
[JsonIgnore]
public virtual List<AppointmentReminder> AppointmentReminders { get; set; }
}
public class OrganizationGroup
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public virtual List<Organization> Organizations { get; set; }
}
My AutoMapper profiles are as follows:
public class OrganizationMapperProfile : Profile
{
public OrganizationMapperProfile()
{
CreateMap<Task<Organization>, Task<OrganizationViewModel>>();
CreateMap<Organization, OrganizationViewModel>()
.ForMember(dest => dest.OrganizationGroupName, opt => opt.MapFrom(src => src.OrganizationGroup.Name));
CreateMap<OrganizationInput, Organization>()
.ForMember(x => x.Id, opt => opt.Ignore());
}
}
public class OrganizationGroupMapperProfile : Profile
{
public OrganizationGroupMapperProfile()
{
CreateMap<Task<OrganizationGroup>, Task<OrganizationGroupViewModel>>();
CreateMap<OrganizationGroup, OrganizationGroupViewModel>();
CreateMap<OrganizationGroupInput, OrganizationGroup>()
.ForMember(x => x.Id, opt => opt.Ignore());
}
}
When I run the following statements I am able to run and get results from the first 2 statements:
var tmp = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.OrganizationGroup.Name).ToListAsync();
var tmp4 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("OrganizationGroup.Name").ToListAsync();
But when I add the ProjectTo I get the error listed above:
var tmp5 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.OrganizationGroup.Name).ProjectTo<OrganizationViewModel>().ToListAsync();
var tmp6 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("OrganizationGroup.Name").ProjectTo<OrganizationViewModel>().ToListAsync();
As some additional information, I am able to OrderBy with Projections working on properties of the parent class, such as:
var tmp7 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy(x => x.Name).ProjectTo<OrganizationViewModel>().ToListAsync();
var tmp8 = await _context.Organizations.Include(x => x.OrganizationGroup).OrderBy("Name").ProjectTo<OrganizationViewModel>().ToListAsync();
Anyone run into this issue before? Looks like I'm trying to do something that is otherwise not supported, is that by design? Thanks for any help/insight.
Looks like the problem is caused by the OrganizationGroup property of the OrganizationViewModel class - AutoMapper generates a null check which EF Core doesn't like in the combination with your OrderBy (I guess just one of the many bugs currently in EF Core). It can easily be reproduced by the following simple manual projection query:
var tmp5a = _context.Organizations
.OrderBy(x => x.OrganizationGroup.Name)
.Select(e => new OrganizationViewModel
{
Id = e.Id,
OrganizationGroup = e.OrganizationGroup != null ? new OrganizationGroupViewModel
{
Id = e.OrganizationGroup.Id,
Name = e.OrganizationGroup.Name,
Active = e.OrganizationGroup.Active,
} : null,
})
.ToList();
To fix the issue, configure AutoMapper to not generate null check for that property as follows:
CreateMap<Organization, OrganizationViewModel>()
.ForMember(dest => dest.OrganizationGroup, opt => opt.AllowNull())
.ForMember(dest => dest.OrganizationGroupName, opt => opt.MapFrom(src => src.OrganizationGroup.Name));
I am trying to retrieve the Person name in my viewmodel while projection in the below code:
// GET api/Tickets
public IQueryable Get()
{
var model = Uow.Tickets.GetAll().OrderByDescending(m => m.DateTimeTag)
.Select(m => new TicketViewModel
{
Id = m.Id,
TicketTitle = m.TicketTitle,
TicketBody = m.TicketBody,
DateTimeTag = m.DateTimeTag,
//AssignedTo = Uow.Persons.GetById(m.AssignedToPersonId).Name,
Status = m.Status.ToString(),
NoOfReplys = m.Replys.Count()
});
return model;
}
But when I uncomment the AssignedTo line, it gives me the error:
InnerException: {
Message: "An error has occurred.",
ExceptionMessage: "LINQ to Entities does not recognize the method 'Ticketing.Model.Person GetById(Int32)' method, and this method cannot be translated into a store expression.",
ExceptionType: "System.NotSupportedException",
StackTrace: " at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq) at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) blah blah blah
The TicketViewModel class is:
public class TicketViewModel
{
public int Id { get; set; }
public string TicketTitle { get; set; }
public string TicketBody { get; set; }
public DateTime DateTimeTag { get; set; }
public string AssignedTo { get; set; }
public string Status { get; set; }
public int NoOfReplys { get; set; }
}
The actual Ticket class is:
public class Ticket
{
public int Id { get; set; }
public string TicketTitle { get; set; }
public string TicketBody { get; set; }
public DateTime DateTimeTag { get; set; }
public int AssignedToPersonId { get; set; }
public Status Status { get; set; }
public virtual ICollection<Reply> Replys { get; set; }
}
My desired output is:
[
{
Id: 3,
TicketTitle: "a problem",
TicketBody: "problem descripted here.",
DateTimeTag: "2012-04-21T00:00:00",
AssignedTo: "Peter", <== ATTENTION!!!
Status: "Open",
NoOfReplys: 0
}
]
Here, Peter is the name of the person who its id is in the ticket object.
My goal is to show the name instead of personId.
may be there is a better way, please help me do that.
thanks
In this case I think that your property:
public int AssignedToPersonId { get; set; }
should be:
public Person AssignedToPerson { get; set; }
in your Ticket class. Mapping to the reference is generally better so that you can access properties like this using Linq. This way the line that is giving you trouble can be:
AssignedTo = AssignedToPerson.Name
The reason it isn't working right now is because Entity Framework has no idea how to convert your line:
Uow.Persons.GetById(m.AssignedToPersonId).Name
to a Query expression. By using a reference mentioned above you will instead create a Join between the two tables and get back the desired data in a single query.
The other and probably less attractive option is to store the Id in your View Model and then do a query for the name outside your Linq query. This will work because you have already retrieve items from the database. Untested example below:
public IQueryable Get()
{
var model = Uow.Tickets.GetAll().OrderByDescending(m => m.DateTimeTag)
.Select(m => new TicketViewModel
{
Id = m.Id,
TicketTitle = m.TicketTitle,
TicketBody = m.TicketBody,
DateTimeTag = m.DateTimeTag,
AssignedToPersonId = m.AssignedToPersonId,
Status = m.Status.ToString(),
NoOfReplys = m.Replys.Count()
}).ToList();
model.ForEach(m => m.AssignedTo = Uow.Persons.GetById(m.AssignedToPersonId).Name);
return model;
}
Note however that this second method is making an additional query to the database for each Ticket object returned in the first query.
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.
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");