Is it possbile to make AutoMapper call a method after mapping source and destination?
My ViewModel looks like this:
public class ShowCategoriesViewModel
{
public int category_id { get; set; }
public string category_name { get; set; }
public List<MvcApplication3.Models.Category> SubCategories { get; set; }
public void Sort()
{
SubCategories.Sort(new CompareCategory());
}
}
And my Controller looks like this:
public ActionResult Index()
{
var category = db.Category.Where(y => y.parrent_id == null).ToList();
Mapper.CreateMap<Category, ShowCategoriesViewModel>().
ForMember(dest => dest.SubCategories, opt => opt.MapFrom(origin => origin.Category1));
List<ShowCategoriesViewModel> scvm = Mapper.Map<List<Category>, List<ShowCategoriesViewModel>>(category);
foreach (ShowCategoriesViewModel model in scvm)
{
model.Sort();
}
return View(scvm);
}
I would like to have AutoMapper call the Sort() method, instead of doing a foreach loop. Is this possible?
I think you can use .AfterMap here
Mapper.CreateMap<Category, ShowCategoriesViewModel>()
.ForMember(dest => dest.SubCategories, opt => opt.MapFrom(origin => origin.Category1))
.AfterMap((c,s) => s.Sort());
Related
I have a "Product" table that has related "Tegs"
public class Product
{
public Product()
{
Tegs = new List<Teg>();
}
[Key]
public int Id { get; set; }
public string ProductName { get; set; }
public virtual ICollection<Teg> Tegs { get; set; }
public virtual ICollection<Product> RelatedProducts { get; set; }
}
This is the Tegs:
public class Teg
{
public Teg()
{
}
public int Id { get; set; }
public string TegName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
Given a product ID, using LINQ I would like to retrieve all products that have at least one teg as the selected product.
I am using this code:
Product product = db.Product.Include(u => u.Tegs).Where(u => u.Id == id).Single();
List<int> tegid = product.Tegs.Select(c => c.Id).ToList();
IEnumerable<Product> relatedProducts = db.Product.Include(u => u.Tegs).Where(p => p.Tegs.Any(t => tegid.Contains(t.Id)));
Question 1:
How can I exclude from the relatedProducts the product with the ID?
Question 2:
How can nest my List inside the Linq query for the relatedProducts ?
On your first question, if I understand correctly, you want to exclude the product with the given productId from relatedProducts result list. In this case you just need to add one more condition to your Where() clause like this:
IEnumerable<Product> relatedProducts = db.Product
.Include(u => u.Tegs)
.Where(p => p.Tegs.Any(t => tegid.Contains(t.Id)) && p.Id != id);
And about your second question, I'm not quite sure what's the point here. Maybe you've meant to combine the second and third query in a single roundtrip to db?
I have this error on read my view on #foreach.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. Model is null.
this is my userprofile model.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Secretarias> Secretarias { get; set; }
public DbSet<Secretarias_Direcciones> Secretarias_Direcciones { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
public int UserId { get; set; }
[Required]
public string Nombre { get; set; }
[Required]
public int SecretariaId { get; set; }
[Required]
public int DireccionId { get; set; }
[Required]
public string UserName { get; set; }
}
this is my control
public ActionResult ListaUsuarios()
{
UsersContext db = new UsersContext();
var model = from usrs in db.UserProfiles
select new { usrs.UserId, usrs.Nombre, usrs.UserName, usrs.SecretariaId, usrs.DireccionId };
ViewBag.Model = model.ToList();
return View();
}
this is my view
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Nombre)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.SecretariaId)
</td>
<td>
#Html.DisplayFor(modelItem => item.DireccionId)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.UserId }) |
#Html.ActionLink("Details", "Details", new { id=item.UserId}) |
#Html.ActionLink("Delete", "Delete", new { id=item.UserId })
</td>
</tr>
}
Here are the Exception Details:
System.NullReferenceException: Object reference not set to an instance
of an object. Model is null.
You're getting the NullReferenceException because you haven't passed a strongly-typed model to your view. So in your foreach loop, Model is null. The normal way of doing that is like so:
return View(model);
The real problem is that you're mixing different concepts here. You're trying to access the properties of a model in a strongly-typed way, and yet you're defining a ViewBag.Model property, which is dynamic and has no relation whatsoever to the Model property within a view.
If you want to access your model in a strongly-typed way, and that's certainly the way I'd suggest you do it, you need to make several changes. I'd suggest you first define a view model to represent the data for a UserProfile:
public class UserProfileViewModel
{
public int UserId { get; set; }
public string Nombre { get; set; }
public int SecretariaId { get; set; }
public int DireccionId { get; set; }
public string UserName { get; set; }
}
Now, change your action to create a list of these:
public ActionResult ListaUsuarios()
{
using (UsersContext db = new UsersContext())
{
var model = from usrs in db.UserProfiles
select new UserProfileViewModel
{
UserId = usrs.UserId,
Nombre = usrs.Nombre,
UserName = usrs.UserName,
SecretariaId = usrs.SecretariaId,
DireccionId = usrs.DireccionId
};
return View(model.ToList());
}
}
Notice that I've made several changes here. Firstly, I've added the using statement to ensure UsersContext is disposed properly. Next, I've changed the query to instantiate UserProfileViewModels, instead of anonymous types. Lastly, I'm directly passing model as a parameter to View(), making sure to call ToList() on it, in order to evaluate the query before UsersContext gets disposed.
Finally, you just need to make one change to your view:
#model List<UserProfileViewModel>
#foreach (var item in Model)
{
// rest of code goes here
}
The #model directive specifies the exact model type that the view should expect to receive, thus making it strongly-typed.
Obviously - check the data types of each of the Ids that you are using to perform your joins in your database. Also ensure the database data types were mapped to the correct .Net data types.
If the data types of your ID fields appear to be ok, I would suggest narrowing down which join is causing the error. Modify you LINQ expression to have a single join and ensure there is no error. Add the other joins in one at a time until the error appears.
For your model, please check that it is being populated.
Depending on whatever is happening, it could be that your model is just null
and possibly its type might be something that it is not expected.
-- in the controller a foreach loop on the model context reference will let you see the values
I have a model:
public class TestModel {
[Display(Name = "Date")]
public DateTime Date { get; set; }
}
with Html.LabelFor helper method in Test.cshtml page
#Html.LabelFor(m => m.Date )
and use this page with 2 MVC action methods: Create and Update.
Example:
public virtual ViewResult Create() {
return View("Test");
}
public virtual ViewResult Update() {
return View("Test");
}
and I want to display #Html.LabelFor(m => m.Date ) with Create page: "Date" and Update page: "Update Date" . I know if the normal way of MVC3 can't do this. I hope your ideal can edit Html.LabelFor hepler method or anything way to bind data to Html.LabelFor in action methods on the controller
Adding a hiddenFor field will bind the data to your Model.
#Html.HiddenFor(m=>m.Date);
For override, please just look this answer
https://stackoverflow.com/a/5196392/5557777
you can override editorfor like this How can I override the #Html.LabelFor template? but I think you can do it more easily with ViewBag:
public virtual ViewResult Create() {
ViewBag.Title = "Create";
return View("Test");
}
public virtual ViewResult Update() {
ViewBag.Title = "Update";
return View("Test");
}
in view:
#string.format("{0} Date" , ViewBag.Title )
I am trying to access the DataTypeName from the ModelMetadata in a custom DisplayTemplate, but it is always null. My goal is to make a decimal.cshtml template that looks for the DataTypeName and if it's equal to Currency format it as currency. Otherwise display the decimal like normal.
Here's an example of what I have now:
The Model:
public class MyModel
{
[DataType(DataType.Currency)]
public decimal foo { get; set; }
}
In a view that is strongly typed to MyModel I have a call like this: #Html.DisplayFor(m => m.foo)
The Display template (~/Views/Shared/DisplayTemplates/decimal.cshtml)
#model decimal?
#{
var type = ViewData.ModelMetadata.DataTypeName; // This is always null
}
I know that it's using my DisplayTemplate like it's supposed to, but I would expect the DataTypeName to not be null in this case since I have the DataType attribute on the model. Is there any way to access the DataType in a display template like this?
Strange, it should work. I am unable to reproduce. Here's what I tried in a new application:
Model:
public class MyModel
{
[DataType(DataType.Currency)]
public decimal foo { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyModel
{
foo = 12.5m
});
}
}
View (~/Views/Home/Index.cshtml):
#model MyModel
#Html.DisplayFor(x => x.foo)
Display template (~/Views/Home/DisplayTemplates/decimal.cshtml):
#model decimal?
#ViewData.ModelMetadata.DataTypeName
When I run the application it prints Currency as expected.
Hopefully this will have a simple answer.
Using MVC3, I am passing a simple list of POCO objects as a model to my view:
public partial class PeopleAddress
{
public int Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
I use the PeopleId as the FK property to the Person entity, and the Person navigational property to navigate to the object. Here is my controller for the view:
public ViewResult Index()
{
var peopleaddresses = db.PeopleAddresses.Include("Person");
return View(peopleaddresses.ToList());
}
Pretty trivial. I add the columns to the grid and normal edit modes, etc in the view, but for the PersonId property.
QUESTION ABOUT COLUMNS: How can I get the select (normal) mode to display the model.Person.Name, but keep the edit mode on editing model.PersonId? For model binding purposes, I need the HTTP post to send PersonId.
Help!
Simple
If all you need is the Person.Id when you hit edit (you're editing outside the grid or something), then it IS that simple. Your column would be:
columns.Bound(e => e.Person).Title("Person").ClientTemplate("<#= Person ? Person.Name : '' #>");
And you can get the Id for the person:
onEdit(e) {
var personId = e.dataItem['Person'].Id;
}
Full
However, if you're trying to edit in the grid using a combobox, your column should look like:
columns.Bound(e => e.Person).Title("Person").ClientTemplate("<#= Person ? Person.Name : '' #>").EditorTemplateName("PersonSelector");
your editor template:
#(Html.Telerik().ComboBox()
.Name("YourNameGoesHere")
.DataBinding(binding => binding.Ajax().Select("SelectPeopleForComboBox","Shared")))
Your client script:
onEdit(e){
$comboBox = $(e.cell).find('#Person');
if($comboBox.length > 0) {
var comboBox = $ddl.data('tComboBox');
comboBox.fill(function(){
if (e.dataItem['Person'] != null){
ddl.value(e.dataItem['Person'].Id)
}
});
}
}