.net core MVC TryUpdateModelAsync passed expression of expression node type 'NewArrayInit' is invalid - asp.net-core-mvc

when I use TryUpdateModelAsync method to update Model I give this error, any one have an idea about this
The passed expression of expression node type 'NewArrayInit' is invalid. Only simple member access expressions for model properties are supported.
Code for this issue is as below.
[HttpPost,ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditLocaton([ModelBinder(typeof(EncryptDataBinder))]int id, IFormCollection formCollection)
{
ModelState.Clear();
LocationModel location = new LocationModel();
try
{
await TryUpdateModelAsync<LocationModel>(location, "", p => new object[] { p.ID, p.Name, p.Code, p.RowVersion });
code for the Location Model
public class LocationModel : BaseEntity
{
[Required]
[StringLength(100)]
[Display(Name = "Location Name")]
public string Name { get; set; }
[Required]
[StringLength(20)]
public string Code { get; set; }
[NotMapped]
public string enID { get; set; }
}
Please help for this issue.

Here's a sample for TryUpdateModelAsync.
var studentToUpdate = await _context.Students.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
try
...
It updates the studentToUpdate using data provided in the incoming request.
So I'm afraid you can try await TryUpdateModelAsync<LocationModel>(location, "", p => p.enID, p => p.Name, p => p.Code);. In your code snippet, I don't find RowVersion in your LocationModel, not sure about it.

Related

MudBlazor MudAutocomplete - how to show 'name's in the list, but bind an Id?

My model looks like this
public partial class EditModel
{
public int Id { get; set; }
...
public string Item { get; set; }
}
My SearchItems method header looks like this
protected async Task<IEnumerable<ListItem>> SearchItems(string value)
which returns 'list' of these
public partial class ListItem
{
public string Id { get; set; }
public string Name { get; set; }
}
How do I get my MudAutocomplete to show the Name, yet return/bind the Id?
<MudAutocomplete T="ListItem" Label="Select item" #bind-Value="EditModel.Item"
Clearable="true"
MinCharacters="4" SearchFunc="#SearchItems"
ToStringFunc="#(i => i==null ? null : $"{i.Id} [{i.Name}]")"
SelectValueOnTab="true"/>
on the #bind-Value, Visual studio shows this error
...cannot convert from 'string' to 'EditModel.Item'
This is how I solved it for now...
My SearchItems method now just returns a list of string
protected async Task<IEnumerable<string>> SearchItems(string value)
I've put this attribute in the MudAutocomplete
ToStringFunc="#(i => ItemDisplay(i))"
This is my ItemDisplay method
private string ItemDisplay(string itemId)
{
var item = ListItems.FirstOrDefault(i => i.Id == itemId);
return item == null ? "!Not Found!" : $"{item.Id} [{item.Name}]";
}
I've had to add this to my ComponentBase, to 'cache' all the ListItems for use in ItemDisplay() method:
public List<ListItem> ListItems { get; set; } = new();
In OnInitializedAsync()
ListItems = await MyService.GetItemsAsync();
I've set up my GetItemsAsync() to use IMemoryCache (Microsoft.Extensions.Caching.Memory), but I still don't like this approach. I find it difficult to believe that this component does not support the feature.
Maybe the component is updated but I was able to achieve this by using the following approach which I think is good.
The model you want to use
record State(Guid Id, string Name);
The binding value
private State value1;
The search function returns IEnumerable<State>
private async Task<IEnumerable<State>> Filter(string value)
{
// Filtering logic
}
Finally, I am using ToStringFunc to define how values are displayed in the drop-down list
<MudAutocomplete T="State" ToStringFunc="#(state => state.Name)" Label="US States" #bind-Value="value1" SearchFunc="#Filter" Variant="Variant.Outlined"/>

Correct interpretation of SQL request by EF Core

I have a certain table in the database that stores the following objects:
public partial class Invoice
{
public string DocumentNumber { get; set; }
public DateTime? DocumentDate { get; set; }
public string DocumentReference { get; set; }
public string SerialNumber { get; set; }
public string ProductCode { get; set; }
public string Description { get; set; }
public string Certificate { get; set; }
public string Language { get; set; }
public string Email { get; set; }
}
I also have a query that returns me the number of specific elements:
SELECT Count(*)
FROM (
SELECT DocumentNumber,DocumentDate,DocumentReference
FROM vInvoiceSwivelInfoWeb
WHERE Email = 'someemail#gmail.com' AND Language = 'FR'
GROUP BY DocumentNumber,DocumentDate,DocumentReference
) AS T
The answer looks something like this:
How to use EF to make such a request and get a numerical answer?
I tried like this:
_context.Database.ExecuteSqlRawAsync($"..some SQL query..")
but I do not get the expected result.
UPD: Having received the answer about the impossibility of fulfilling this request through EF, the following question reasonably arose: Is it possible to make this request using LINQ?
You can Leverage ADO.NET via the Context.Database property.
Unfortunately, there is no way to get the count from the database using EF Core execute methods if you have a custom query that is not related to your entities.
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "SELECT Count(*) From Table1";
context.Database.OpenConnection();
using (var result = command.ExecuteReader())
{
// do something with result
}
}
for Updated question
var count = from a in _context.vInvoiceSwivelInfoWeb
where a.Email == "someemail#gmail.com" && a.Language == "FR"
group new { a.DocumentNumber , a.DocumentReference , a.DocumentDate } by a into g
select g.Count()
also, it's important to know which version of EF-Core are you using:
currently, if you are using EF-Core 3 group-by doesn't translate to SQL command so you have to do it on client-side:
check this link :
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client
for EF-Core 3.0 - 3.1.1
var count = _context.vInvoiceSwivelInfoWeb
.Where(a => a.Email == "someemail#gmail.com" && a.Language == "FR" ).ToList()
.GroupBy(a => new { a.DocumentNumber ,a.DocumentDate, a.DocumentReference }).Count();

Trying to Extend ProDinner Chef class with collection of Phone Numbers

I am trying to extend ProDinner by adding phone numbers to Chef.
ChefInput view model:
public class ChefInput :Input
{
public string Name { get; set; }
public ChefInput()
{
PhoneNumberInputs = new List<PhoneNumberInput>(){
new PhoneNumberInput()
};}
public IList<PhoneNumberInput> PhoneNumberInputs { get; set; }
}
PhoneInput view model:
public class PhoneNumberInput :Input
{
public string Number { get; set; }
public PhoneType PhoneType { get; set; } <-- an enum in Core project
}
Chef Create.cshtml file:
#using (Html.BeginForm())
{
#Html.TextBoxFor(o => o.Name)
#Html.EditorFor(o => o.PhoneNumberInputs)
}
PhoneNumberInput.cshtml in EditorTemplate folder:
#using (Html.BeginCollectionItem("PhoneNumberInputs"))
{
#Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(typeof(PreDefPhoneType))))
#Html.TextBoxFor(m => m.Number)
}
When debugging and I stop it at Create in Crudere file, the Phone collection is null.
Anyone have any ideas?
Thanks in Advance.
Joe,
You don't show your controller logic but I've got a feeling you're getting null because you're not populating the PhoneNumberInputs ViewModel. From what I can see, all you're doing is newing up the list in the model. Ensure that you fill this 'list' in your controller from the database (with the appropriate values) and i'm certain all will work as planned.
[edit] - in answer to comment. don't know what the prodinner controllers etc look like but something alsong these lines:
public ActionResult Edit(int id)
{
var viewModel = new ChefInput();
viewModel.ChefInput = _context.GetById<ChefModel>(id);
viewModel.PhoneNumberInputs = _context.All<PhoneNumberInput>();
return View(viewModel);
}
as i said, not sure of the prodinner setup, but this is what i meant.

MVC3 EditorFor dynamic property (or workaround required)

I am building a system which asks questions and receives answers to them. Each question can have an aswer of its own type. Let's limit it to String and DateTime for now. In Domain, question is represented the following way:
public class Question
{
public int Id
{
get;
set;
}
public string Caption
{
get;
set;
}
public AnswerType
{
get;
set;
}
}
, where AnswerType is
enum AnswerType
{
String,
DateTime
}
Please note that actually I have much more answer types.
I came up with an idea of creating a MVC model, deriving from Question and adding Answer property to it. So it has to be something like this:
public class QuestionWithAnswer<TAnswer> : Question
{
public TAnswer Answer
{
get;
set;
}
}
And here start the problems. I want to have a generic view to draw any question, so it needs to be something like that:
#model QuestionWithAnswer<dynamic>
<span>#Model.Caption</span>
#Html.EditorFor(m => m.Answer)
For String I want to have simple input here, for DateTime I am going to define my own view. I can pass the concrete model from the controller. But the problem is that on the rendering stage, naturally, it cannot determine the type of Answer, especially if it is initially null (default for String), so EditorFor draws nothing for String and inputs for all properties in DateTime.
I do understand the nature of the problem, but is there any elegant workaround? Or I have to implement my own logic for selecting editor view name basing on control type (big ugly switch)?
Personally I don't like this:
enum AnswerType
{
String,
DateTime
}
I prefer using .NET type system. Let me suggest you an alternative design. As always we start by defining out view models:
public abstract class AnswerViewModel
{
public string Type
{
get { return GetType().FullName; }
}
}
public class StringAnswer : AnswerViewModel
{
[Required]
public string Value { get; set; }
}
public class DateAnswer : AnswerViewModel
{
[Required]
public DateTime? Value { get; set; }
}
public class QuestionViewModel
{
public int Id { get; set; }
public string Caption { get; set; }
public AnswerViewModel Answer { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new QuestionViewModel
{
Id = 1,
Caption = "What is your favorite color?",
Answer = new StringAnswer()
},
new QuestionViewModel
{
Id = 1,
Caption = "What is your birth date?",
Answer = new DateAnswer()
},
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<QuestionViewModel> questions)
{
// process the answers. Thanks to our custom model binder
// (see below) here you will get the model properly populated
...
}
}
then the main Index.cshtml view:
#model QuestionViewModel[]
#using (Html.BeginForm())
{
<ul>
#for (int i = 0; i < Model.Length; i++)
{
#Html.HiddenFor(x => x[i].Answer.Type)
#Html.HiddenFor(x => x[i].Id)
<li>
#Html.DisplayFor(x => x[i].Caption)
#Html.EditorFor(x => x[i].Answer)
</li>
}
</ul>
<input type="submit" value="OK" />
}
and now we can have editor templates for our answers:
~/Views/Home/EditorTemplates/StringAnswer.cshtml:
#model StringAnswer
<div>It's a string answer</div>
#Html.EditorFor(x => x.Value)
#Html.ValidationMessageFor(x => x.Value)
~/Views/Home/EditorTemplates/DateAnswer.cshtml:
#model DateAnswer
<div>It's a date answer</div>
#Html.EditorFor(x => x.Value)
#Html.ValidationMessageFor(x => x.Value)
and the last piece is a custom model binder for our answers:
public class AnswerModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
var type = Type.GetType(typeValue.AttemptedValue, true);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
which will be registered in Application_Start:
ModelBinders.Binders.Add(typeof(AnswerViewModel), new AnswerModelBinder());
You can still use the Html.EditorFor(..), but specify a second parameter which is the name of the editor template. You have a property on the Question object that is the AnswerType, so you could do something like...
#Html.EditorFor(m => m.Answer, #Model.AnswerType)
The in your EditorTemplates folder just define a view for each of the AnswerTypes. ie "String", "DateTime", etc.
EDIT: As far as the Answer object being null for String, i would put a placeholder object there just so the model in you "String" editor template is not null.

Linq to NHibernate projection to anon. type results in mystifying cast error

I have an TaxWork entity which is persisted using NHibernate. This entity has the following properties (among others):
public virtual TaxWorkType Type { get; set; } //Kctc.TaxWorkType is an enumeration
public virtual TaxWorkStatus Status { get; set; } //Kctc.TaxWorkStatus is an enumeration
public virtual LegalWorkPriority Priority { get; set; } //Kctc.LegalWorkType is an enumeration
public virtual User Handler { get; set; } //Kctc.BusinessLayer.Entities.User is another entity
public virtual int? CaseNumber { get; set; }
I am using Linq to NHibernate to pull of a subset of the tax work objects as follows (taxWorkRepository.All obviously returns an IQueryable):
foreach (TaxWork taxWork in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated))
{
...
}
This works fine. I want to use projection in order to query only the columns that are required in this case. I am usnig the following code:
foreach (var taxWorkFragment in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated).Select(x => new { Type = x.Type, DateCreated = x.DateCreated, Handler = x.Handler, Status = x.Status, Priority = x.Priority }))
{
...
}
However, I'm getting the following error when trying to create the anonymous type:
Invalid cast from 'Kctc.TaxWorkStatus' to 'Kctc.BusinessLayer.Entities.User'.
Where on earth is it getting the idea that it should be casting a TaxWorkStatus to a User?
Any suggestions whatsoever what might be going wrong?
Try to make like this:
foreach (var taxWorkFragment in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated)
.Select(x => new TaxWork { Type = x.Type, DateCreated = x.DateCreated, Handler = x.Handler, Status = x.Status, Priority = x.Priority }))
{
...
}
It should help

Resources