An issue occurs when I want to save an object posted back from a form, but exclude a particular field from the resultant SQL statement. In this case I did not want to include the password field in a user edit form.
An edit HttpPost controller method contained this:
db.Users.Attach(user);
db.Entry(user).State = EntityState.Modified;
db.Entry(user).Property(x => x.Password).IsModified = false;
db.SaveChanges();
I assumed that the .IsModified statement would mean that the generated SQL would consist of an UPDATE statement without the password field and so not overwrite the existing password value with null in the database.
However, this was not the case and EF seems to work in a slightly different way. My user class and its associate metadata class did not specify that the password field was required and yet, when executing, there was a ‘Password field required’ DbEntityValidationException thrown when SaveChanges was invoked.
One solution would be to include the password field with its value as a hidden form field, which would suffice in most cases, but in this instance would not be a good idea as password, hashed or otherwise is rather sensitive data.
On further inspection, it seemed that, in this case where we are using a database first approach, the .edmx file contains a ‘not null’ directive on the Password field. When EF comes to execute the actual save, this is checked and the exception raised.
This was not entirely intuitive in my opinion but once it is known it can be worked around.
My solution was to provide a value for the excluded field and retain the .IsModified statement. This means the DbEntityValidationException isn’t thrown as EF sees the Password field containing a value (as prescribed in the .edmx file) but is not actually written to the database due to IsModified being false. E.g.
user.Password = "not required";
db.Users.Attach(user);
db.Entry(user).State = EntityState.Modified;
db.Entry(user).Property(x => x.Password).IsModified = false;
db.SaveChanges();
Is there a better solution to this as using a dummy value as above seems like a bit of a hack?
You haven't really explained why you don't want to save the password field, I'm assuming its just because you don't know what the value is and in your current code it is being set to NULL.
You could use a ViewModel to bind with your webpage, and only supply the fields that are required (so in your case, NOT the password field). When the user POSTs data back, you can load the user from the db, set only the fields you need to, then save the data back. The password field will be unchanged.
For example, assuming the User table is this:
ID
UserName
Password
FullName
You would have a view model class of
public class UserViewModel
{
public int ID { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
}
When the data is posted back to the server from the client:
using (MyEntity db = new MyEntity())
{
User u = db.Users.Find(userViewModel.ID);
u.UserName = userViewModel.UserName;
u.FullName = userViewModel.FullName;
db.SaveChanges();
}
GO with the stored procedure approach, where you dont need to worry about entity framework, call stored proc from entity framework and update your table inside sql statement without updating password field
Related
Say I have an User entity and it haves a Password property which is not nullable:
Map((x) => x.Password).Column("PASSWORD").Not.Nullable();
In the create action, I manually set the Password value as it is a generated hash. It never goes to the View.
In the update, I try to save it, but I don't have the Password value. I get this error for Password propery:
PropertyValueException: not-null property references a null or transient value
This is my Update method:
public bool Update(UserViewModel input)
{
if (!IsValid(input))
return false;
var user = Mapper.Map<User>(input);
this.UserRepository.Update(user); // <- this is a wrapper for NH's Session.Update()
return true;
}
How can I tell NHibernate to ignore a property in an update?
Note: This is not the same as this question.
Update:
Here is how I use it: The Password property never goes to any View. Even in the Login action I have a generic LoginViewModel, only for it's view. That property is only used in the login process and it could be updated in the Reset password feature, where a new password is generated and sent to the related user e-mail.
I see 2 possibilities to achieve that
Get the entity before Update and update explicitly
// use 'Get()' because it uses the NHibernate cache
// if you already loaded the entity, it won't query the db and read it from the cache
var user = this.UserRepository.Get(input.Id);
user.PropertyToUpdate = ...;
this.UserRepository.Update(user);
In addition to that, you can use Dynamic-Update. But this will only work with entities that are bound to the Session. NHibernate will then only update the changed properties and not all while you are updating a entity. Otherwise NHibernate can't know which properties has changed and will update all. DynamicUpdate should only work when you got the entity from NHibernate. The Entity is then bound to the Context and NHibernate can track changes.
If all your entities are auto mapped you can use a ClassConvention to set DynamicUpdate to all your entities (or just filter the ones you want):
public class ClassConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.DynamicUpdate();
}
}
As another option you can use a explicit mapping override:
public class UserOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.DynamicUpdate();
}
}
Use different classes for different behaviours
You can declare different classes for the same Entity. One class for User creation or password resetting that contains the password property. And one class for simple updates that don't need the password property. FluentNhibernate allows you to map different classes for the same table. But you need a little more effort in mapping or rather in AutoMappingOverrides.
In Entity Framework I can apply NotMapped attribute to a property which I do NOT want to create a column in a database table for. How to get the same effect for auto generated classes in DBML file?
I have a StoredProcedure that returns some additional fields.
I called SP like:
[global::System.Data.Linq.Mapping.FunctionAttribute(Name = "dbo.sp_GetSupplierArticles")]
public ISingleResult<SupplierArticle> GetSupplierArticles([global::System.Data.Linq.Mapping.ParameterAttribute(DbType = "BigInt")] long mainArticleId, [global::System.Data.Linq.Mapping.ParameterAttribute(DbType = "BigInt")] long? userId)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), mainArticleId, userId);
return ((ISingleResult<SupplierArticle>)(result.ReturnValue));
}
Necessary field I added into the separated partial class. Without any additional attributes it returns default value for my and applied [Column(IsDbGenerated = false)] in the separated partial class:
public partial class SupplierArticle
{
[Column(IsDbGenerated = false)]
public double Extra { get; set; }
}
So it works until I try to get SupplierArticle using another query (not my stored procedure):
db.LoadOptions = db.GenerateDataLoadOptions(entitiesToInclude);
var query =
from shoppingCartItem in db.ShoppingCartItems
where shoppingCartItem.UserId == userId
select shoppingCartItem;
return query.ToList();
My entity is loaded due to LoadOptions (passed in entitiesToInclude parameter).
In this query and another which try to load "poor" entity with properties that defined in .dbml file I get exception:
Invalid column name 'Extra' and the same message for each additional property.
What is the proper way to extend entity or how to avoid that exception?
UPD:
If I remove all attributes exception no longer occurs. But added properties are not initialized when SP returns a result.
I would suggest creating a complex type for that stored procedure. I would even go as far as creating complex types for all of your stored procedures as this is best practice. You can then add an extension method, or a method to your partial classes that will convert the complex type returned from the stored procedure to it's related entity, and vice versa. Another option would be to include a foreign key to your complex stored procedure type, and a navigation property pointing to the correct entity.
These are, of course, solutions to a problem that EF itself doesn't address. This is expected as EF is an ORM and is not concerned with what's not persisted.
A SQL View could be used if you wanted to. The View is composible and LINQ to SQL does not distinguish it from a Table. First rename the original table. Then make a View with the same name as the original table, while including the extra column with a default value. Let the Stored Procedure use the table with the new name. Of course any other SQL objects with references to the table need to be updated with the new table name. Now both LINQ to SQL and the SP will be happy.
I'm trying to develop my first ASP.net MVC project with Entity Framework. I wasn't using any view models, i was using same models for both in views and database transactions. Here is the thing, i have custom user table. In creation, i have 5 things in my User model: UserName, UserPassword, FullName, Branch and BranchId(Navigation to another table). But when i want to edit a user, i don't need the UserPassword field, because changing the password won't be possible for now. So i created a model same with the User model except the UserPassword field named UserEdit.
In create view i use my User model, in edit view i use the UserEdit model. In controller i'm using automapper and copy values from User to UserEdit and return that to view. It's working fine, problem is about updating.
I'm trying to update the user like this:
public bool Update(UserEdit userEdit) {
User user = Find(userEdit.UserUsername);
Mapper.CreateMap<UserEdit, User>();
user = (User)Mapper.Map(userEdit, user, typeof(UserEdit), typeof(User));
if (_modelState.IsValid)
{
_transactionManager.GetContext().Entry(user).State = System.Data.Entity.EntityState.Modified;
_transactionManager.CommitTransaction();
return true;
}
else
{
return false;
}
}
But it gives me this error:
Additional information: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
When i check the user i'm trying to update, i see Entity Framework related objects in it. So i'm thinking reason of this error is probably about branch table and those related objects in the user object. I really don't need those entity related objects in my user object. If i could only copy the properties in the model, it would be great. Maybe i'm doing something else wrong. Because i know people use view models all the time, there should be an easy way to do it.
Any ideas?
Thanks
You really shouldn't be rolling your own password authentication. Use what's built into MVC. That said, the easiest way to save your view model would be this;
using (YourContext db = new YourContext())
{
User u = db.User.Find(userEdit.UserUsername);
db.Entity(u).State = System.Data.Entity.EntityState.Modified;
u.FullName = userEdit.FullName;
....set the other properties here...
db.SaveChanges();
}
I am writing to a database log for my application. One of the fields in the table is userprofile_id which references a username. This is a nullable field, as some stuff gets written to the log without a user context.
In NHibernate, to collect the username, I really can't look at the reference column though, but rather the referenced object. In most cases this is fine; if it's null, there's just no username. So when trying to display the username associated with the log, I actually have to reference the Log.User.UserName.
The twist comes in in that I have a system account with a rigid set of permissions and no associated user profile to invoke automated tasks through the api interface. It writes to the log as userprofile_id system, which does not exist in my UserProfiles table.
To make this work, I had to remove the foreign key between the logs table and the UserProfiles table, but the data is logged, and NHibernate doesn't seem to care. It just reports the UserProfile object as null, which is technically correct.
I would actually like to see the text of the userprofile_id field through NHibernate though, even though it won't join to the UserProfiles table, so I can differentiate the log entries that have no user context and those that have a system function context.
Is there a way, in (N)Hibernate, to request the value of the reference field, rather than the associated object? Or am I going to have to do something custom?
Just add another property to your log entry class and map it as a property to the userprofile_id column.
That said, I think you should really handle the sytem account like all other accounts and have its permissions stored in the database.
EDIT
To illustrate the workaround:
public class Log
{
public virtual User User { get; set; }
//Add this property
public virtual string UserProfileId { get; set; }
public string UserProfileName
{
get { return User != null ? User.Name : UserProfileId; }
}
}
And map it to the column:
public class LogMap : ClassMap<Log>
{
public LogMap()
{
//Map it with this:
Map(l => l.UserProfileId).Column("userprofile_id");
//Don't change the existing mappings
References(l => l.User);//...
}
}
If a Log's userprofile_id is null, then Log.User will be null. You can check it through the referenced property like,
Log.User==null
or query like this,
session.Query<Log>().Where(l=>l.User==null).ToList()
This will fetch the entities not having Users (Logs with userprofile_id is null).
Relying on not-found=ignore is considered a feature designed to help with legacy databases - not something that should be designed into new systems. There are some negative performance aspects. It would be better to make sure you have the proper foreign key, or alternatively not map it as a reference in NHibernate at all (i.e. remove navigability from Log to User) - instead you would call you user lookup method if you need more than the username itself.
Firstly, new to Asp.Net MVC and ADO.NET entity data model.
While practicing an example, I created a table but forgot to set "Identity specification" to true for PK.
Created a model for MVC Application using entity data Model and worked fine.
Later on I've have set that "Identity specification" to true for that table in the DB.
When I try to insert a record an exception is raised and record does not get inserted.
{"Cannot insert explicit value for
identity column in table 'Contacts'
when IDENTITY_INSERT is set to OFF."}
Here is the digner created class in the model, which should have changed as per schema changes in DB
public static Contact CreateContact(int id, string firstName, string lastName, string phone, string email)
{
Contact contact = new Contact();
contact.Id = id;
//
return contact;
}
There is no need for "Id" variable in the above Method Signature as but it is still auto generating that.
How can we make our model to refresh itself or manually,
if the database schema is updated.
NOTE: using C#, ASP.NET MVC
Thanks
Configure your DB schema correctly, then right-click your model and choose "Update Model from Database." This will correct the SSDL in your EDMX, which tells the EF that the id is store generated.
It will not, however, remove the id argument from the CreateContact method. The EF's code generator puts all non-nullable properties in the signature to this method. But once you have updated the SSDL, you should no longer get the exception when you save; if you want to use this method (you don't have to), you can just pass a 0.