Add record to table with FK - asp.net

I have a table UserStoreName,
Columns are :
int Id
string UserNameId (as a FK of the table AspNetUsers (Column Id))
sring StoreName
I have a page AddStore, a very simple page where user just enter the store name into the StoreName Field.
I already know the UserNameId, i'm taking it from the User.
So when user populate the storeName field and click submit i just need to add a record to the table UserStoreName.
sounds easy.
when i click submit the AddStore function from the controller is giving me ModelState.IsValid = false.
reason for that is cause userNameId is a required field.
i want to populate that field in the AddStore
function but when we get there the modelState is already invalid because of a required field in userStoreNameId enter code here
Here is the AddStore in case it will help :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddStore(UserStoreName userStoreName)
{
userStoreName.UserNameId =
(_unitOfWork.ApplicationUser.GetAll().Where(q => q.UserName == User.Identity.Name).Select(q => q.Id)).FirstOrDefault();
userStoreName.UserName = User.Identity.Name;
userStoreName.IsAdminStore = false;
if (ModelState.IsValid)
{
_unitOfWork.UserStoreName.Add(userStoreName);
_unitOfWork.Save();
return RedirectToAction(nameof(Index));
}
return View(userStoreName);
}
Any idea what am i doing wrong? new to asp.net core mvc, its my first project.
Thanks :)
Thank you

If the UserNameId field is required, it must be supplied to pass model validation.
There are two ways around this. First, you could create a View Model, with just the fields you plan on actually submitting, and use it in place of the userStoreName variable. Then in the controller action, you can just instantiate a new UserStoreName object, and fill out the fields.
Alternatively, you could pass the UserNameId variable to the view, and populate the model client side using a hidden field, so it passes validation when returned to the controller. Hidden fields can potentially have their values edited client-side, however, so it may be worth checking the value again server side, especially if there are any security implications.

Foreign keys can be nullable so just make sure the UserNameId field is not marked with the "[Required]" Data Annotation in your model.
You'll also need to make sure that the column is nullable on the UserStoreName table to match the model otherwise it'll cause problems if your model is different from its underlying table.
Just a small suggestion also, I wouldn't foreign key on strings, I would change your model foreign key to an int, and make sure that the column in the table it's related to is also an int. It's a lot safer to do so, especially if you're dealing with IDENTITY columns.
If there is anything wrong with the reference, an exception will throw when the code tries to save your change, usually because the value it has in the FK reference cannot be found in the related table.

Related

Default value for ViewModel

