I am working on project to implement some check-in policies in TFS at server side . As part of this, I am trying to get the history of the changeset now being committed. But when check in happens, I get changeset number as -1. I dont' know why this happening. I doubt whether the changeset number is assigned only after the ProcessEvent method is executed. Appreciate your help.
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType,
object notificationEventArgs, out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = string.Empty;
if (notificationType == NotificationType.DecisionPoint)
{
try
{
if (notificationEventArgs is CheckinNotification)
{
CheckinNotification notification = notificationEventArgs as CheckinNotification;
int changeId = notification.Changeset;; // here I get the Changeset as -1
}
}
}
}
You're not going to be able to get the current changeset number, as the policies are executed before the changeset is committed.
It has to do it this way so that it can deny the checkin if the policy is violated. If the checkin is denied, it shouldn't increment the changeset number.
Related
I am trying to handle concurrency in an "add-if-not-already-there" operation in .Net Core EF as follows:
internal static Folder GetOrCreateFolder(DbContext dbContext, User user, string folderNature, string folderName)
{
Folder folder = GetExistingFolder(dbContext, folderNature, folderName);
if (folder == null)
{
try
{
folder = new Folder()
{
CreatedBy = user,
CreatedDate = DateTime.UtcNow,
Nature = folderNature,
Name = folderName
};
dbContext.Folders.Add(folder);
dbContext.SaveChanges();
}
catch
{
// see if the record has already been created in another call
folder = GetExistingFolder(dbContext, folderNature, folderName);
if (folder == null) // something else is wrong
{
throw;
}
}
}
return folder;
}
The problem is that the second attempt to get the row when it has already been created also fails, throwing an exception: Cannot insert duplicate key row in object 'dbo.Folders' with unique index 'IX_Folders_Name_Nature'. The duplicate key value is...
UPDATE: Here is the GetExistingFolder method:
private static Folder GetExistingFolder(CMSDbContext dbContext, string folderNature, string folderName)
{
return dbContext.Folders.FirstOrDefault(f => f.Nature == folderNature && f.Name == folderName);
}
The code seems like it should be able to handle this situation, but why am I still getting that error? Thanks!
So here is the remedy:
First, here is the procedure using one transient dbcontext in each thread:
Get or create a row in parent table - commit if parent row is missing
Create a child row in child table and commit
Now, two concurrent requests needed to create the same parent row. At the beginning, the parent row is not there, so both attempt to create it. Needless to say, only one succeeds when committing due to constraints on the parent table. Knowing that the parent row may have been created in another thread, I accounted for that by re-examining the parent table when this happens to see if the required row is already there -- if yes, we are good, and we can proceed to step 2 without committing.
What I missed once I figured that the parent row has already been created in another thread, was that I should have also removed it from dbcontext change history using:
dbContext.ParentTable.Remove(parentRowThatHasAlreadyBeenCreated);
So its creation would not be attempted during the commit for the child row. So essentially there should be a step 2.5, where an unsuccessful commit to parent table is cleaned up.
I have two different processes (on different machines) that are reading and updating a database record.
The rule I need to ensure is that the record must only be updated if the value of it, lets say is "Initial". Also, after the commit I would want to know if it actually got updated from the current process or not (in case if value was other than initial)
Now, the below code performs something like:
var record = context.Records
.Where(r => (r.id == id && r.State == "Initial"))
.FirstOrDefault();
if(record != null) {
record.State = "Second";
context.SaveChanges();
}
Now couple of questions
1) From looking at the code it appears that after the record is fetched with state "Initial", some other process could have updated it to state "Second" before this process performs SaveChanges.
In this case we are unnecessarily overwriting the state to the same value. Is this the case happening here ?
2) If case 1 is not what happens then EntityFramework may be translating the above to something like
update Record set State = "Second" where Id = someid and State = "Initial"
and performing this as a transaction. This way only one process writes the value. Is this the case with EF default TransactionScope ?
In both cases again how do I know for sure that the update was made from my process as opposed to some other process ?
If this were in-memory objects then in code it would translate to something like assuming multiple threads accessing same data structure
Record rec = FindRecordById(id);
lock (someobject)
{
if(rec.State == "Initial")
{
rec.State = "Second";
//Now, that I know I updated it I can do some processing
}
}
Thanks
In general there are 2 main concurrency patterns that can be used:
Pessimistic concurrency: You lock a row to prevent others from unexpectedly changing the data you are currently attempting to update. EF does not provide any native support for this type of concurrency pattern.
Optimistic concurrency: Citing from EF's documentation: "Optimistic concurrency involves optimistically attempting to save your entity to the database in the hope that the data there has not changed since the entity was loaded. If it turns out that the data has changed then an exception is thrown and you must resolve the conflict before attempting to save again." This pattern is supported by EF, and can be used rather simply.
Focusing on the optimistic concurrency option, which EF does support, let's compare how your example behaves with and without EF's optimistic concurrency control handling. I'll assume you are using SQL Server.
No concurrency control
Let's start with the following script in the database:
create table Record (
Id int identity not null primary key,
State varchar(50) not null
)
insert into Record (State) values ('Initial')
And here is the code with the DbContext and Record entity:
public class MyDbContext : DbContext
{
static MyDbContext()
{
Database.SetInitializer<MyDbContext>(null);
}
public MyDbContext() : base(#"Server=localhost;Database=eftest;Trusted_Connection=True;") { }
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new Record.Configuration());
}
}
public class Record
{
public int Id { get; set; }
public string State { get; set; }
public class Configuration : EntityTypeConfiguration<Record>
{
public Configuration()
{
this.HasKey(t => t.Id);
this.Property(t => t.State)
.HasMaxLength(50)
.IsRequired();
}
}
}
Now, let's test your concurrent update scenario with the following code:
static void Main(string[] args)
{
using (var context = new MyDbContext())
{
var record = context.Records
.Where(r => r.Id == 1 && r.State == "Initial")
.Single();
// Insert sneaky update from a different context.
using (var sneakyContext = new MyDbContext())
{
var sneakyRecord = sneakyContext.Records
.Where(r => r.Id == 1 && r.State == "Initial")
.Single();
sneakyRecord.State = "Sneaky Update";
sneakyContext.SaveChanges();
}
// attempt to update row that has just been updated and committed by the sneaky context.
record.State = "Second";
context.SaveChanges();
}
}
If you trace the SQL, you will see that the update statement looks like this:
UPDATE [dbo].[Record]
SET [State] = 'Second'
WHERE ([Id] = 1)
So, in effect, it doesn't care that another transaction sneaked in an update. It just blindly writes over whatever the other update did. And so, the final value of State in the database for that row is 'Second'.
Optimistic concurrency control
Let's adjust our initial SQL script to include a concurrency control column to our table:
create table Record (
Id int identity not null primary key,
State varchar(50) not null,
Concurrency timestamp not null -- add this row versioning column
)
insert into Record (State) values ('Initial')
Let's also adjust our Record entity class (the DbContext class stays the same):
public class Record
{
public int Id { get; set; }
public string State { get; set; }
// Add this property.
public byte[] Concurrency { get; set; }
public class Configuration : EntityTypeConfiguration<Record>
{
public Configuration()
{
this.HasKey(t => t.Id);
this.Property(t => t.State)
.HasMaxLength(50)
.IsRequired();
// Add this config to tell EF that this
// property/column should be used for
// concurrency checking.
this.Property(t => t.Concurrency)
.IsRowVersion();
}
}
}
Now, if we try to re-run the same Main() method we used for the previous scenario, you will notice a change in how the update statement is generated and executed:
UPDATE [dbo].[Record]
SET [State] = 'Second'
WHERE (([Id] = 1) AND ([Concurrency] = <byte[]>))
SELECT [Concurrency]
FROM [dbo].[Record]
WHERE ##ROWCOUNT > 0 AND [Id] = 1
In particular, notice how EF automatically includes the column defined for concurrency control in the where clause of the update statement.
In this case, because there was in fact a concurrent update, EF detects it, and throws a DbUpdateConcurrencyException exception on this line:
context.SaveChanges();
And so, in this case, if you check the database, you'll see that the State value for the row in question will be 'Sneaky Update', because our 2nd update failed to pass the concurrency check.
Final thoughts
As you can see, there isn't much that needs to be done to activate automatic optimistic concurrency control in EF.
Where it gets tricky though is, how do you handle the DbUpdateConcurrencyException exception when it gets thrown? It will largely be up to you to decide what you want to do in this case. But for further guidance on the topic, you'll find more information here: EF - Optimistic Concurrency Patterns.
I am using TFS 2012. So after I checkin a changes of a workspace to the server I don’t need that workspace any more in my server. So how it can be deleted logically? The entries from the database need to be deleted for that workspace?
How can I get the list of workspace whose changes are already checked in ? I think there will be sql some script by using database and tables of that collection. I tried to identify from workspace table entries. But didn’t find any identification there. So please help.
This little program should work for you:
class Program
{
static private TfsTeamProjectCollection _tfs;
static void Main(string[] args)
{
_tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("myuri"));
var service = _tfs.GetService<VersionControlServer>();
Workspace[] ws = service.QueryWorkspaces(null, null, null);
foreach(Workspace s in ws)
{
var pend = s.GetPendingChanges();
if (pend.Count() == 0)
{
Console.WriteLine("Workspace {0} has no pending changes", s.Name);
// s.Delete()
continue;
}
}
// Console.ReadLine();
}
}
I believe that uncommenting the Delete will clear them for you, but that is untested, so use with caution.
I've run into some trouble with unique constraints in NHibernate.
I have a User entity that is mapped with a unique constraint for the Username property. What I want to do is to be able to check if a particular username exists before a new user is added and also before an existing user updates it's username.
The first scenario (adding a new user) works just fine. However, when I try to check if the username exists before updating an existing user, I get a constraint violation. Here's what the code for my Save method looks like.
public void Save<T>(T entity) where T : User
{
using (var session = GetSession())
using (var transaction = session.BeginTransaction())
{
try
{
CheckIfUsernameExists(entity);
session.SaveOrUpdate(entity);
session.Flush();
transaction.Commit();
}
catch (HibernateException)
{
transaction.Rollback();
throw;
}
}
}
The constraint is violated in the CheckIfUsernameExists() method and it looks like this:
public void CheckIfUsernameExists<T>(T entity) where T : User
{
var user = GetUserByUsername(entity);
if (user != null)
throw new UsernameExistsException();
}
private T GetUserByUsername<T>(T entity) where T : User
{
var username = entity.Username;
var idToExclude = entity.Id;
var session = GetSession();
var user = session.CreateCriteria<T>()
.Add(Restrictions.Eq("Username", username))
.Add(Restrictions.Not(Restrictions.IdEq(idToExclude)))
.UniqueResult() as T;
return user;
}
It is the session.CreateCriteria() line that causes it to crash resulting in an NHibernateException (SQLiteException) with the message "Abort due to constraint violation. column Username is not unique".
Is it related to the NHibernate cash? The entity that is passed to the save method has been updated with the desired username at the time session.CreateCriteria() is called.
Maybe I'm doing this all wrong (I am an NHibernate beginner) so please feel free to state the obvious and suggest alternatives.
Any help is much appreciated!
Hmm, I'm not sure about the core of the problem, but for the strategy of trying to see whether a user already exists, why do you need the ".UniqueResult()"?
Couldn't you just assume to get a list of users which match that username and which do not have the same id as your current user (obviously). Pseudo-code like I'd do something like this
public bool ExistsUsername(string username, int idToExclude)
{
IList<User> usersFound = someNHibernateCriteria excluding entries that have id = idToExclude
return (usersFound.Count > 0)
}
Two thoughts:
- Why don't you just SaveOrUpdate and see if you succeed. Is that not possible in your scenario?
- I've seen you mentioning SQLite. Is that your real production system, or just something you use for testing. If so, have you checked if it's SQLite that makes the problems, and the query works against a fully featured DBMS? - SQLite frequently makes that kind of problems, because it does not support every kind of constraint...
Are you sure the exception is thrown at CreateCriteria? Because, I don't see how you could get a SQLlite constraint exception from a select statement. I do virtually the same thing...
public bool NameAlreadyExists(string name, int? exclude_id)
{
ICriteria crit = session.CreateCriteria<User>()
.SetProjection(Projections.Constant(1))
.Add(Restrictions.Eq(Projections.Property("name"), name));
if (exclude_id.HasValue)
crit.Add(Restrictions.Not(Restrictions.IdEq(exclude_id.Value)));
return crit.List().Count > 0;
}
I would look at the order of the generated sql to see what's causing it. If that entity was loaded in that session, it could be getting updated before the query.
transaction.Rollback() doesn't remove your entity from session cache, use session.Evict() instead.
See:
- https://www.hibernate.org/hib_docs/nhibernate/html/performance.html#performance-sessioncache
I am new to smack API. I am trying to develop a chat application where I was trying for setting and getting the presence.
When I change the presence of a user, its working perfectly fine and it is getting reflected in the Openfire Server.
But when I tries to get the Presence of a user, I am always getting the status as 'unavailable' even if his presence in openfire is showing as 'available'.
I am using the following code to set the status.
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Online, Programmatically!");
presence.setPriority(24);
presence.setMode(Presence.Mode.available);
user.getConnection().sendPacket(presence);
I am using the Roster class to get the presence as follows.
Roster roster = avatar.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for(RosterEntry rosterEntry: entries) {
String user = rosterEntry.getUser();
Presence presence = roster.getPresence(user);
System.out.println("Presence : "+presence); // 1
System.out.println("Presence type: "+presence.getType()); // 2
System.out.println("Presence mode: "+presence.getMode()); // 3
}
Line No 1 alwasys gives 'unavailable' while line number 2 and 3 always give null
I am not able to figure out the cause of this problem. Please help me to resolve this issue.
Thanks in advance.
Using RosterListener is the proper solution to this problem. There is no reason that code should have a Thread.sleep() in order to make it work properly.
Roster roster = con.getRoster();
roster.addRosterListener(new RosterListener() {
// Ignored events public void entriesAdded(Collection<String> addresses) {}
public void entriesDeleted(Collection<String> addresses) {}
public void entriesUpdated(Collection<String> addresses) {}
public void presenceChanged(Presence presence) {
System.out.println("Presence changed: " + presence.getFrom() + " " + presence);
}
});
(source: http://www.igniterealtime.org/builds/smack/docs/latest/documentation/roster.html)
the problem is that after logging in immediately, it is gonna take some time for the presence of users to get updated.So between logging in and calling the online buddies function there should be a thread.sleep() for a few seconds.Then the online contacts will be retrieved. I did that and was able to retrieve them.
after login use
Thread.sleep(5000);
use in the beginiing of the method also
I had the same problem and searched for a while before finding what the problem was. In fact, you don't need to do a Thread.sleep(). The problem is that you don't have the "permission" to get the Presence of other users.
To solve the problem, just go in Openfire admin -> your user options -> Roster // Then just set the subscription of the buddy you wanna get the presence to "both" (both users can view each other presence).
Hope that is helps.
Edit : In fact you need to add a Thread.sleep() before getting the roster from the connection. Without the Thread.sleep(), sometimes it works, sometimes not...
I fixed it adding:
if (!roster.isLoaded())
roster.reloadAndWait();
after:
Roster roster = Roster.getInstanceFor(connection);
Ref: Smack 4.1.0 android Roster not displaying
This full code
public void getRoaster(final Callback<List<HashMap<String, String>>> callback) {
final Roster roster = Roster.getInstanceFor(connection);
boolean success = true;
if (!roster.isLoaded())
try {
roster.reloadAndWait();
} catch (SmackException.NotLoggedInException | SmackException.NotConnectedException | InterruptedException e) {
android.util.Log.e(AppConstant.PUBLIC_TAG, TAG + " " + e.getMessage());
success = false;
}
if (!success) {
if (callback != null) {
callback.onError(new Throwable());
}
}
Collection<RosterEntry> entries = roster.getEntries();
List<HashMap<String, String>> maps = new ArrayList<HashMap<String, String>>(entries.size());
for (RosterEntry entry : entries) {
HashMap<String, String> map = new HashMap<String, String>(3);
Presence presence = roster.getPresence(entry.getUser());
map.put(ROASTER_KEY, entry.getName());
map.put(ROASTER_BARE_JID, entry.getUser());
map.put(PRESENCE_TYPE, presence.isAvailable() == true ? PRESENCE_ONLINE : PRESENCE_OFFLINE);
maps.add(map);
}
if (maps != null && maps.size() > 0 && callback != null) {
callback.onSuccess(maps);
} else {
callback.onError(new Throwable());
}
}