MVC c# ViewModel with table object - asp.net-mvc

I have a viewmodel as such
public class NoteViewModel
{
public tblNotes tblnote { get; set; }
}
In my controller, I do the following next after doing a build so my controller knows about the viewmodel:
NoteViewModel viewModel= new NoteViewModel();
viewModel.tblnote.NoteModeID = 1234; // get error here
return PartialView(viewModel);
I get the following error though:
{"Object reference not set to an instance of an object."}

What is the type tblNotes? (Side note: In C# class names should begin with a capital letter as a matter of convention.)
Since this is a custom type and, thus, a reference type, its default value is null. So when you instantiate a new NoteViewModel it's going to set all of its members to their default values unless otherwise specified. Since that value is null, you can't use it here:
viewModel.tblnote.NoteModeID = 1234;
Without knowing more about your types, the simple answer is to just instantiate that member in the view model's constructor:
public class NoteViewModel
{
public tblNotes tblnote { get; set; }
public NoteViewModel()
{
tblnote = new tblNotes();
}
}
This way the object will be instantiated any time a view model is created, so you can use it.

What exactly is tblNotes? If it is a reference type, than viewModel.tblNote is null after the first line of code is executed.

Related

Ignore property from serialization in a collection of a type with Json.Net

Is there an "easy way" to ignore a property from Model serialization on a collection of a type in Asp.Net Core?
For example
public sealed class MainViewModel
{
public Guid Id { get; set; }
[JsonIgnore("PropertyInSubViewModel")]
public ICollection<SubViewModel> Products { get; set; }
}
The idea was to remove some property in SubViewModel from model serialization, so when I get it in my action, it would have its default value set, not the one set through the request.
Not with JsonIgnore. That can only be applied on the actual property that you want to ignore, and is constant at that point. However, JSON.NET does have support for conditional serialization. The easiest and most straight-forward is adding a ShouldSerialize* method to your class. You'd obviously need to determine some condition you could lean on for the determination, but that could be a straight-forward as literally setting some boolean on your sub view model instances. Basically, you just add something like:
public class SubViewModel
{
...
public bool ShouldSerializePropertyInSubViewModel()
{
// return true or false to either allow or disallow serializing the property on this instance
}
}

Model being passed instead of parameter to partial view

I've created a partial view named "_ColorWheel.cshtml" which a string that holds the HEX value of the color.
This is the base model
public class PageComponentModel
{
// .....
public string BgColorVal { get; set; }
}
This model inherits PageComponentModel
public class OneColumnComponentModel: PageComponentModel
{
// ....
}
On their respective views, I use the same line #Html.Partial("_ColorWheel",Model.BgColorVal) to create the control. The view of OneColumnComponentModel works well, but PageComponentModel's throws
The model item passed into the dictionary is of type '.....Model.PageComponentModel', but this dictionary requires a model item of type 'System.String'.
I've tried multiple times to copy and paste the line just in case of typos or whatnot. Setting a breakpoint in razor shows that BgColorVal is string and has the correct value.

Entity Framework ASP.NET MVC private model fields

There is a field in our database which really ought to be a boolean, but for some reason the original developers made it a CHAR which will either be set to "1" or "0".
[Column("CHARGEABLE")]
[StringLength(1)]
private string Chargeable { get; set; }
I want my model to represent this field as a boolean so I figured I could add a property to my model to wrap it:
[NotMapped]
public bool ChargeableTrue
{
get
{
return Chargeable == "1" ? true : false;
}
set
{
Chargeable = value ? "1" : "0";
}
}
Now on my View I just display the EditorFor ( ChargeableTrue ), but when I click save it doesn't actually update it.
I think what is happening is that when the model is being updated, it's still attempting to get the value of 'Chargeable' from the View, even though I haven't displayed it there. And since there is no input field, it just gets null and ends up saving that to the database.
if (ModelState.IsValid)
{
db.Entry(call).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
What is one expected to do in this situation?
Based on KMan's answer, here's the extended version just in case you're not familiar with creating view models.
The idea is that your domain object is not really what you want to be updating exactly from your views. Instead, you create a go-between that can also include view-specific items (like a list of objects to populate a drop-down).
public class MyViewModel {
public bool Chargeable { get; set; }
}
Now you can do this:
#* In view *#
Html.EditorFor(m => m.Chargeable)
// In controller
public ActionResult Save(MyViewModel model) {
if (ModelState.IsValid) {
var domainObject = new MyObject() {
Chargeable = model.Chargeable ? "1" : "0"
};
// the rest of your code using domainObject
}
}
I'd consider just creating an overload of your domain object's constructor that accepts your view model to keep the mapping in one place. I typically use a tool like AutoMapper to map objects or manual extension methods.
A view model typically contains a sub-set of your domain object's properties, but can contain all of them or more properties like lists, visbility states, etc. They come in incredibly useful and I've never done a MVC project where I haven't used them.
Use a view model and make your mapping on the controller.

Can I access the discriminator value in TPH mapping with Entity Framework 4 CTP5

Using Entity Framework 4 CTP5 Code First and this example
Is it possible to access the discriminator value?
I would like to use it in a projection like
context.BillingDetails.Select(x => new { Number = x.Number, DiscrimitatorValue = /* how do I get the discriminator value? */ });
From this post I understand the discriminator cannot be mapped to a property but is there any other way of accessing it?
I may be late to the game on this one, but I just added a getter property to the base class that returned the name of the current type:
public string DiscriminatorValue {
get {
return this.GetType().Name;
}
}
Since by default EF is going to use this same value for the Discriminator field, they will match up.
In EF Core 2.1 (I haven't checked previous versions) it's enough to add Discriminator to the base abstract class as private set property. It will be mapped with adequate value.
public abstract class Entity
{
public int Id { get; set; }
public string Discriminator { get; private set; }
}
EF by itself will automatically insert appropriate discriminator value to the database and will automatically set it to an object on read.
After further information from Morteza Manavi in the comments of his post the simple answer is no
you should be aware that the discriminator column is used internally by Code First and you cannnot read/write its values from an inheritance mapping standpoint.
To access the discriminator I would have to execute a SqlQuery against the database or change my mapping strategy.
Reason aside, I recently ran into the same problem but believe this is still relevant for v4 of the EF Framework.
First, create a view which selects the discriminator value into two columns.
create view dbo.vw_BillingDetail
as
select BillingDetailId, DiscriminatorValue, DiscriminatorValue as DiscriminatorValue2 from dbo.BillingDetail
go
Secondly, map the view to your entity during context creation:
modelBuilder
.Entity<BillingDetail>()
.HasKey(n => n.BillingDetailId)
.Map(map =>
{
map.ToTable("vw_Person");
})
Thirdly, define your discriminator mapping for your derived class using one of the columns in your view:
.Map<MyDerivedBillingDetail>(map =>
{
map.Requires("DiscriminatorValue2").HasValue("YourValue");
})
Finally, define a getter and a private setter for the other discriminator column in your view with the DatabaseGenerated annotation set as Computed to prevent EF from updating/inserting for this field:
class BillingDetail
{
public BillingDetailId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DiscriminatorValue { get; private set; }
}
You can change the private setter to be protected and set this value explicitly during the construction of your derived entities so that the discriminator has a value prior to being persisted:
class MyDerivedBillingDetail : BillingDetail
{
public MyDerivedBillingDetail()
{
this.DiscriminatorValue = "MyValue";
}
}
To expand on #Michael Black's answer for Entity Framework Core 2.1 (earlier? tested in 2.1.4)
You can use any property name, database field name and data type you want.
Create a property:
[Column("foo_type_id")]
class Foo {
public FooTypesEnum TypeId {get; set;}
}
Then in your context class with the fluent API via modelBuilder:
modelBuilder.Entity<Foo>(b => {
b.HasDiscriminator(foo => foo.TypeId)
.HasValue<SubFooA>(FooTypesEnum.SubFooA)
.HasValue<SubFooB>(FooTypesEnum.SubFooB);
});
This is really useful if you need to build composable queries that e.g., group on the discriminator, etc.
Why don't you use the following query instead?
var q = con.BillingDetails.OfType<BankAccount>().ToList();
You can add a property with the name you gave to the discriminator in EF Core. Example:
In DBContext:
...HasDiscriminator<string>("Type")..
In base class do:
public string Type { get; private set; }

ASP.NET MVC ViewModel mapping with custom formatting

The project I'm working on has a large number of currency properties in the domain model and I'm needing for format these as $#,###.## for transmitting to and from the view. I've had a view thoughts as to different approaches which could be used. One approach could be to format the values explicitly inside the view, as in "Pattern 1" from Steve Michelotti :
<%= string.Format("{0:c}",
Model.CurrencyProperty) %>
...but this starts violating DRY principle very quickly.
The preferred approach appears to be to do the formatting during the mapping between DomainModel and a ViewModel (as per ASP.NET MVC in Action section 4.4.1 and "Pattern 3"). Using AutoMapper, this will result in some code like the following:
[TestFixture]
public class ViewModelTests
{
[Test]
public void DomainModelMapsToViewModel()
{
var domainModel = new DomainModel {CurrencyProperty = 19.95m};
var viewModel = new ViewModel(domainModel);
Assert.That(viewModel.CurrencyProperty, Is.EqualTo("$19.95"));
}
}
public class DomainModel
{
public decimal CurrencyProperty { get; set; }
}
public class ViewModel
{
///<summary>Currency Property - formatted as $#,###.##</summary>
public string CurrencyProperty { get; set; }
///<summary>Setup mapping between domain and view model</summary>
static ViewModel()
{
// map dm to vm
Mapper.CreateMap<DomainModel, ViewModel>()
.ForMember(vm => vm.CurrencyProperty, mc => mc.AddFormatter<CurrencyFormatter>());
}
/// <summary> Creates the view model from the domain model.</summary>
public ViewModel(DomainModel domainModel)
{
Mapper.Map(domainModel, this);
}
public ViewModel() { }
}
public class CurrencyFormatter : IValueFormatter
{
///<summary>Formats source value as currency</summary>
public string FormatValue(ResolutionContext context)
{
return string.Format(CultureInfo.CurrentCulture, "{0:c}", context.SourceValue);
}
}
Using IValueFormatter this way works great. Now, how to map it back from the DomainModel to ViewModel? I've tried using a custom class CurrencyResolver : ValueResolver<string,decimal>
public class CurrencyResolver : ValueResolver<string, decimal>
{
///<summary>Parses source value as currency</summary>
protected override decimal ResolveCore(string source)
{
return decimal.Parse(source, NumberStyles.Currency, CultureInfo.CurrentCulture);
}
}
And then mapped it with:
// from vm to dm
Mapper.CreateMap<ViewModel, DomainModel>()
.ForMember(dm => dm.CurrencyProperty,
mc => mc
.ResolveUsing<CurrencyResolver>()
.FromMember(vm => vm.CurrencyProperty));
Which will satisfy this test:
///<summary>DomainModel maps to ViewModel</summary>
[Test]
public void ViewModelMapsToDomainModel()
{
var viewModel = new ViewModel {CurrencyProperty = "$19.95"};
var domainModel = new DomainModel();
Mapper.Map(viewModel, domainModel);
Assert.That(domainModel.CurrencyProperty, Is.EqualTo(19.95m));
}
... But I'm feeling that I shouldn't need to explicitly define which property it is being mapped from with FromMember after doing ResolveUsing since the properties have the same name - is there a better way to define this mapping? As I mentioned, there are a good number of properties with currency values that will need to be mapped in this fashion.
That being said - is there a way I could have these mappings automatically resolved by defining some rule globally? The ViewModel properties are already decorated with DataAnnotation attributes [DataType(DataType.Currency)] for validation, so I was hoping that I could define some rule that does:
if (destinationProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyFormatter>()
if (sourceProperty.PropertyInfo.Attributes.Has(DataType(DataType.Currency))
then Mapper.Use<CurrencyResolver>()
... so that I can minimize the amount of boilerplate setup for each of the object types.
I'm also interested in hearing of any alternate strategies for accomplishing custom formatting to-and-from the View.
From ASP.NET MVC in Action:
At first we might be tempted to pass
this simple object straight to the
view, but the DateTime? properties
[in the Model] will cause problems.
For instance, we need to choose a
formatting for them such as
ToShortDateString() or ToString(). The
view would be forced to do null
checking to keep the screen from
blowing up when the properties are
null. Views are difficult to unit
test, so we want to keep them as thin
as possible. Because the output of a
view is a string passed to the
response stream, we’ll only use
objects that are stringfriendly; that
is, objects that will never fail when
ToString() is called on them. The
ConferenceForm view model object is an
example of this. Notice in listing
4.14 that all of the properties are strings. We’ll have the dates properly
formatted before this view model
object is placed in view data. This
way, the view need not consider the
object, and it can format the
information properly.
Have you considered using an extension method to format money?
public static string ToMoney( this decimal source )
{
return string.Format( "{0:c}", source );
}
<%= Model.CurrencyProperty.ToMoney() %>
Since this is clearly a view-related (not model-related) issue, I'd try to keep it in the view if at all possible. This basically moves it to an extension method on decimal, but the usage is in the view. You could also do an HtmlHelper extension:
public static string FormatMoney( this HtmlHelper helper, decimal amount )
{
return string.Format( "{0:c}", amount );
}
<%= Html.FormatMoney( Model.CurrencyProperty ) %>
If you liked that style better. It is somewhat more View-related as it's an HtmlHelper extension.
Have you considered putting a DisplayFormat on your ViewModel? That is what I use and it's quick and simple.
ViewModel :
[DisplayFormat(DataFormatString = "{0:c}", ApplyFormatInEditMode = true)]
public decimal CurrencyProperty { get; set; }
View :
#Html.DisplayFor(m => m.CurrencyProperty)
A custom TypeConverter is what you're looking for:
Mapper.CreateMap<string, decimal>().ConvertUsing<MoneyToDecimalConverter>();
Then create the converter:
public class MoneyToDecimalConverter : TypeConverter<string, decimal>
{
protected override decimal ConvertCore(string source)
{
// magic here to convert from string to decimal
}
}

Resources