Entity Framework is not providing me IDs when calling back - entity-framework

I'm using a ViewModel (RoleVM) with a collection of ViewModels (RolePermissionVM) for this particular edit view. The view displays the RoleVM fields, and a checkbox list of RolePermissionVM. Each row in the checkbox list has a hiddenFor for the ID of the RolePermission.
When I save the form, my controller correctly writes the data to the database, adding or updating records. However, I would like the user to remain on the page, so I call the View again, but trying to get an updated model so that I have the IDs for any newly created RolePermissionVM objects. I am not getting the new IDs into the HiddenFor fields.
Here's my class:
public class RolePermissionVM
{
public int? RolePermissionId { get; set; }
public int RoleId { get; set; }
public int PermissionId { get; set; }
public string PermissionName { get; set; }
public bool IsActive { get; set; }
}
My controller code:
private RoleVM GetRoleVm(int id)
{
var thisRoleVm = (from r in db.Role
where r.RoleId == id
select new RoleVM
{
RoleId = r.RoleId,
RoleName = r.RoleName,
RoleDescription = r.RoleDescription,
OwnerId = r.OwnerId,
IsActive = r.IsActive
}).FirstOrDefault();
thisRoleVm.RolePermission = (from p in db.Permission
join rPerm in
(from rp in db.RolePermission
where rp.RoleId == id
select rp)
on p.PermissionId equals rPerm.PermissionId into pp
from rps in pp.DefaultIfEmpty()
select new RolePermissionVM
{
RolePermissionId = (int?)rps.RolePermissionId,
RoleId = id,
PermissionId = p.PermissionId,
PermissionName = p.PermissionName,
IsActive = (rps.IsActive == null ? false : rps.IsActive)
})
.OrderBy(p => p.PermissionName).ToList();
return thisRoleVm;
}
[HttpPost, ActionName("_roleedit")]
[ValidateAntiForgeryToken]
public ActionResult _RoleEdit(RoleVM editedRole)
{
//...
if (ModelState.IsValid)
{
var dbRole = db.Role.Find(editedRole.RoleId);
dbRole.RoleName = editedRole.RoleName;
dbRole.RoleDescription = editedRole.RoleDescription;
dbRole.OwnerId = editedRole.OwnerId;
foreach (var thisPerm in editedRole.RolePermission) // RolePermission here is the ViewModel, not the actual model
{
if (thisPerm.RolePermissionId != null && thisPerm.RolePermissionId > 0)
{
// We have a record for this, let's just update it
var thisRolePerm =
dbRole.RolePermission.FirstOrDefault(rp => rp.RolePermissionId == thisPerm.RolePermissionId);
thisRolePerm.IsActive = thisPerm.IsActive;
db.Entry(thisRolePerm).State = EntityState.Modified;
}
else
{
if (thisPerm.IsActive)
{
// New and active, so we add it
dbRole.RolePermission.Add(new RolePermission
{
RoleId = editedRole.RoleId,
PermissionId = thisPerm.PermissionId,
IsActive = true
});
}
}
}
db.Entry(dbRole).State = EntityState.Modified;
db.SaveChanges(User.ProfileId);
var newEditedRole = GetRoleVm(editedRole.RoleId); // We don't get the new IDs here, but I would like to
newEditedRole.ResponseMessage = "Saved Successfully";
return View(newEditedRole); // This should have the new RolePermissionId values, but it doesn't.
}
editedRole.ResponseMessage = "Error Saving";
return View(editedRole);
}
The partial view used for each row of the CheckBox list:
#using PublicationSystem.Tools
#model PublicationSystem.Areas.Admin.Models.RolePermissionVM
<li class="editorRow ui-state-default removable-row">
#using (Html.BeginCollectionItem("RolePermission"))
{
<div class="row">
#Html.HiddenFor(model => model.RolePermissionId)
#Html.HiddenFor(model => model.RoleId)
#Html.HiddenFor(model => model.PermissionId)
#Html.HiddenFor(model => model.PermissionName)
<div class="col-md-7">
#Html.DisplayFor(model => model.PermissionName, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-md-3">
#Html.CheckBoxFor(model => model.IsActive, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
}
</li>
var newEditedRole = GetRoleVm(editedRole.RoleId); should be calling the database to get the updated IDs, but it does not. I think the issue is the DBContext is using a cached copy.
So, why do the new database generated IDs not get pulled back? How can I fix that? Is there a more efficient way to do this?

