How to increment ID before any insert with NHibernate - nhibernate

It looks like NH gets MAX(ID) only once, at first insert and then stores this value internally, this causes me some problems when other processes inserts data. Then I have not actual ID and duplicate key exception is thrown.
Lets imagine we have table Cats
CREATE TABLE Cats(ID int, Name varchar(25))
Then we have corresponding mapping done with FluentNhibernate
public class CatMap : ClassMap<Cat>
{
public CatMap()
{
Id(m=>m.ID).GeneratedBy.Increment();
Map(m=>.Name);
}
}
All I want to achieve is to insert my Cat records with ID's generated by NHibernate using SELECT MAX(ID) FROM Cats before any insert. Executing Session.Flush after any commit dosnt work. I'v done some investigation using SQL Server profiler, and this sql stetement is executed only once (at first insert) - other inserts doesnt force to retreive actual MAX(ID). I know that other algorithms like HiLo are better, but I cant replace it.

As you found out, the NHibernate Increment id generator was not intended for use in a multi-user environment. You state that using a HiLo generator is not an option so you're left with these options:
use the Native generator and change the id column to use the database supported identity mechanism
use the Assigned generator and write code to determine the next valid id
create a Custom generator where you implement the IIdentifierGenerator interface to do what you need
Below is sample code for a custom generator that uses a generalized proc to get an ID for a given table. The main issue with this approach is that you must wrap the code in something like a Unit of Work pattern to ensure the 'select max(id) ..." and the insert are covered by the same database transaction. The IIdentifierGenerator link has the XML mapping you need to wire up this custom generator.
using System;
using System.Collections.Generic;
using System.Data;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.Id;
using NHibernate.Persister.Entity;
using NHibernate.Type;
namespace YourCompany.Stuff
{
public class IdGenerator : IIdentifierGenerator, IConfigurable
{
private string _tableName;
// The "select max(id) ..." query will go into this proc:
private const string DefaultProcedureName = "dbo.getId";
public string ProcedureName { get; protected set; }
public string TableNameParameter { get; protected set; }
public string OutputParameter { get; protected set; }
public IdGenerator()
{
ProcedureName = DefaultProcedureName;
TableNameParameter = "#tableName";
OutputParameter = "#newID";
}
public object Generate(ISessionImplementor session, object obj)
{
int newId;
using (var command = session.Connection.CreateCommand())
{
var tableName = GetTableName(session, obj.GetType());
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
// Set input parameters
var parm = command.CreateParameter();
parm.Value = tableName;
parm.ParameterName = TableNameParameter;
parm.DbType = DbType.String;
command.Parameters.Add(parm);
// Set output parameter
var outputParameter = command.CreateParameter();
outputParameter.Direction = ParameterDirection.Output;
outputParameter.ParameterName = OutputParameter;
outputParameter.DbType = DbType.Int32;
command.Parameters.Add(outputParameter);
// Execute the stored procedure
command.ExecuteNonQuery();
var id = (IDbDataParameter)command.Parameters[OutputParameter];
newId = int.Parse(id.Value.ToString());
if (newId < 1)
throw new InvalidOperationException(
string.Format("Could not retrieve a new ID with proc {0} for table {1}",
ProcedureName,
tableName));
}
return newId;
}
public void Configure(IType type, IDictionary<string, string> parms, Dialect dialect)
{
_tableName = parms["TableName"];
}
private string GetTableName(ISessionImplementor session, Type objectType)
{
if (string.IsNullOrEmpty(_tableName))
{
//Not set by configuration, default to the mapped table of the actual type from runtime object:
var persister = (IJoinable)session.Factory.GetClassMetadata(objectType);
var qualifiedTableName = persister.TableName.Split('.');
_tableName = qualifiedTableName[qualifiedTableName.GetUpperBound(0)]; //Get last string
}
return _tableName;
}
}
}

Related

Looping through Dapper.Net query results without the use of models