I'm making a simple Search page in MVC with some filters in it. The filters are represented by properties in my ViewModel. My ViewModel is binded to a GET form in the cshtml so my filter will appears in the querystrings and the user will be able to bookmark his search.
What I want to do is to assign a default value to some of my filters.
My (simplified) ViewModel :
public class SearchViewModel
{
//Filter I want to set a default value to
public OrganizationType? OrganizationType {get; set;}
//Results of the search
public IEnumerable<ItemViewModel> Items {get; set;}
}
I'd like to set a default value for OrganizationType. I can't simply set it in the constructor of SearchViewModel because it depends on the current user :
public void InitViewModel(SearchViewModel vm)
{
vm.OrganizationType = _someLogic.GetDefaultValue(_currentUser);
}
First solution was simply to check if OrganizationType is null, then assign a default value :
public ActionResult Search(SearchViewModel vm)
{
if(vm.OrganizationType == null)
vm.OrganizationType = _someLogic.GetDefaultValue(_currentUser);
return View(vm);
}
But this solution doesn't work as a null value corresponds to an empty filter and it's a choice that the user can make. So I can't override it.
The second solution I tried was to specify that the default value of the controller should be null in the Search action :
public ActionResult Search(SearchViewModel vm = null)
{
if (vm == null)
{
vm = new SearchViewModel();
InitViewModel(vm);
}
...
return View(vm);
}
But in practice, the variable vm is never null, so the default values are never setted.
I also tried having two Action, one wihout a ViewModel where I instanciate a new ViewModel with the default values and then call the second action :
public ActionResult Search()
{
var vm = new SearchViewModel();
InitViewModel(vm);
//Simply call the second action with the initizalied ViewModel
return Search(vm);
}
public ActionResult Search(SearchViewModel vm)
{
...
return View(vm);
}
But it doesn't work because there is now an ambiguity between the two action, and asp.net doesn't know which one to choose.
So in summary, I'd like to find a way to set a default value for a ViewModel, without setting it in the constructor and overriding user choices.
Another way to say it, how can I distinguish an "empty" ViewModel from one where some values are binded from the form.
Any idea ?
Ok I think I found a solution to my own problem...
I can use the ModelState property of the controler to check it the ViewModel is empty or was binded from the form :
public ActionResult Search(SearchViewModel vm = null)
{
if (ModelState.Count == 0)
{
InitViewModel(vm);
}
...
return View(vm);
}
So if ModelState.Count equals to 0 it means that user didn't change any filters. So the form is empty and we can bind our default values. As soon as the user will change one of the filters or submit the request, the ModelState.Count will be greater than 0 so we shouldn't set the default value. Otherwise we would override an user choice.
The logic of what you're doing is a little iffy. Generally speaking, if a value is nullable then null is the default value. However, it seems that you're trying to make a distinction here between whether the value is null because it's not set or null because the user explicitly set it to null. This type of semantic variance is usually a bad idea. If null has a meaning, then it should always carry that meaning. Otherwise, your code becomes more confusing and bugs are generally introduced as a result.
That said, you can't count on ModelState having no items. I've honestly never played around with ModelState enough in scenarios where there's not post data, but it's possible there's some scenario where there's no post data and yet ModelState may have items. Even if there isn't, this is an implementation detail. What if Microsoft does an update that adds items to ModelState in situations where it previously had none. Then, your code breaks with no obvious reason why.
The only thing you can really count on here is whether the request method is GET or POST. In the GET version of your action, you can reasonably assume that the user has made no modifications. Therefore, in this scenario, you can simply set the value to whatever you like without concern.
In the POST version of your action, the user has made some sort of modification. However, at this point, there is no way to distinguish any more whether the value is null because it is or because the user explicitly wanted it to be. Therefore, you must respect the value as-is.

random EntityValidationErrors (The field is required)

I've been trying to update an Entity using the following code:
var db = new MyContext();
var data = db.Tableau.Find(Id);
if (data != null)
{
data.Name = model.Name;
data.EmbedCode = model.EmbedCode;
db.SaveChanges();
}
The problem is that my Tableaus table has a Parent field (FK not null to a DataTree table). Sometimes when I save the changes to this edited record, I get an error saying that "The Parent field is required". But I am not editing the Parent field. The parent field should be intact and existent, since I am only altering the Name and EmbedCode fields.
How to proceed? Thanks in advance.
That is because you are allowing null values in ParentId column in your Tableaus table, but in your Tableau entity you have ParentId as non-nullable property( which it means the relationship is required), and when you load a Tableau instance from your DB, EF expects that you set that property too. Try changing that property to nullable:
public int? ParentId {get;set;}
If you configure your relationship using Fluent Api it would be:
modelBuilder.Entity<Tableau>()
.HasOptional(t=>t.Parent)
.WithMany(dt=>dt.Tablous)// if you don't have a collection nav. property in your DataTree entity, you can call this method without parameter
.HasForeignKey(t=>t.ParentId);
Update 1
If you want ParentId property as Required in your Tableau entity, you need to make sure that you have a valid value in that column in your DB per each row. With a "valid value" I mean it should be different of the default value and it should exist as PK in your DataTree table.
Update 2:
One way to load a related entity as part of your query is using Include extension method:
var data = db.Tableau.Include(t=>t.Parent).FirstOrDefault(t=>t.Id==Id);

Update a model with nested collection, ASP.NET MVC