You have to call return RedirectToAction("ViewName") instead of return View(newEditedRole);
Another way is removing the value from the ModelState, so it will be updated on view:
ModelState.Remove("RoleId")
model.RoleId = dbRole.RoleId
I think return RedirectToAction("ViewName") is better/more reliable choice.

Related

Filters not working in my repository in ASP.NET Core

I have these parameters in a class:
public class UserParams
{
public string Gender {get; set;}
public int MinAge {get; set;} = 1;
public int MaxAge {get; set;} = 19;
}
The query is done in the repository as shown below. First is to query for the child sex or gender and the second is to query for the child sex or gender
var query = _context.Children.AsQueryable();
query = query.Where(c => c.Sex == userParams.Gender);
var minchildDob = DateTime.Today.AddYears(-userParams.MaxAge - 1);
var maxchildDob = DateTime.Today.AddYears(-userParams.MinAge);
query = query.Where(u => u.DateOfBirth >= minchildDob && u.DateOfBirth <= maxchildDob);
return await PagedList<Child>.CreateAsync(query.AsNoTracking(), userParams.PageNumber, userParams.PageSize);
The gender filter returns empty array of children and the minchildDob and maxchildDob too not working
if (!string.IsNullOrEmpty(temp.Gender))
{
all = all.Where(u => new[] { "men", "women" }.Contains(u.sex));
//all = all.Where(t => t.sex == temp.Gender);
}
=======================Update=======================
var temp = new UserParams();
temp.Gender = "men";
var minchildDob = DateTime.Today.AddYears(-temp.MaxAge - 1);
var maxchildDob = DateTime.Today.AddYears(-temp.MinAge);
IEnumerable<Table> all = from m in _context.data
select m;
_logger.LogError("all data");
foreach (var item in all)
{
_logger.LogError(item.name);
}
_logger.LogError("============================================");
if (!string.IsNullOrEmpty(temp.Gender)) {
all = all.Where(t => t.sex == temp.Gender);
}
_logger.LogError("filter gender");
foreach (var item in all) {
_logger.LogError(item.name);
}
_logger.LogError("============================================");
if (temp.MaxAge > 0) {
all = all.Where(t => t.birthday >= minchildDob && t.birthday <= maxchildDob);
}
_logger.LogError("filter age");
foreach (var item in all)
{
_logger.LogError(item.name);
}
_logger.LogError("============================================");

xamarin forms picker binding selected value to NgModel like angular