I am still new to C# and I am struggling to find a solution to my problem. My SQL dapper query returns a table (based on my understanding though it is not really a table if it is IEnumerable unlike what I am use to working with ADO and recordsets) with three columns col1, col2, and col3 and has multiple rows. I need to loop through this query result for each row and test the values (ie, a foreach loop where I check row(0).field1=5, row(1).field1 = 5 for each row, etc) do what I need to do. This seems so basic but I all the dapper tutorials I see do not show examples for this and if they do they seem to utilize class objects rather than accessing the results directly (if thats even possible or do you have to map the results to a model?) My code is as follows:
String query = "exec dbo.storeProcedure #jsonData, #mainDocJSON, #supportingDocsJSON";
IEnumerable queryResult;
using (var connection = new SqlConnection(connectionString))
{
queryResult = connection.Query(query, new { jsonData = jsonData, mainDocJSON = mainDocJSON, supportingDocsJSON = supportingDocsJSON });
}
I also end up returning IEnumerable results from the controller this code resides in so I send it back to the user in JSON using the following.
return Ok(queryResult);
connection.Query return a IEnumerable, why dont we create a class to map the set from ? Dapper is a micro-ORM, but still... ORM.
For ex: Your table return 3 column Id, Name, CreatedDate.
// declare a class to map the result first
public class ResultHolderDto
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
}
// query somewhere
// This will return IEnumerable<ResultHolderDto>, feel free to play around as normal
var queryResult = await connection.QueryAsync<ResultHolderDto>(query, new { jsonData = jsonData, mainDocJSON = mainDocJSON, supportingDocsJSON = supportingDocsJSON });
foreach(var item in queryResult)
{
var col1Value = queryResult.Id;
var col2Value = queryResult.Name;
var col3Value = queryResult.CreatedDate;
// Then do something with col1Value, col2Value, col3Value...
}

Automatically mapping output parameters with Dapper

I've been using Dapper to call stored procedures passing it an object. For example:
If I have an object:
public int ID { get; set; }
public int Year { get; set; }
I can create this object and pass it to my Execute call as the parameters. Dapper automatically maps all of those properties into parameters and executes the stored procedure. Awesome.
What about output parameters? If my object looked like the following how can I get Dapper to populate that property with the output parameter value?
public int ID { get; set; }
public int Year { get; set; }
public int OutputParameter { get; set; }
Do output parameters have to be added as DynamicParameters?
Something like this:
DynamicParameters _params = new DynamicParameters();
_params.Add("#newId", DbType.Int32, direction: ParameterDirection.Output);
var result = connection.Execute("[dbo].YourProc", _params, null, null, CommandType.StoredProcedure);
var retVal = _params.Get<int>("newId");
DynamicParameters _params = new DynamicParameters(new {
ID = 123,
Year = 2020,
OutputParameter = 0
});
_params.Add("OutputParameter", 0, direction: ParameterDirection.Output);
var result = connection.Execute("[dbo].YourProc", _params, commandType: CommandType.StoredProcedure);
var retVal = _params.Get<int>("OutputParameter");
Here's what I've used to create a generic method for calling stored procedures with generic output parameters.
public void CallProcedure<I, O>(
string storedProcedure,
I inputParameters,
O outputParameters,
string connectionId = "Default")
{
using IDbConnection connection =
new SqlConnection(_config.GetConnectionString(connectionId));
var dynamicParameters = new DynamicParameters();
if (inputParameters != null)
dynamicParameters.AddDynamicParams(inputParameters);
if (outputParameters != null)
foreach (PropertyInfo prop in outputParameters.GetType().GetProperties())
dynamicParameters.Add(prop.Name,
prop.GetValue(outputParameters),
GetDbType(prop.PropertyType),
ParameterDirection.Output);
connection.ExecuteAsync(storedProcedure, dynamicParameters,
commandType: CommandType.StoredProcedure);
if (outputParameters != null)
foreach (PropertyInfo prop in outputParameters.GetType().GetProperties())
{
var method = typeof(DynamicParameters)
.GetMethod("Get")
.MakeGenericMethod(new Type[] { prop.PropertyType });
var outputParamValue = method.Invoke(dynamicParameters, new object[] { prop.Name });
prop.SetValue(outputParameters, outputParamValue);
}
}
What you are looking for is the last few lines, below the second if (outputParameters != null)
I do not know how fast it is, as this code is a part of a sketch project.
I also asked this question 6 years later, so you can check if anyone answered it here: Get all stored procedure output parameters using generic type in dapper
All in all this is a good headsup for dapper team to create a simple method we can call to get all output parameters of a stored procedure, instead of us getting into reflection.

get next sequence value from database using hibernate

