Reputation: 1270
In my current project i am working with the database which has very strange table structure (All Id Fields in most tables are marked as not nullable and primary while there is not auto increment increment enabled for them those Id fields need to be unique as well).
unfortunately there is not way i can modify DB so i find another why to handle my problem.
I have no issues while querying for data but during insert What i want to do is, To get max Id from table where entity is about to be inserted and increment it by one or even better use SSELECT max(id) pattern during insert.
I was hoping to use Interceptor inside EF to achieve this but is looks too difficult for me now and all i managed to do is to identify if this is insert command or not.
Can someone help me through my way on this problem? how can i achieve this and set ID s during insert either by selecting max ID or using SELECT max(id)
public void TreeCreated(DbCommandTreeInterceptionContext context)
{
if (context.OriginalResult.CommandTreeKind != DbCommandTreeKind.Insert && context.OriginalResult.DataSpace != DataSpace.CSSpace) return;
{
var insertCommand = context.Result as DbInsertCommandTree;
var property = insertCommand?.Target.VariableType.EdmType.MetadataProperties.FirstOrDefault(x => x.Name == "TableName");
if (property == null) return;
var tbaleName = property?.Value as ReadOnlyCollection<EdmMember>;
var variableReference = insertCommand.Target.VariableType.Variable(insertCommand.Target.VariableName);
var tenantProperty = variableReference.Property("ID");
var tenantSetClause = DbExpressionBuilder.SetClause(tenantProperty, DbExpression.FromString("(SELECT MAX(ID) FROM SOMEHOWGETTABLENAME)"));
var filteredSetClauses = insertCommand.SetClauses.Cast<DbSetClause>().Where(sc => ((DbPropertyExpression)sc.Property).Property.Name != "ID");
var finalSetClauses = new ReadOnlyCollection<DbModificationClause>(new List<DbModificationClause>(filteredSetClauses) { tenantSetClause });
var newInsertCommand = new DbInsertCommandTree(
insertCommand.MetadataWorkspace,
insertCommand.DataSpace,
insertCommand.Target,
finalSetClauses,
insertCommand.Returning);
context.Result = newInsertCommand;
}
}
Unfortunately that concept of Interceptor is a little bit new for me and i do not understand it completely.
I manage to dynamically build that expression so that ID field is now included in insert statement, but the problem here is that I can not use SQL query inside it. whenever i try to use this it always results in some wrong SQL query so is there anyway i tweak insert statement so that this SELECT MAX(ID) FROM TABLE_NAME is executed during insert?
Upvotes: 0
Views: 1006
Reputation: 7211
Get the next id from the context, and then set the parameter of the insert command accordingly.
void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
var context = interceptionContext.DbContexts.First() as WhateverYourEntityContainerNameIs;
// get the next id from the database using the context
var theNextId = (from foo in context...)
// update the parameter on the command
command.Parameters["YourIdField"].Value = theNextId;
}
Just bear in mind this is not terribly thread safe; if two users update the same table at exactly the same time, they could theoretically get the same id. This is going to be a problem no matter what update method you use if you manage keys in the application instead of the database. But it looks like that decision is out of your hands.
If this is going to be a problem, you might have to do something more drastic like alter the command.CommandText
to replace the value in the values
clause with a subquery, for example change
insert into ... values (@YourIdField, ...)
to
insert into ... values ((select max(id) from...), ...)
Upvotes: 1