I have a dynamic model of the user entity with JSON type. I need to show a form to select the value foreach property with dropdownlist then insert to the db. Like a entity of user has property sex and age(property can be edit by endpage by customer) .
the example result like below:
[
{
"bindValue": null,
"title": "sex",
"code":"sex",
"property": {
"option": [
"male",
"female"
]
}
},
{
"bindValue": null,
"code":"grade",
"property": {
"option": [
"2",
"3"
]
}
}
]
with angular ionic project i can code with this:
<ng-container *ngFor="let x of dynamicdroplist">
<ion-item>
<ion-label fixed>{{x.title}}</ion-label>
<ion-select [(ngModel)]="x.bindValue">
<ion-select-option *ngFor="let y of x.property.option" value="{{y}}">{{y}}</ion-select-option>
</ion-select>
</ion-item>
</ng-container>
when user change the select value, it'll bind "ngModel" to x.bindvalue, i can filter the code and the binvalue data to send to api.
But with xamarin forms i don't know how to bind select value to bindvalue
foreach(var item in field){
StackLayout layout = new StackLayout().LoadFromXaml("<StackLayout></StackLayout>");
layout.Children.Add(new Label().LoadFromXaml("<Label Text=\"" + item.Title + "\"></Label>"));
var picker = new Picker { Title = item.Title };
foreach (var e in item.Property.option)
{
picker.Items.Add(e);
}
layout.Children.Add(picker);
_stackLayout.Children.Add(layout);
}
I want to know how can i code this with xamarin forms?
You can bind the Picker.SelectedItem to bindValue in TwoWay, then if user change the select value, the bindValue in the model will update:
picker.BindingContext = item;
picker.SetBinding(Picker.SelectedItemProperty, "bindValue",mode:BindingMode.TwoWay);
Here is the a sample code you can refer:
public partial class MainPage : ContentPage
{
StackLayout _stackLayout;
List<RootObject> field = new List<RootObject>();
public MainPage()
{
InitializeComponent();
_stackLayout = new StackLayout();
field.Add(new RootObject() { bindValue = "", title = "sex", code ="sex", property = new Property() { option = new List<string>() { "male","female"} } });
field.Add(new RootObject() { bindValue = "", title = "grade", code = "grade", property = new Property() { option = new List<string>() { "2", "3" } } });
foreach (RootObject item in field)
{
StackLayout layout = new StackLayout();
layout.Children.Add(new Label() { Text = item.title });
var picker = new Picker { Title = item.title };
//set the BindingContext here
picker.BindingContext = item;
foreach (string e in item.property.option)
{
picker.Items.Add(e);
//bind the value here
picker.SetBinding(Picker.SelectedItemProperty, "bindValue",mode:BindingMode.TwoWay);
}
layout.Children.Add(picker);
picker.SelectedIndexChanged += Picker_SelectedIndexChanged;
_stackLayout.Children.Add(layout);
}
Content = _stackLayout;
}
private void Picker_SelectedIndexChanged(object sender, EventArgs e)
{
//check the value change in field
RootObject r1 = field[0];
Console.WriteLine(r1.bindValue);
RootObject r2 = field[1];
Console.WriteLine(r2.bindValue);
}
}
public class Property
{
public List<string> option { get; set; }
}
public class RootObject
{
public string bindValue { get; set; }
public string title { get; set; }
public string code { get; set; }
public Property property { get; set; }
}
Refer: data-binding
If you need the value programatically, just access it using the picker variable you declared;
picker.SelectedItem
Otherwise if you want to bind it to the UI, you can;
myLabel.SetBinding(Label.TextProperty, new Binding("SelectedItem", source: picker));

Dynamic list using array from anthor list