I have an entity that has an NON-ID field that must be set from a sequence.
Currently, I fetch for the first value of the sequence, store it on the client's side, and compute from that value.
However, I'm looking for a "better" way of doing this. I have implemented a way to fetch the next sequence value:
public Long getNextKey()
{
Query query = session.createSQLQuery( "select nextval('mySequence')" );
Long key = ((BigInteger) query.uniqueResult()).longValue();
return key;
}
However, this way reduces the performance significantly (creation of ~5000 objects gets slowed down by a factor of 3 - from 5740ms to 13648ms ).
I have tried to add a "fake" entity:
#Entity
#SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
}
However this approach didn't work either (all the Ids returned were 0).
Can someone advise me how to fetch the next sequence value using Hibernate efficiently?
Edit: Upon investigation, I have discovered that calling Query query = session.createSQLQuery( "select nextval('mySequence')" ); is by far more inefficient than using the #GeneratedValue- because of Hibernate somehow manages to reduce the number of fetches when accessing the sequence described by #GeneratedValue.
For example, when I create 70,000 entities, (thus with 70,000 primary keys fetched from the same sequence), I get everything I need.
HOWEVER , Hibernate only issues 1404 select nextval ('local_key_sequence') commands. NOTE: On the database side, the caching is set to 1.
If I try to fetch all the data manually, it will take me 70,000 selects, thus a huge difference in performance. Does anyone know the internal functioning of Hibernate, and how to reproduce it manually?
You can use Hibernate Dialect API for Database independence as follow
class SequenceValueGetter {
private SessionFactory sessionFactory;
// For Hibernate 3
public Long getId(final String sequenceName) {
final List<Long> ids = new ArrayList<Long>(1);
sessionFactory.getCurrentSession().doWork(new Work() {
public void execute(Connection connection) throws SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
resultSet = preparedStatement.executeQuery();
resultSet.next();
ids.add(resultSet.getLong(1));
}catch (SQLException e) {
throw e;
} finally {
if(preparedStatement != null) {
preparedStatement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
});
return ids.get(0);
}
// For Hibernate 4
public Long getID(final String sequenceName) {
ReturningWork<Long> maxReturningWork = new ReturningWork<Long>() {
#Override
public Long execute(Connection connection) throws SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
resultSet = preparedStatement.executeQuery();
resultSet.next();
return resultSet.getLong(1);
}catch (SQLException e) {
throw e;
} finally {
if(preparedStatement != null) {
preparedStatement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
};
Long maxRecord = sessionFactory.getCurrentSession().doReturningWork(maxReturningWork);
return maxRecord;
}
}
Here is what worked for me (specific to Oracle, but using scalar seems to be the key)
Long getNext() {
Query query =
session.createSQLQuery("select MYSEQ.nextval as num from dual")
.addScalar("num", StandardBasicTypes.BIG_INTEGER);
return ((BigInteger) query.uniqueResult()).longValue();
}
Thanks to the posters here: springsource_forum
I found the solution:
public class DefaultPostgresKeyServer
{
private Session session;
private Iterator<BigInteger> iter;
private long batchSize;
public DefaultPostgresKeyServer (Session sess, long batchFetchSize)
{
this.session=sess;
batchSize = batchFetchSize;
iter = Collections.<BigInteger>emptyList().iterator();
}
#SuppressWarnings("unchecked")
public Long getNextKey()
{
if ( ! iter.hasNext() )
{
Query query = session.createSQLQuery( "SELECT nextval( 'mySchema.mySequence' ) FROM generate_series( 1, " + batchSize + " )" );
iter = (Iterator<BigInteger>) query.list().iterator();
}
return iter.next().longValue() ;
}
}
If you are using Oracle, consider specifying cache size for the sequence. If you are routinely create objects in batches of 5K, you can just set it to a 1000 or 5000. We did it for the sequence used for the surrogate primary key and were amazed that execution times for an ETL process hand-written in Java dropped in half.
I could not paste formatted code into comment. Here's the sequence DDL:
create sequence seq_mytable_sid
minvalue 1
maxvalue 999999999999999999999999999
increment by 1
start with 1
cache 1000
order
nocycle;
To get the new id, all you have to do is flush the entity manager. See getNext() method below:
#Entity
#SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
public static long getNext(EntityManager em) {
SequenceFetcher sf = new SequenceFetcher();
em.persist(sf);
em.flush();
return sf.getId();
}
}
POSTGRESQL
String psqlAutoincrementQuery = "SELECT NEXTVAL(CONCAT(:psqlTableName, '_id_seq')) as id";
Long psqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(psqlAutoincrementQuery)
.addScalar("id", Hibernate.LONG)
.setParameter("psqlTableName", psqlTableName)
.uniqueResult();
MYSQL
String mysqlAutoincrementQuery = "SELECT AUTO_INCREMENT as id FROM information_schema.tables WHERE table_name = :mysqlTableName AND table_schema = DATABASE()";
Long mysqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(mysqlAutoincrementQuery)
.addScalar("id", Hibernate.LONG)
.setParameter("mysqlTableName", mysqlTableName)
.uniqueResult();
Interesting it works for you. When I tried your solution an error came up, saying that "Type mismatch: cannot convert from SQLQuery to Query". --> Therefore my solution looks like:
SQLQuery query = session.createSQLQuery("select nextval('SEQUENCE_NAME')");
Long nextValue = ((BigInteger)query.uniqueResult()).longValue();
With that solution I didn't run into performance problems.
And don't forget to reset your value, if you just wanted to know for information purposes.
--nextValue;
query = session.createSQLQuery("select setval('SEQUENCE_NAME'," + nextValue + ")");
Spring 5 has some builtin helper classes for that:
org/springframework/jdbc/support/incrementer
Here is the way I do it:
#Entity
public class ServerInstanceSeq
{
#Id //mysql bigint(20)
#SequenceGenerator(name="ServerInstanceIdSeqName", sequenceName="ServerInstanceIdSeq", allocationSize=20)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ServerInstanceIdSeqName")
public Long id;
}
ServerInstanceSeq sis = new ServerInstanceSeq();
session.beginTransaction();
session.save(sis);
session.getTransaction().commit();
System.out.println("sis.id after save: "+sis.id);
Your idea with the SequenceGenerator fake entity is good.
#Id
#GenericGenerator(name = "my_seq", strategy = "sequence", parameters = {
#org.hibernate.annotations.Parameter(name = "sequence_name", value = "MY_CUSTOM_NAMED_SQN"),
})
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
It is important to use the parameter with the key name "sequence_name". Run a debugging session on the hibernate class SequenceStyleGenerator, the configure(...) method at the line final QualifiedName sequenceName = determineSequenceName( params, dialect, jdbcEnvironment ); to see more details about how the sequence name is computed by Hibernate. There are some defaults in there you could also use.
After the fake entity, I created a CrudRepository:
public interface SequenceRepository extends CrudRepository<SequenceGenerator, Long> {}
In the Junit, I call the save method of the SequenceRepository.
SequenceGenerator sequenceObject = new SequenceGenerator();
SequenceGenerator result = sequenceRepository.save(sequenceObject);
If there is a better way to do this (maybe support for a generator on any type of field instead of just Id), I would be more than happy to use it instead of this "trick".