I'd appreciate any advice.
I've been searching for the right approach of modifying a collection, but not sure which is the best one.
I've an entity with nested collection:
public class Customer
{
//customer properties
ICollection <Address> Addresses{get; set;}
}
My Edit view for Customer includes the Addresses as well, i.e. user adds Addresses dynamically, and the whole collection is passed to the controller on form submittion.
In the controller I update the Customer as usually:
Context.Entry(customer).State = EntityState.Modified;
And also I have to update the collection of Address:
customer.Addresses.ToList()
.ForEach(p =>
Context.Entry(p).State = EntityState.Modified);
It works fine untill I add a new record to Addresses. Since it doesn't exist in DB, saving throws the error.
I know I could check if the entry exists in DB - modify, otherwise - add.
But there's a problem in Address entity. It's primary key, say ID, is Identity, i.e. auto-increments when inserted to DB.
So, initially in the collection new Address will have ID equal to 0. Then if I add one Address to the context, then before adding another one,I check if the second Address is in context, it will return true, because the first one is also with ID = 0.
Also, okay, I can drop the whole collection and add it again, but it can affect the performance.
So, I'd be so grateful for your advices.
You could iterate over the addresses and check the id. Based on the value it is possible to set the state of the entity entry to Added or Modified. In that case you don't have to recreate the collection.
foreach (var a in customer.Addresses)
{
model.Entry(a).State = a.ID == 0 ? EntityState.Added :EntityState.Modified;
}
You could use the same approach for the customer of course.

Adding additional fields in ApplicationUser class

I have a WebForms applicaiton using the new ASP.NET Identity. I've added a couple of additional fields in the class to allow for Email and a Boolean called IsOnLine.
I use migrations, to explicititly update the tables, and can see that my new fields are there. However, whenever I try to login now i get the following error:
Additional information: The 'IsOnLine' property on 'ApplicationUser' could not be set to a 'null' value. You must set this property to a non-null value of type 'System.Boolean'.
All the exmamples on the net relate to MVC, which i'm not using yet.
How can i fix this?
A bool cannot be null so that is what is set to in the database. If you want it to be null you will need to define it as a nullable bool using the ? operator.
public class ApplicationUser : IdentityUser
{
public bool? IsOnline { get; set; }
}
To update this information in the database take a look at this article on adding email confirmation to ASP.NET Identity. In this example there is a boolean flag IsConfirmed that is updated during the registration process, which will be similar to your IsOnline flag. Here is a snippet of the relevant code to update the database.
user.IsOnline = true;
DbSet<ApplicationUser> dbSet = context.Set<ApplicationUser>();
dbSet.Attach(user);
context.Entry(user).State = EntityState.Modified;
context.SaveChanges();
I prefer to avoid null values whenever possible, to determine if a property can accept nulls values it is better to think what that information represents in the domain problem. In this case i think it not make sense to have an undetermined value for IsOnline because the user is online or offline, not middle terms apply to this fact.
To resolve this problem with the database, taking advantage that you are using Code Migration , you can use the Seed method from Configuration class and assing the value(s) to your new fields that are not nullable.
Pseudo code:
Seed()
for each user
set user IsOnline to false

MVC - Insert data into multiple entities from same page

I have a single page which collects related data into a series of input fields.
When the user clicks submit I want to insert the data into two different database tables in one go. ie I want to get the primary key from the first insert, and then use it for the second insert operation.
I am hoping to do this all in one go, but I am not sure of the best way to do this with the Models/Entities in MVC.
Are you using LINQ or some other ORM? Typically these will support the ability to add a related entity and handle the foreign key relationships automatically. Typically, what I would do with LINQtoSQL is something like:
var modelA = new ModelA();
var modelB = new ModelB();
UpdateModel( modelA, new string[] { "aProp1", "aProp2", ... } );
UpdateModel( modelB, new string[] { "bProp1", "bProp2", ... } );
using (var context = new DBContext())
{
modelA.ModelB = modelB;
context.ModelA.InsertOnSubmit( modelA );
context.SubmitChanges();
}
This will automatically handle the insertion of modelA and modelB and make sure that the primary key for modelA is set properly in the foreign key for modelB.
If you are doing it by hand, you may have to do it in three steps inside a transaction, first inserting modelA, getting the key from that insert and updating modelB, then inserting modelB with a's data.
EDIT: I've shown this using UpdateModel but you could also do it with model binding with the parameters to the method. This can be a little tricky with multiple models, requiring you have have separate prefixes and use the bind attribute to specify which form parameters go with which model.

Resources