My application is ASP.NET MVC 5 / SQL Server.
I am trying to select specific columns from a list based on an array:
First list has 200 columns: Age, Gender, .....
var list1 = _reportRepository.ShowMasteView().ToList();
Second list has 20 columns: Age, Gender, ......
From the view I select the items to be displayed:
string[] lits2 = showColumn.Where(c => c.Value == true).Select(c=> c.Key).ToArray();
I get
To get these two specific columns, I tried
var nList = list1.Select(t2 => lits2.Any(t1 => t2.Contains(t1)));
I get an error
Can not resolve symbol "Contains"
I was able to do it using the following
var keys = "Age,Gender";
var connection =
ConfigurationManager.ConnectionStrings["DALEntities"].ConnectionString;
using (var dataAdapter = new SqlDataAdapter("SELECT " + keys
+ " from dbo.vw_MasterView", connection))
{
var dataTable = new DataTable();
dataAdapter.Fill(dataTable);
dataAdapter.FillSchema(dataTable, SchemaType.Mapped);
return dataTable;
}
Is there a better way in linq?
From my understand it appears you are trying to extract/select a dynamic object that only has the desired properties/columns.
This can be achieved by building a dynamic expression/function to apply to the Select
The following builds an expression based on the model type and the provided properties
static class DynamicExtensions {
public static IQueryable<dynamic> SelectDynamic<TModel>(this IQueryable<TModel> query, ISet<string> propertyNames) {
var selector = query.BuildSelectorFor(propertyNames);
return query.Select(selector);
}
static Expression<Func<TModel, dynamic>> BuildSelectorFor<TModel>(this IQueryable<TModel> query, ISet<string> propertyNames) {
var modelType = typeof(TModel);
var properties = modelType.GetProperties().Where(p => propertyNames.Contains(p.Name));
// Manually build the expression tree for
// the lambda expression v => new { PropertyName = v.PropertyName, ... }
// (TModel v) =>
var parameter = Expression.Parameter(modelType, "v");
// v.PropertyName
var members = properties.Select(p => Expression.PropertyOrField(parameter, p.Name));
var addMethod = typeof(IDictionary<string, object>).GetMethod(
"Add", new Type[] { typeof(string), typeof(object) });
// { { "PropertyName", v.PropertyName}, ... }
var elementInits = members.Select(m =>
Expression.ElementInit(addMethod, Expression.Constant(m.Member.Name), Expression.Convert(m, typeof(object))));
// new ExpandoObject()
var newExpando = Expression.New(typeof(ExpandoObject));
// new ExpandoObject() { { "PropertyName", v.PropertyName}, ... }
var expando = Expression.ListInit(newExpando, elementInits);
// (TModel v) => new ExpandoObject() { { "PropertyName", v.PropertyName}, ... }
var lambdaExpression = Expression.Lambda<Func<TModel, dynamic>>(expando, parameter);
return lambdaExpression;
}
}
This takes advantage of ExpandoObject whose members can be dynamically added and removed at run time.
The following test was used as an example of how the above function is invoked.
[TestMethod]
public void DynamicList() {
var list1 = new List<Person>
{
new Person{ Gender = "Male", Age = 10, FirstName = "Nama1", SampleNumber = 12},
new Person{ Gender = "Male", Age = 12, FirstName = "Nama2", SampleNumber = 13},
new Person{ Gender = "Female", Age = 13, FirstName = "Nama3", SampleNumber = 14},
new Person{ Gender = "Male", Age = 14, FirstName = "Nama4", SampleNumber = 15},
};
var keys = new string[] { "Age", "Gender", };
var nList = list1.AsQueryable().SelectDynamic(new HashSet<string>(keys));
foreach (IDictionary<string, object> row in nList) {
var msg = $"{{ {keys[0]} = {row[keys[0]]}, {keys[1]} = {row[keys[1]]} }}";
Debug.WriteLine(msg);
}
}
and produces the following output
{ Age = 10, Gender = Male }
{ Age = 12, Gender = Male }
{ Age = 13, Gender = Female }
{ Age = 14, Gender = Male }
The dynamic objects can be used in the View and it is a simple matter of calling the desired members.
For example suppose you have a model as follows
public class MyViewModel {
public string MyProperty { get; set; }
public string[] Keys { get; set; }
public List<dynamic> MyDynamicProperty { get; set; }
}
that was populated with data and given to the view
var list1 = _reportRepository.ShowMasteView();
var keys = new string[] { "Age", "Gender", };
var nList = list1.AsQueryable().SelectDynamic(new HashSet<string>(keys));
var viewModel = new MyViewModel {
MyProperty = "Hello World",
MyDynamicProperty = nList.ToList(),
Keys = keys
};
return View(viewModel);
Then in the view you can use the model as desired, casting to get access to members in the expando object.
#model MyViewModel
...
<h2>#Model.MyProperty</h2>
<table>
<tr>
#foreach(string key in Model.Keys) {
<th>#key</th>
}
</tr>
#foreach (IDictionary<string, object> row in Model.MyDynamicProperty) {
<tr>
#foreach(string key in Model.Keys) {
<td>#row[#key]</td>
}
</tr>
}
</table>
I think you just need to use Contains on your list2.
var nList = list1.Where(t => lits2.Contains(t1));
Contains is a method for Lists. The code you had was trying to use it on a string.
If you have two list of a person's class
public class Person
{
public int id { get; set; }
public string name { get; set; }
}
If the lists are as below:
var list1 = new List<Person>
{
new Person{ id = 1, name = "Nama1"},
new Person{ id = 2, name = "Nama2"},
new Person{ id = 3, name = "Nama3"},
new Person{ id = 4, name = "Nama4"},
};
var list2 = new List<Person>
{
new Person{ id = 1, name = "Nama1"},
new Person{ id = 2, name = "Nama2"},
};
You can filter in the following ways
var keys = list2.Select(x => x.id).ToList();
var filter1= list1.Where(x => keys.Contains(x.id)).ToList();
var filter2= list1.Where(x => keys.Contains(x.id)).Select(x => new { x.name }).ToList();
var filter3= list1.Select(x => new
{
id = x.id,
name = x.name,
check = keys.Contains(x.id)
}).Where(x => x.check).ToList();
If you have array of string
you can use below code
array string same
var lis1 = new string[] {"name1", "name2","name3" };
var lis2 = new string[] { "name1" };
You can filter array of string in the following ways
var items1= lis1.Where(x=>lis2.Contains(x)).ToList();
var items= lis1.Select(x=> new { x, check= lis2.Contains(x) }).Where(x=>x.check == true).ToList();

