2 // $Id: PgProfileProvider.cs 36 2007-11-24 09:44:42Z dna $
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Copyright © 2006, 2007 Nauck IT KG http://www.nauck-it.de
26 // Daniel Nauck <d.nauck(at)nauck-it.de>
28 // Adapted to Sqlite by Marek Habersack <mhabersack@novell.com>
33 using System.Data.Common;
34 using System.Configuration;
35 using System.Configuration.Provider;
36 using System.Collections.Generic;
37 using System.Collections.Specialized;
38 using System.Diagnostics;
40 using System.Web.Hosting;
41 using System.Web.Util;
43 using Mono.Data.Sqlite;
45 namespace System.Web.Profile
47 internal class SqliteProfileProvider : ProfileProvider
49 const string m_ProfilesTableName = "Profiles";
50 const string m_ProfileDataTableName = "ProfileData";
51 string m_ConnectionString = string.Empty;
53 SerializationHelper m_serializationHelper = new SerializationHelper();
55 DbParameter AddParameter (DbCommand command, string parameterName)
57 return AddParameter (command, parameterName, null);
60 DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
62 return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
65 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
67 DbParameter dbp = command.CreateParameter ();
68 dbp.ParameterName = parameterName;
69 dbp.Value = parameterValue;
70 dbp.Direction = direction;
71 command.Parameters.Add (dbp);
76 /// System.Configuration.Provider.ProviderBase.Initialize Method
78 public override void Initialize(string name, NameValueCollection config)
80 // Initialize values from web.config.
82 throw new ArgumentNullException("Config", Properties.Resources.ErrArgumentNull);
84 if (string.IsNullOrEmpty(name))
85 name = Properties.Resources.ProfileProviderDefaultName;
87 if (string.IsNullOrEmpty(config["description"]))
89 config.Remove("description");
90 config.Add("description", Properties.Resources.ProfileProviderDefaultDescription);
93 // Initialize the abstract base class.
94 base.Initialize(name, config);
96 m_ApplicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
98 // Get connection string.
99 string connStrName = config["connectionStringName"];
101 if (string.IsNullOrEmpty(connStrName))
103 throw new ArgumentOutOfRangeException("ConnectionStringName", Properties.Resources.ErrArgumentNullOrEmpty);
107 ConnectionStringSettings ConnectionStringSettings = ConfigurationManager.ConnectionStrings[connStrName];
109 if (ConnectionStringSettings == null || string.IsNullOrEmpty(ConnectionStringSettings.ConnectionString.Trim()))
111 throw new ProviderException(Properties.Resources.ErrConnectionStringNullOrEmpty);
114 m_ConnectionString = ConnectionStringSettings.ConnectionString;
119 /// System.Web.Profile.ProfileProvider properties.
121 #region System.Web.Security.ProfileProvider properties
122 string m_ApplicationName = string.Empty;
124 public override string ApplicationName
126 get { return m_ApplicationName; }
127 set { m_ApplicationName = value; }
132 /// System.Web.Profile.ProfileProvider methods.
134 #region System.Web.Security.ProfileProvider methods
137 /// ProfileProvider.DeleteInactiveProfiles
139 public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
141 throw new Exception("DeleteInactiveProfiles: The method or operation is not implemented.");
144 public override int DeleteProfiles(string[] usernames)
146 throw new Exception("DeleteProfiles1: The method or operation is not implemented.");
149 public override int DeleteProfiles(ProfileInfoCollection profiles)
151 throw new Exception("DeleteProfiles2: The method or operation is not implemented.");
154 public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
156 throw new Exception("FindInactiveProfilesByUserName: The method or operation is not implemented.");
159 public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
161 throw new Exception("FindProfilesByUserName: The method or operation is not implemented.");
164 public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
166 throw new Exception("GetAllInactiveProfiles: The method or operation is not implemented.");
169 public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
171 throw new Exception("GetAllProfiles: The method or operation is not implemented.");
174 public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
176 throw new Exception("GetNumberOfInactiveProfiles: The method or operation is not implemented.");
181 /// System.Configuration.SettingsProvider methods.
183 #region System.Web.Security.SettingsProvider methods
188 public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
190 SettingsPropertyValueCollection result = new SettingsPropertyValueCollection();
191 string username = (string)context["UserName"];
192 bool isAuthenticated = (bool)context["IsAuthenticated"];
193 Dictionary<string, object> databaseResult = new Dictionary<string, object>();
195 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
197 using (SqliteCommand dbCommand = dbConn.CreateCommand())
199 dbCommand.CommandText = string.Format("SELECT \"Name\", \"ValueString\", \"ValueBinary\" FROM \"{0}\" WHERE \"Profile\" = (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated)", m_ProfileDataTableName, m_ProfilesTableName);
201 AddParameter (dbCommand, "@Username", username);
202 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
203 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
210 using (SqliteDataReader reader = dbCommand.ExecuteReader())
212 while (reader.Read())
214 object resultData = null;
215 if(!reader.IsDBNull(1))
216 resultData = reader.GetValue(1);
217 else if(!reader.IsDBNull(2))
218 resultData = reader.GetValue(2);
220 databaseResult.Add(reader.GetString(0), resultData);
224 catch (SqliteException e)
226 Trace.WriteLine(e.ToString());
227 throw new ProviderException(Properties.Resources.ErrOperationAborted);
237 foreach (SettingsProperty item in collection)
239 if (item.SerializeAs == SettingsSerializeAs.ProviderSpecific)
241 if (item.PropertyType.IsPrimitive || item.PropertyType.Equals(typeof(string)))
242 item.SerializeAs = SettingsSerializeAs.String;
244 item.SerializeAs = SettingsSerializeAs.Xml;
247 SettingsPropertyValue itemValue = new SettingsPropertyValue(item);
249 if ((databaseResult.ContainsKey(item.Name)) && (databaseResult[item.Name] != null))
251 if(item.SerializeAs == SettingsSerializeAs.String)
252 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBase64((string)databaseResult[item.Name]);
254 else if (item.SerializeAs == SettingsSerializeAs.Xml)
255 itemValue.PropertyValue = m_serializationHelper.DeserializeFromXml((string)databaseResult[item.Name]);
257 else if (item.SerializeAs == SettingsSerializeAs.Binary)
258 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBinary((byte[])databaseResult[item.Name]);
260 itemValue.IsDirty = false;
261 result.Add(itemValue);
264 UpdateActivityDates(username, isAuthenticated, true);
269 public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
271 string username = (string)context["UserName"];
272 bool isAuthenticated = (bool)context["IsAuthenticated"];
274 if (collection.Count < 1)
277 if (!ProfileExists(username))
278 CreateProfileForUser(username, isAuthenticated);
280 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
282 using (SqliteCommand deleteCommand = dbConn.CreateCommand(),
283 insertCommand = dbConn.CreateCommand())
285 deleteCommand.CommandText = string.Format("DELETE FROM \"{0}\" WHERE \"Name\" = @Name AND \"Profile\" = (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated)", m_ProfileDataTableName, m_ProfilesTableName);
287 AddParameter (deleteCommand, "@Name");
288 AddParameter (deleteCommand, "@Username", username);
289 AddParameter (deleteCommand, "@ApplicationName", m_ApplicationName);
290 AddParameter (deleteCommand, "@IsAuthenticated", !isAuthenticated);
293 insertCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Profile\", \"Name\", \"ValueString\", \"ValueBinary\") VALUES (@pId, (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated), @Name, @ValueString, @ValueBinary)", m_ProfileDataTableName, m_ProfilesTableName);
295 AddParameter (insertCommand, "@pId");
296 AddParameter (insertCommand, "@Name");
297 AddParameter (insertCommand, "@ValueString");
298 insertCommand.Parameters["@ValueString"].IsNullable = true;
299 AddParameter (insertCommand, "@ValueBinary");
300 insertCommand.Parameters["@ValueBinary"].IsNullable = true;
301 AddParameter (insertCommand, "@Username", username);
302 AddParameter (insertCommand, "@ApplicationName", m_ApplicationName);
303 AddParameter (insertCommand, "@IsAuthenticated", !isAuthenticated);
305 SqliteTransaction dbTrans = null;
310 deleteCommand.Prepare();
311 insertCommand.Prepare();
313 using (dbTrans = dbConn.BeginTransaction())
316 foreach (SettingsPropertyValue item in collection)
321 deleteCommand.Parameters["@Name"].Value = item.Name;
323 insertCommand.Parameters["@pId"].Value = Guid.NewGuid().ToString();
324 insertCommand.Parameters["@Name"].Value = item.Name;
326 if (item.Property.SerializeAs == SettingsSerializeAs.String)
328 insertCommand.Parameters["@ValueString"].Value = m_serializationHelper.SerializeToBase64(item.PropertyValue);
329 insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
331 else if (item.Property.SerializeAs == SettingsSerializeAs.Xml)
333 item.SerializedValue = m_serializationHelper.SerializeToXml(item.PropertyValue);
334 insertCommand.Parameters["@ValueString"].Value = item.SerializedValue;
335 insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
337 else if (item.Property.SerializeAs == SettingsSerializeAs.Binary)
339 item.SerializedValue = m_serializationHelper.SerializeToBinary(item.PropertyValue);
340 insertCommand.Parameters["@ValueString"].Value = DBNull.Value; //string.Empty;//DBNull.Value;
341 insertCommand.Parameters["@ValueBinary"].Value = item.SerializedValue;
344 deleteCommand.ExecuteNonQuery();
345 insertCommand.ExecuteNonQuery();
348 UpdateActivityDates(username, isAuthenticated, false);
350 // Attempt to commit the transaction
354 catch (SqliteException e)
356 Trace.WriteLine(e.ToString());
360 // Attempt to roll back the transaction
361 Trace.WriteLine(Properties.Resources.LogRollbackAttempt);
364 catch (SqliteException re)
367 Trace.WriteLine(Properties.Resources.ErrRollbackFailed);
368 Trace.WriteLine(re.ToString());
371 throw new ProviderException(Properties.Resources.ErrOperationAborted);
383 #region private methods
385 /// Create a empty user profile
387 /// <param name="username"></param>
388 /// <param name="isAuthenticated"></param>
389 void CreateProfileForUser(string username, bool isAuthenticated)
391 if (ProfileExists(username))
393 throw new ProviderException(string.Format(Properties.Resources.ErrProfileAlreadyExist, username));
396 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
398 using (SqliteCommand dbCommand = dbConn.CreateCommand())
400 dbCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Username\", \"ApplicationName\", \"IsAnonymous\", \"LastActivityDate\", \"LastUpdatedDate\") Values (@pId, @Username, @ApplicationName, @IsAuthenticated, @LastActivityDate, @LastUpdatedDate)", m_ProfilesTableName);
402 AddParameter (dbCommand, "@pId", Guid.NewGuid().ToString());
403 AddParameter (dbCommand, "@Username", username);
404 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
405 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
406 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
407 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
414 dbCommand.ExecuteNonQuery();
416 catch (SqliteException e)
418 Trace.WriteLine(e.ToString());
419 throw new ProviderException(Properties.Resources.ErrOperationAborted);
431 bool ProfileExists(string username)
433 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
435 using (SqliteCommand dbCommand = dbConn.CreateCommand())
437 dbCommand.CommandText = string.Format("SELECT COUNT(*) FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_ProfilesTableName);
439 AddParameter (dbCommand, "@Username", username);
440 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
448 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out numRecs);
453 catch (SqliteException e)
455 Trace.WriteLine(e.ToString());
456 throw new ProviderException(Properties.Resources.ErrOperationAborted);
470 /// Updates the LastActivityDate and LastUpdatedDate values when profile properties are accessed by the
471 /// GetPropertyValues and SetPropertyValues methods.
472 /// Passing true as the activityOnly parameter will update only the LastActivityDate.
474 /// <param name="username"></param>
475 /// <param name="isAuthenticated"></param>
476 /// <param name="activityOnly"></param>
477 void UpdateActivityDates(string username, bool isAuthenticated, bool activityOnly)
479 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
481 using (SqliteCommand dbCommand = dbConn.CreateCommand())
485 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
487 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
491 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate, \"LastUpdatedDate\" = @LastUpdatedDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
493 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
494 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
497 AddParameter (dbCommand, "@Username", username);
498 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
499 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
506 dbCommand.ExecuteNonQuery();
508 catch (SqliteException e)
510 Trace.WriteLine(e.ToString());
511 throw new ProviderException(Properties.Resources.ErrOperationAborted);
523 /// A helper function to retrieve config values from the configuration file.
525 /// <param name="configValue"></param>
526 /// <param name="defaultValue"></param>
527 /// <returns></returns>
528 string GetConfigValue(string configValue, string defaultValue)
530 if (string.IsNullOrEmpty(configValue))