How to update only one field using Entity Framework?

Here's the table
Users
UserId
UserName
Password
EmailAddress
and the code..
public void ChangePassword(int userId, string password){
//code to update the password..
}
Ladislav's answer updated to use DbContext (introduced in EF 4.1):
public void ChangePassword(int userId, string password)
{
var user = new User() { Id = userId, Password = password };
using (var db = new MyEfContextName())
{
db.Users.Attach(user);
db.Entry(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
}
}
You can tell entity-framework which properties have to be updated in this way:
public void ChangePassword(int userId, string password)
{
var user = new User { Id = userId, Password = password };
using (var context = new ObjectContext(ConnectionString))
{
var users = context.CreateObjectSet<User>();
users.Attach(user);
context.ObjectStateManager.GetObjectStateEntry(user)
.SetModifiedProperty("Password");
context.SaveChanges();
}
}
In Entity Framework Core, Attach returns the entry, so all you need is:
var user = new User { Id = userId, Password = password };
db.Users.Attach(user).Property(x => x.Password).IsModified = true;
db.SaveChanges();
You have basically two options:
go the EF way all the way, in that case, you would
load the object based on the userId provided - the entire object gets loaded
update the password field
save the object back using the context's .SaveChanges() method
In this case, it's up to EF how to handle this in detail. I just tested this, and in the case I only change a single field of an object, what EF creates is pretty much what you'd create manually, too - something like:
`UPDATE dbo.Users SET Password = #Password WHERE UserId = #UserId`
So EF is smart enough to figure out what columns have indeed changed, and it will create a T-SQL statement to handle just those updates that are in fact necessary.
you define a stored procedure that does exactly what you need, in T-SQL code (just update the Password column for the given UserId and nothing else - basically executes UPDATE dbo.Users SET Password = #Password WHERE UserId = #UserId) and you create a function import for that stored procedure in your EF model and you call this function instead of doing the steps outlined above
i'm using this:
entity:
public class Thing
{
[Key]
public int Id { get; set; }
public string Info { get; set; }
public string OtherStuff { get; set; }
}
dbcontext:
public class MyDataContext : DbContext
{
public DbSet<Thing > Things { get; set; }
}
accessor code:
MyDataContext ctx = new MyDataContext();
// FIRST create a blank object
Thing thing = ctx.Things.Create();
// SECOND set the ID
thing.Id = id;
// THIRD attach the thing (id is not marked as modified)
db.Things.Attach(thing);
// FOURTH set the fields you want updated.
thing.OtherStuff = "only want this field updated.";
// FIFTH save that thing
db.SaveChanges();
While searching for a solution to this problem, I found a variation on GONeale's answer through Patrick Desjardins' blog:
public int Update(T entity, Expression<Func<T, object>>[] properties)
{
DatabaseContext.Entry(entity).State = EntityState.Unchanged;
foreach (var property in properties)
{
var propertyName = ExpressionHelper.GetExpressionText(property);
DatabaseContext.Entry(entity).Property(propertyName).IsModified = true;
}
return DatabaseContext.SaveChangesWithoutValidation();
}
"As you can see, it takes as its second parameter an expression of a
function. This will let use this method by specifying in a Lambda
expression which property to update."
...Update(Model, d=>d.Name);
//or
...Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
( A somewhat similar solution is also given here: https://stackoverflow.com/a/5749469/2115384 )
The method I am currently using in my own code, extended to handle also (Linq) Expressions of type ExpressionType.Convert. This was necessary in my case, for example with Guid and other object properties. Those were 'wrapped' in a Convert() and therefore not handled by System.Web.Mvc.ExpressionHelper.GetExpressionText.
public int Update(T entity, Expression<Func<T, object>>[] properties)
{
DbEntityEntry<T> entry = dataContext.Entry(entity);
entry.State = EntityState.Unchanged;
foreach (var property in properties)
{
string propertyName = "";
Expression bodyExpression = property.Body;
if (bodyExpression.NodeType == ExpressionType.Convert && bodyExpression is UnaryExpression)
{
Expression operand = ((UnaryExpression)property.Body).Operand;
propertyName = ((MemberExpression)operand).Member.Name;
}
else
{
propertyName = System.Web.Mvc.ExpressionHelper.GetExpressionText(property);
}
entry.Property(propertyName).IsModified = true;
}
dataContext.Configuration.ValidateOnSaveEnabled = false;
return dataContext.SaveChanges();
}
New EF Core 7 native feature — ExecuteUpdate:
Finally! After a long wait, EF Core 7.0 now has a natively supported way to run UPDATE (and also DELETE) statements while also allowing you to use arbitrary LINQ queries (.Where(u => ...)), without having to first retrieve the relevant entities from the database: The new built-in method called ExecuteUpdate — see "What's new in EF Core 7.0?".
ExecuteUpdate is precisely meant for these kinds of scenarios, it can operate on any IQueryable instance, and lets you update specific columns on any number of rows, while always issuing a single UPDATE statement behind the scenes, making it as efficient as possible.
Usage:
Let's take OP's example — i.e. updating the password column of a specific user:
dbContext.Users
.Where(u => u.Id == someId)
.ExecuteUpdate(b =>
b.SetProperty(u => u.Password, "NewPassword")
);
As you can see, calling ExecuteUpdate requires you to make calls to the SetProperty method, to specify which property to update, and also what new value to assign to it.
EF Core will translate this into the following UPDATE statement:
UPDATE [u]
SET [u].[Password] = "NewPassword"
FROM [Users] AS [u]
WHERE [u].[Id] = someId
Also, ExecuteDelete for deleting rows:
There's also a counterpart to ExecuteUpdate called ExecuteDelete, which, as the name implies, can be used to delete a single or multiple rows at once without having to first fetch them.
Usage:
// Delete users that haven't been active in 2022:
dbContext.Users
.Where(u => u.LastActiveAt.Year < 2022)
.ExecuteDelete();
Similar to ExecuteUpdate, ExecuteDelete will generate DELETE SQL statements behind the scenes — in this case, the following one:
DELETE FROM [u]
FROM [Users] AS [u]
WHERE DATEPART(year, [u].[LastActiveAt]) < 2022
Other notes:
Keep in mind that both ExecuteUpdate and ExecuteDelete are "terminating", meaning that the update/delete operation will take place as soon as you call the method. You're not supposed to call dbContext.SaveChanges() afterwards.
If you're curious about the SetProperty method, and you're confused as to why ExectueUpdate doesn't instead receive a member initialization expression (e.g. .ExecuteUpdate(new User { Email = "..." }), then refer to this comment (and the surrounding ones) on the GitHub issue for this feature.
Furthermore, if you're curious about the rationale behind the naming, and why the prefix Execute was picked (there were also other candidates), refer to this comment, and the preceding (rather long) conversation.
Both methods also have async equivalents, named ExecuteUpdateAsync, and ExecuteDeleteAsync respectively.
In EntityFramework Core 2.x there is no need for Attach:
// get a tracked entity
var entity = context.User.Find(userId);
entity.someProp = someValue;
// other property changes might come here
context.SaveChanges();
Tried this in SQL Server and profiling it:
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [User] SET [someProp] = #p0
WHERE [UserId] = #p1;
SELECT ##ROWCOUNT;
',N'#p1 int,#p0 bit',#p1=1223424,#p0=1
Find ensures that already loaded entities do not trigger a SELECT and also automatically attaches the entity if needed (from the docs):
Finds an entity with the given primary key values. If an entity with the given primary key values is being tracked by the context, then it is returned immediately without making a request to the database. Otherwise, a query is made to the database for an entity with the given primary key values and this entity, if found, is attached to the context and returned. If no entity is found, then null is returned.
I'm late to the game here, but this is how I am doing it, I spent a while hunting for a solution I was satisified with; this produces an UPDATE statement ONLY for the fields that are changed, as you explicitly define what they are through a "white list" concept which is more secure to prevent web form injection anyway.
An excerpt from my ISession data repository:
public bool Update<T>(T item, params string[] changedPropertyNames) where T
: class, new()
{
_context.Set<T>().Attach(item);
foreach (var propertyName in changedPropertyNames)
{
// If we can't find the property, this line wil throw an exception,
//which is good as we want to know about it
_context.Entry(item).Property(propertyName).IsModified = true;
}
return true;
}
This could be wrapped in a try..catch if you so wished, but I personally like my caller to know about the exceptions in this scenario.
It would be called in something like this fashion (for me, this was via an ASP.NET Web API):
if (!session.Update(franchiseViewModel.Franchise, new[]
{
"Name",
"StartDate"
}))
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
Entity framework tracks your changes on objects that you queried from database via DbContext. For example if you DbContext instance name is dbContext
public void ChangePassword(int userId, string password){
var user = dbContext.Users.FirstOrDefault(u=>u.UserId == userId);
user.password = password;
dbContext.SaveChanges();
}
I know this is an old thread but I was also looking for a similar solution and decided to go with the solution #Doku-so provided. I'm commenting to answer the question asked by #Imran Rizvi , I followed #Doku-so link that shows a similar implementation. #Imran Rizvi's question was that he was getting an error using the provided solution 'Cannot convert Lambda expression to Type 'Expression> [] ' because it is not a delegate type'. I wanted to offer a small modification I made to #Doku-so's solution that fixes this error in case anyone else comes across this post and decides to use #Doku-so's solution.
The issue is the second argument in the Update method,
public int Update(T entity, Expression<Func<T, object>>[] properties).
To call this method using the syntax provided...
Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn);
You must add the 'params' keyword in front of the second arugment as so.
public int Update(T entity, params Expression<Func<T, object>>[] properties)
or if you don't want to change the method signature then to call the Update method you need to add the 'new' keyword, specify the size of the array, then finally use the collection object initializer syntax for each property to update as seen below.
Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });
In #Doku-so's example he is specifying an array of Expressions so you must pass the properties to update in an array, because of the array you must also specify the size of the array. To avoid this you could also change the expression argument to use IEnumerable instead of an array.
Here is my implementation of #Doku-so's solution.
public int Update<TEntity>(LcmsEntities dataContext, DbEntityEntry<TEntity> entityEntry, params Expression<Func<TEntity, object>>[] properties)
where TEntity: class
{
entityEntry.State = System.Data.Entity.EntityState.Unchanged;
properties.ToList()
.ForEach((property) =>
{
var propertyName = string.Empty;
var bodyExpression = property.Body;
if (bodyExpression.NodeType == ExpressionType.Convert
&& bodyExpression is UnaryExpression)
{
Expression operand = ((UnaryExpression)property.Body).Operand;
propertyName = ((MemberExpression)operand).Member.Name;
}
else
{
propertyName = System.Web.Mvc.ExpressionHelper.GetExpressionText(property);
}
entityEntry.Property(propertyName).IsModified = true;
});
dataContext.Configuration.ValidateOnSaveEnabled = false;
return dataContext.SaveChanges();
}
Usage:
this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
#Doku-so provided a cool approach using generic's, I used the concept to solve my issue but you just can't use #Doku-so's solution as is and in both this post and the linked post no one answered the usage error questions.
Combining several suggestions I propose the following:
async Task<bool> UpdateDbEntryAsync<T>(T entity, params Expression<Func<T, object>>[] properties) where T : class
{
try
{
var entry = db.Entry(entity);
db.Set<T>().Attach(entity);
foreach (var property in properties)
entry.Property(property).IsModified = true;
await db.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("UpdateDbEntryAsync exception: " + ex.Message);
return false;
}
}
called by
UpdateDbEntryAsync(dbc, d => d.Property1);//, d => d.Property2, d => d.Property3, etc. etc.);
Or by
await UpdateDbEntryAsync(dbc, d => d.Property1);
Or by
bool b = UpdateDbEntryAsync(dbc, d => d.Property1).Result;
I use ValueInjecter nuget to inject Binding Model into database Entity using following:
public async Task<IHttpActionResult> Add(CustomBindingModel model)
{
var entity= await db.MyEntities.FindAsync(model.Id);
if (entity== null) return NotFound();
entity.InjectFrom<NoNullsInjection>(model);
await db.SaveChangesAsync();
return Ok();
}
Notice the usage of custom convention that doesn't update Properties if they're null from server.
ValueInjecter v3+
public class NoNullsInjection : LoopInjection
{
protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
{
if (sp.GetValue(source) == null) return;
base.SetValue(source, target, sp, tp);
}
}
Usage:
target.InjectFrom<NoNullsInjection>(source);
Value Injecter V2
Lookup this answer
Caveat
You won't know whether the property is intentionally cleared to null OR it just didn't have any value it. In other words, the property value can only be replaced with another value but not cleared.
_context.Users.UpdateProperty(p => p.Id, request.UserId, new UpdateWrapper<User>()
{
Expression = p => p.FcmId,Value = request.FcmId
});
await _context.SaveChangesAsync(cancellationToken);
Update Property is an extension method
public static void UpdateProperty<T, T2>(this DbSet<T> set, Expression<Func<T, T2>> idExpression,
T2 idValue,
params UpdateWrapper<T>[] updateValues)
where T : class, new()
{
var entity = new T();
var attach = set.Attach(entity);
attach.Property(idExpression).IsModified = false;
attach.Property(idExpression).OriginalValue = idValue;
foreach (var update in updateValues)
{
attach.Property(update.Expression).IsModified = true;
attach.Property(update.Expression).CurrentValue = update.Value;
}
}
And Update Wrapper is a class
public class UpdateWrapper<T>
{
public Expression<Func<T, object>> Expression { get; set; }
public object Value { get; set; }
}
I was looking for same and finally I found the solution
using (CString conn = new CString())
{
USER user = conn.USERs.Find(CMN.CurrentUser.ID);
user.PASSWORD = txtPass.Text;
conn.SaveChanges();
}
believe me it work for me like a charm.
public async Task<bool> UpdateDbEntryAsync(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
{
try
{
this.Context.Set<TEntity>().Attach(entity);
EntityEntry<TEntity> entry = this.Context.Entry(entity);
entry.State = EntityState.Modified;
foreach (var property in properties)
entry.Property(property).IsModified = true;
await this.Context.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
public void ChangePassword(int userId, string password)
{
var user = new User{ Id = userId, Password = password };
using (var db = new DbContextName())
{
db.Entry(user).State = EntityState.Added;
db.SaveChanges();
}
}

passing associative array of type Timestamp to oracle stored procedure

We're running into a strange error using Oracle Odp.Net (connecting to Oracle 9). The problem is illustrated by the code snippet below.
This is the error we're experiencing:
ORA-00600: internal error code, arguments: [15419], [severe error during PL/SQL execution], [], [], [], [], [], []
ORA-06544: PL/SQL: internal error, arguments: [78502], [], [], [], [], [], [], []
ORA-06553: PLS-801: internal error [78502]
Googling around makes us suspect (though we're not entirely sure) that passing an array of Timestamps is not supported by Odp.Net.
So the question is 2-fold:
is it possible to pass an array of
timestamp to a pl/sql procedure using
odp.net?
if not, is there a good workaround available?
C# console program illustrating the problem:
using System;
using System.Collections;
using System.Data;
using Oracle.DataAccess.Client;
class Program
{
private const string _db = "<db>";
private const string _username = "<user>";
private const string _password = "<password>";
private const string _storedProcedureName = "<sproc>";
static void Main(string[] args)
{
var connectionString = string.Format(
"data source={0};user id={1};password={2}",
_db, _username, _password);
var connection = new OracleConnection(connectionString);
try
{
connection.Open();
var timeStamps = new[] { DateTime.Now, DateTime.Now };
var parameter = new OracleParameter("inTimeStamps", OracleDbType.TimeStamp)
{
Direction = ParameterDirection.Input,
CollectionType = OracleCollectionType.PLSQLAssociativeArray,
Size = timeStamps.Length,
Value = timeStamps
};
var command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = _storedProcedureName;
command.Parameters.Add(parameter);
command.ExecuteReader();
}
finally
{
connection.Close();
}
}
}
The code is calling the following PL/SQL stored procedure
TYPE ArrayOfTimestamps is table of timestamp index by binary_integer;
PROCEDURE TestOdpTimeStamp (inTimeStamps in ArrayOfTimestamps)
IS
test number;
BEGIN
select 1 into test from dual;
END;
You can pass a nested table of timestamps instead of an associative array to a PL/SQL procedure.
You need odp.net 11.1.0.6.20 or higher, you can connect with odp.net 11.1.0.6.20 to an Oracle 9 server.
Execute as Oracle user testts:
create or replace type MyTimeStamp as object
(
my timestamp
)
/
create or replace type mytimestamp_table as table of MyTimeStamp
/
create table testinserttimestamp
( my timestamp);
create or replace procedure test_timestamp_table (p_in in mytimestamp_table)
is
begin
for i in p_in.first..p_in.last loop
insert into testinserttimestamp values (p_in(i).my);
end loop;
commit;
end;
In C#, create a form with a button called button1, and do...
using System;
using System.Data;
using System.Windows.Forms;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
namespace TestTimeStamp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class MyUdtTimeStamp : INullable, IOracleCustomType
{
[OracleObjectMappingAttribute("MY")]
public OracleTimeStamp My { get; set; }
public bool IsNull
{
get { return false;}
}
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, "MY", My);
}
public void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
My = (OracleTimeStamp)OracleUdt.GetValue(con, pUdt, "MY");
}
}
[OracleCustomTypeMappingAttribute("TESTTS.MYTIMESTAMP")]
public class StudentFactory : IOracleCustomTypeFactory
{
public IOracleCustomType CreateObject()
{
return new MyUdtTimeStamp();
}
}
[OracleCustomTypeMappingAttribute("TESTTS.MYTIMESTAMP_TABLE")]
public class PersonArrayFactory : IOracleArrayTypeFactory
{
public Array CreateArray(int numElems)
{
return new MyUdtTimeStamp[numElems];
}
public Array CreateStatusArray(int numElems)
{
return null;
}
}
private void button1_Click(object sender, EventArgs e)
{
OracleConnectionStringBuilder b = new OracleConnectionStringBuilder();
b.UserID = "testts";
b.Password = "ts";
b.DataSource = "ora11";
using (OracleConnection conn = new OracleConnection(b.ToString())) {
conn.Open();
using (OracleCommand comm = conn.CreateCommand())
{
comm.CommandText = "begin test_timestamp_table(:1); end;";
OracleParameter p = new OracleParameter();
p.OracleDbType = OracleDbType.Array;
p.Direction = ParameterDirection.Input;
p.UdtTypeName = "TESTTS.MYTIMESTAMP_TABLE";
MyUdtTimeStamp[] times = new MyUdtTimeStamp[2];
MyUdtTimeStamp m1 = new MyUdtTimeStamp();
m1.My = new OracleTimeStamp(DateTime.Now);
MyUdtTimeStamp m2 = new MyUdtTimeStamp();
m2.My = new OracleTimeStamp(DateTime.Now);
times[0] = m1;
times[1] = m2;
p.Value = times;
comm.Parameters.Add(p);
comm.ExecuteNonQuery();
}
conn.Close();
}
}
}
}
Do in Oracle...
SQL> select * from testinserttimestamp;
MY
-------------------------------------------------
12-10-09 21:13:54,328125
12-10-09 21:13:55,171875
There is a Metalink note (788282.1) that states that this is an error possible from passing an unsupported datatype. TIMESTAMP is not supported. You can get around it by constructing an anonymous PL/SQL block in your C# code and calling the problematic stored procedure from within that block.
EDIT:
I can't post the code from Metalink for obvious reasons.
The workaround is problematic if your array contains a lot of values, since the anonymous PL/SQL block will have to contain code to explicitly assign each entry in the array a value via a bind variable. It's clunky. This illustrates the idea:
comm.CommandText = "declare "+
"theTS mytimestamp_table;"+
"begin"+
" theTS(1):= :1;"+
" theTS(2):= :2;"+
" test_timestamp_table(theTS);"+
" end;";
You would then have to construct your parameter list to provide values for each of the bind variables.
I recently hit this problem too, but I overcome in a different way by using strings and to_timestamp, I hope this helps anyone who further down the line hits this problem:
http://timscyclingblog.wordpress.com/2011/10/07/oracle-plsqlassociativearray-timestamp-workaround/