Updating related Phone entities with custom tag helper

As my application currently sits, each AppUser may (or may not) have 3 phone numbers (UserPhones). One of each type (Mobile, Home, Other).
The following Tag Helper works great (Thanks #itminus).
Calling code from Razor Page:
<user-phones phones="#Model.UserPhones"
asp-for="#Model.UserPhones"
prop-name-to-edit="PhoneNumber"
types-to-edit="new EnumPhoneType[] { EnumPhoneType.Mobile,
EnumPhoneType.Other }" />
Code:
public class UserPhonesTagHelper : TagHelper
{
private readonly IHtmlGenerator _htmlGenerator;
private const string ForAttributeName = "asp-for";
[HtmlAttributeName("expression-filter")]
public Func<string, string> ExpressionFilter { get; set; } = e => e;
public List<UserPhones> Phones { get; set; }
public EnumPhoneType[] TypesToEdit { get; set; }
public string PropNameToEdit { get; set; }
[ViewContext]
public ViewContext ViewContext { set; get; }
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
public UserPhonesTagHelper(IHtmlGenerator htmlGenerator)
{
_htmlGenerator = htmlGenerator;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = null; //DO NOT WANT AN OUTTER HTML ELEMENT
for (int i = 0; i < Phones.Count(); i++)
{
var props = typeof(UserPhones).GetProperties();
var pType = props.Single(z => z.Name == "Type");
var pTypeVal = pType.GetValue(Phones[i]);
EnumPhoneType eType = (EnumPhoneType) Enum.Parse(typeof(EnumPhoneType), pTypeVal.ToString());
string lVal = null;
switch (eType)
{
case EnumPhoneType.Home:
lVal = "Home Phone";
break;
case EnumPhoneType.Mobile:
lVal = "Mobile Phone";
break;
case EnumPhoneType.Other:
lVal = "Other Phone";
break;
default:
break;
}
//LOOP ALL PROPERTIES
foreach (var pi in props)
{
var v = pi.GetValue(Phones[i]);
var expression = this.ExpressionFilter(For.Name + $"[{i}].{pi.Name}");
var explorer = For.ModelExplorer.GetExplorerForExpression(typeof(IList<UserPhones>), o => v);
//IF REQUESTED TYPE AND PROPERTY SPECIFIED
if (pi.Name.NormalizeString() == PropNameToEdit.NormalizeString() && TypesToEdit.Contains(eType))
{
TagBuilder gridItem = new TagBuilder("div");
gridItem.Attributes.Add("class", "rvt-grid__item");
gridItem.InnerHtml.AppendHtml(BuildLabel(explorer, expression, lVal));
gridItem.InnerHtml.AppendHtml(BuildTextBox(explorer, expression, v.ToString()));
output.Content.AppendHtml(gridItem);
}
else //ADD HIDDEN FIELD SO BOUND PROPERLY
output.Content.AppendHtml(BuildHidden(explorer, expression, v.ToString()));
}
}
}
private TagBuilder BuildTextBox(ModelExplorer explorer, string expression, string v)
{
return _htmlGenerator.GenerateTextBox(ViewContext, explorer, expression, v, null, new { #class = "form-control" });
}
public TagBuilder BuildHidden(ModelExplorer explorer, string expression, string v)
{
return _htmlGenerator.GenerateHidden(ViewContext, explorer, expression, v, false, new { });
}
public TagBuilder BuildLabel(ModelExplorer explorer, string expression, string v)
{
return _htmlGenerator.GenerateLabel(ViewContext, explorer, expression, v, new { });
}
}
My Question:
Lets assume this AppUser only has one related Mobile phone number listed currently. So AppUser.UserPhones (count = 1 of type Mobile). So the code above, as-is, will only render an input for Mobile phone.
Since types-to-edit calls for both Mobile and Other, I want both inputs to be rendered to the screen. And IF the user adds a phone number to the Other input, then it would be saved to the related UserPhones entity on the Razor Pages OnPostAsync method. If the user does NOT provide a number for the "Other" input, then the related UserPhones record of type "Other" should NOT be created.
Can you help?
Thanks again!!!!
TagHelper
As my application currently sits, each AppUser may (or may not) have 3 phone numbers (UserPhones). One of each type (Mobile, Home, Other).
If I understand correctly, an AppUser might have 3 phone numbers and the count of each phone type for every user will be zero or one.
If that's the case, we can simply use PhoneType as an index, in other words, there's no need to use a custom index to iterate through the Phones property, and the ProcessAsync() method could be :
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = null; //DO NOT WANT AN OUTTER HTML ELEMENT
var props = typeof(UserPhones).GetProperties();
// display editable tags for phones
foreach (var pt in this.TypesToEdit) {
var phone = Phones.SingleOrDefault(p=>p.Type == pt);
var index = (int) pt;
foreach (var pi in props)
{
// if phone==null , then the pv should be null too
var pv = phone==null? null: pi.GetValue(phone);
var tag = GenerateFieldForProperty(pi.Name, pv, index, pt);
output.Content.AppendHtml(tag);
}
}
// generate hidden input tags for phones
var phones= Phones.Where(p => !this.TypesToEdit.Contains((p.Type)));
foreach (var p in phones) {
var index = (int)p.Type;
foreach (var pi in props) {
var pv = pi.GetValue(p);
var tag = GenerateFieldForProperty(pi.Name,pv,index,p.Type);
output.Content.AppendHtml(tag);
}
}
}
Here the GenerateFieldForProperty is a simply helper method to generate tag builder for particular property:
private TagBuilder GenerateFieldForProperty(string propName,object propValue,int index, EnumPhoneType eType )
{
// whether current UserPhone is editable (check the PhoneType)
var editable = TypesToEdit.Contains(eType);
var expression = this.ExpressionFilter(For.Name + $"[{index}].{propName}");
var explorer = For.ModelExplorer.GetExplorerForExpression(typeof(IList<UserPhones>), o => propValue);
//IF REQUESTED TYPE AND PROPERTY SPECIFIED
if (pi.Name.NormalizeString() == PropNameToEdit.NormalizeString() && editable)
{
TagBuilder gridItem = new TagBuilder("div");
gridItem.Attributes.Add("class", "rvt-grid__item");
var labelText = this.GetLabelTextByPhoneType(eType);
gridItem.InnerHtml.AppendHtml(BuildLabel(explorer, expression, labelText));
gridItem.InnerHtml.AppendHtml(BuildTextBox(explorer, expression, propValue?.ToString()));
return gridItem;
}
else //ADD HIDDEN FIELD SO BOUND PROPERLY
return BuildHidden(explorer, expression, propValue?.ToString());
}
private string GetLabelTextByPhoneType(EnumPhoneType eType) {
string lVal = null;
switch (eType)
{
case EnumPhoneType.Home:
lVal = "Home Phone";
break;
case EnumPhoneType.Mobile:
lVal = "Mobile Phone";
break;
case EnumPhoneType.Other:
lVal = "Other Phone";
break;
default:
break;
}
return lVal;
}
When posted to server, if someone doesn't input a phone number for the other PhoneType, the actual payload will be something like:
AppUser.UserPhones[0].UserPhoneId=....&AppUser.UserPhones[0].PhoneNumber=911&....
&AppUser.UserPhones[2].UserPhoneId=&AppUser.UserPhones[2].PhoneNumber=&AppUser.UserPhones[2].Type=&AppUser.UserPhones[2].AppUserId=&AppUser.UserPhones[2].AppUser=
&AppUser.UserPhones[1].UserPhoneId=...&AppUser.UserPhones[1].PhoneNumber=119&....
Since we use phone type as the index, we can conclude that the UserPhones[0] will be used as an Mobile phone and the UserPhones[2] will be treated as an Home phone.
page handler or action method
And the model binder on server side will create a empty string for each UserPhone.
To remove those empty inputs and prevent overposting attack, we could use Linq to filter UserPhones so that we can create or update UserPhone records without empty Phones:
var editables = new[] {
EnumPhoneType.Mobile,
EnumPhoneType.Other,
};
AppUser.UserPhones = AppUser.UserPhones
.Where(p => !string.IsNullOrEmpty(p.PhoneNumber)) // remove empty inputs
.Where(p => editables.Contains(p.Type) ) // remove not editable inputs
.ToList();
// now the `UserPhones` will be clean for later use
// ... create or update user phones as you like
Let's say you want to create phones :
public IActionResult OnPostCreate() {
var editables = new[] {
EnumPhoneType.Mobile,
EnumPhoneType.Other,
};
AppUser.UserPhones = AppUser.UserPhones
.Where(p => !string.IsNullOrEmpty(p.PhoneNumber))
.Where(p => editables.Contains(p.Type) )
.Select(p => { // construct relationship for inputs
p.AppUser = AppUser;
p.AppUserId = AppUser.Id;
return p;
})
.ToList();
this._dbContext.Set<UserPhones>().AddRange(AppUser.UserPhones);
this._dbContext.SaveChanges();
return Page();
}
Test Case :
<form method="post">
<div class="row">
<user-phones
phones="#Model.AppUser.UserPhones"
asp-for="#Model.AppUser.UserPhones"
prop-name-to-edit="PhoneNumber"
types-to-edit="new EnumPhoneType[] { EnumPhoneType.Mobile, EnumPhoneType.Other}"
>
</user-phones>
</div>
<button type="submit">submit</button>
</form>
User1 who has Mobile phone and Home phone number:
User2 who wants to create a new Mobile phone number :

Stuck with view (MVC) using Entity Framework code-first approach

I'm getting an error on View
The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[oneToOneRelationship.Student]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable'1[oneToOneRelationship.StudentDBContext]'.
While running the application, database is getting updated.
namespace oneToOneRelationship.Controllers
{
public class StudentController : Controller
{
StudentDBContext objContext;
public StudentController()
{
objContext = new StudentDBContext();
}
public ActionResult Index()
{
Student s = new Student
{
StudentId = 1,
StudentName = "Hitesh",
StudentAge = 24
};
StudentAccount sa = new StudentAccount {
StudentName = "Sports account",
StudentAmount = 300,
student = s
};
objContext.Students.Add(s);
objContext.StudentAccounts.Add(sa);
objContext.SaveChanges();
var result = from r in objContext.Students select r;
var data = objContext.Students.ToList();
return View(data);
}
}
}