PageRenderTime 66ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Library/CSObject.cs

https://bitbucket.org/digitalizarte/coolstorage
C# | 1275 lines | 862 code | 239 blank | 174 comment | 203 complexity | d8bf4e88a17993d02f91f20085a7da31 MD5 | raw file
  1. #region License
  2. //=============================================================================
  3. // Vici CoolStorage - .NET Object Relational Mapping Library
  4. //
  5. // Copyright (c) 2004-2009 Philippe Leybaert
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  23. // IN THE SOFTWARE.
  24. //=============================================================================
  25. #endregion
  26. using System;
  27. using System.Collections.Generic;
  28. using System.ComponentModel;
  29. using System.Data;
  30. using System.Linq;
  31. using System.Linq.Expressions;
  32. using System.Reflection;
  33. using System.Runtime.Serialization;
  34. using System.Text;
  35. using Vici.Core;
  36. namespace Vici.CoolStorage
  37. {
  38. internal enum CSObjectDataState
  39. {
  40. New, KeysLoaded, Loaded, Modified, Deleted, MarkedForDelete
  41. }
  42. internal class PrefetchField
  43. {
  44. public CSSchemaField SchemaField;
  45. public Dictionary<string, string> AliasMap = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
  46. }
  47. [Serializable]
  48. public abstract class CSObject : IEquatable<CSObject>, ISerializable, INotifyPropertyChanged
  49. {
  50. private CSFieldValueCollection _fieldData;
  51. internal CSFieldValueCollection FieldData
  52. {
  53. get { return _fieldData; }
  54. }
  55. private CSSchema _schema;
  56. private CSObjectDataState _dataState;
  57. public event PropertyChangedEventHandler PropertyChanged;
  58. internal CSObject()
  59. {
  60. Initialize();
  61. }
  62. protected void Deserialize(SerializationInfo info, StreamingContext context)
  63. {
  64. _dataState = (CSObjectDataState)info.GetValue("DataState", typeof(CSObjectDataState));
  65. foreach (CSFieldValue fieldValue in _fieldData)
  66. {
  67. fieldValue.ValueState = (CSFieldValueState)info.GetValue("FieldState_" + fieldValue.SchemaField.Name, typeof(CSFieldValueState));
  68. fieldValue.ValueDirect = info.GetValue("FieldValue_" + fieldValue.SchemaField.Name, typeof(object));
  69. if (fieldValue.SchemaField.Relation != null && (fieldValue.SchemaField.Relation.RelationType == CSSchemaRelationType.OneToMany || fieldValue.SchemaField.Relation.RelationType == CSSchemaRelationType.ManyToMany))
  70. {
  71. if (fieldValue.ValueDirect is CSList)
  72. {
  73. ((CSList)fieldValue.ValueDirect).Relation = fieldValue.SchemaField.Relation;
  74. ((CSList)fieldValue.ValueDirect).RelationObject = this;
  75. }
  76. }
  77. }
  78. }
  79. private void Initialize()
  80. {
  81. if (_schema == null)
  82. _schema = CSSchema.Get(GetType());
  83. _dataState = CSObjectDataState.New;
  84. _fieldData = new CSFieldValueCollection(this);
  85. }
  86. /// <summary>
  87. /// Overridden in object class to implement object initialization.
  88. /// </summary>
  89. /// <remarks>
  90. /// Would perfer this to be an abstract method, however, that would be a huge breaking change.
  91. /// </remarks>
  92. /// <returns></returns>
  93. public virtual CSObject InitializeNew()
  94. {
  95. return this;
  96. }
  97. internal CSSchema Schema
  98. {
  99. get
  100. {
  101. return _schema;
  102. }
  103. }
  104. internal CSFieldValueCollection Data
  105. {
  106. get
  107. {
  108. return _fieldData;
  109. }
  110. }
  111. internal CSObjectDataState DataState
  112. {
  113. get
  114. {
  115. return _dataState;
  116. }
  117. }
  118. internal void FromDataReader(ICSDbReader dataReader, Dictionary<string, string> aliasMap)
  119. {
  120. for (int i = 0; i < dataReader.FieldCount; i++)
  121. {
  122. string aliasName = dataReader.GetName(i);
  123. string fieldName;
  124. if (aliasMap == null)
  125. fieldName = aliasName;
  126. else if (!aliasMap.TryGetValue(aliasName, out fieldName))
  127. continue;
  128. CSSchemaColumn schemaColumn = _schema.Columns[fieldName];
  129. if (schemaColumn == null || schemaColumn.Hidden)
  130. continue;
  131. object columnValue = dataReader[i];
  132. if (columnValue is DBNull)
  133. columnValue = null;
  134. if (schemaColumn.MappedField.Trim && columnValue is string)
  135. columnValue = ((string)columnValue).Trim();
  136. CSFieldValue fieldValue = _fieldData["#" + schemaColumn.Name];
  137. fieldValue.ValueDirect = columnValue;
  138. fieldValue.ValueState = CSFieldValueState.Read;
  139. }
  140. _dataState = CSObjectDataState.Loaded;
  141. }
  142. internal bool ReadFields(CSStringCollection columnList, CSStringCollection keyList, List<object> valueList)
  143. {
  144. List<string> fieldList = new List<string>();
  145. List<string> aliasList = new List<string>();
  146. Dictionary<string, string> fieldAliasMap = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
  147. CSFilter whereClause = new CSFilter();
  148. CSTable table = new CSTable(_schema);
  149. CSParameterCollection parameters = new CSParameterCollection();
  150. foreach (CSSchemaColumn schemaColumn in _schema.Columns)
  151. {
  152. if (keyList.Contains(schemaColumn.Name))
  153. {
  154. CSParameter parameter = parameters.Add();
  155. parameter.Value = valueList[keyList.IndexOf(schemaColumn.Name)];
  156. whereClause = whereClause.And(table.TableAlias + "." + _schema.DB.QuoteField(schemaColumn.Name) + "=" + parameter.Name);
  157. _fieldData["#" + schemaColumn.Name].ValueDirect = parameter.Value;
  158. _fieldData["#" + schemaColumn.Name].ValueState = CSFieldValueState.Read;
  159. }
  160. else if (columnList.Contains(schemaColumn.Name))
  161. {
  162. string alias = CSNameGenerator.NextFieldAlias;
  163. fieldList.Add(table.TableAlias + "." + schemaColumn.Name);
  164. aliasList.Add(alias);
  165. fieldAliasMap[alias] = schemaColumn.Name;
  166. }
  167. }
  168. /** Build query for prefetch of relations **/
  169. CSJoinList joinList = new CSJoinList();
  170. List<PrefetchField> prefetchFields = GetPrefetchFieldsOne(table, fieldList, aliasList, joinList, null);
  171. if (whereClause.Expression.Length == 0)
  172. return false;
  173. if (fieldList.Count == 0)
  174. return true;
  175. string sqlQuery = _schema.DB.BuildSelectSQL(table.TableName, table.TableAlias, fieldList.ToArray(), aliasList.ToArray(), joinList.BuildJoinExpressions(), whereClause.Expression, null, 1, 1, true, false);
  176. using (CSTransaction csTransaction = new CSTransaction(_schema))
  177. {
  178. using (ICSDbReader dataReader = _schema.DB.CreateReader(sqlQuery, parameters))
  179. {
  180. if (!dataReader.Read())
  181. return false;
  182. FromDataReader(dataReader, fieldAliasMap);
  183. foreach (PrefetchField prefetchField in prefetchFields)
  184. ReadRelationToOne(prefetchField.SchemaField, dataReader, prefetchField.AliasMap);
  185. }
  186. csTransaction.Commit();
  187. }
  188. return true;
  189. }
  190. internal static List<PrefetchField> GetPrefetchFieldsOne(CSTable table, List<string> fieldList, List<string> aliasList, CSJoinList joinList, string[] prefetchPaths)
  191. {
  192. List<PrefetchField> prefetchFields = new List<PrefetchField>();
  193. foreach (CSSchemaField schemaField in table.Schema.Fields)
  194. {
  195. bool prefetch = schemaField.Prefetch;
  196. string fieldName = schemaField.Name;
  197. prefetch |= (prefetchPaths != null && prefetchPaths.Any(s =>
  198. {
  199. if (s.IndexOf('.') > 0)
  200. s = s.Substring(0,s.IndexOf('.'));
  201. return s == fieldName;
  202. }));
  203. if (schemaField.Relation != null && schemaField.Relation.RelationType == CSSchemaRelationType.ManyToOne && prefetch)
  204. {
  205. CSJoin join = new CSJoin(schemaField.Relation, table.TableAlias);
  206. joinList.Add(join);
  207. PrefetchField prefetchField = new PrefetchField();
  208. prefetchField.SchemaField = schemaField;
  209. foreach (string columnName in schemaField.Relation.ForeignSchema.ColumnsToRead)
  210. {
  211. string alias = CSNameGenerator.NextFieldAlias;
  212. fieldList.Add(join.RightAlias + "." + columnName);
  213. aliasList.Add(alias);
  214. prefetchField.AliasMap[alias] = columnName;
  215. }
  216. prefetchFields.Add(prefetchField);
  217. }
  218. }
  219. return prefetchFields;
  220. }
  221. private bool ReadFields(CSStringCollection columnList)
  222. {
  223. CSStringCollection keyList = new CSStringCollection();
  224. List<object> valueList = new List<object>();
  225. foreach (CSSchemaColumn schemaColumn in _schema.Columns)
  226. {
  227. if (schemaColumn.IsKey)
  228. {
  229. keyList.Add(schemaColumn.Name);
  230. valueList.Add(_fieldData["#" + schemaColumn.Name].ValueDirect);
  231. }
  232. }
  233. return ReadFields(columnList, keyList, valueList);
  234. }
  235. internal bool Read()
  236. {
  237. bool ok = ReadFields(_schema.ColumnsToRead);
  238. if (ok)
  239. _dataState = CSObjectDataState.Loaded;
  240. return ok;
  241. }
  242. internal bool ReadField(CSSchemaField schemaField)
  243. {
  244. if (schemaField.Relation != null)
  245. {
  246. if (schemaField.Relation.RelationType == CSSchemaRelationType.OneToMany || schemaField.Relation.RelationType == CSSchemaRelationType.ManyToMany)
  247. ReadRelationToMany(schemaField);
  248. else
  249. ReadRelationToOne(schemaField, null, null);
  250. return true;
  251. }
  252. else
  253. {
  254. return ReadFields(new CSStringCollection(schemaField.MappedColumn.Name));
  255. }
  256. }
  257. internal void ReadRelationToOne(CSSchemaField schemaField, ICSDbReader dataReader, Dictionary<string, string> aliasMap)
  258. {
  259. try
  260. {
  261. CSFieldValue fieldLocal = _fieldData["#" + schemaField.Relation.LocalKey];
  262. bool notIsNull = true;
  263. List<object> values = new List<object>(schemaField.Relation.LocalKeys.Count);
  264. schemaField.Relation.LocalKeys.ForEach<string>(key =>
  265. {
  266. //CSFieldValue fieldLocal = Data[String.Format("#{0}", key)];
  267. fieldLocal = Data[String.Format("#{0}", key)];
  268. values.Add(fieldLocal.Value);
  269. notIsNull = notIsNull && (fieldLocal.Value != null);
  270. });
  271. //CSFieldValue fieldLocal = Data[String.Format("#{0}", schemaField.Relation.LocalKey)];
  272. //if (fieldLocal.Value != null)
  273. if (notIsNull)
  274. {
  275. CSObject relationObject = CSFactory.New(schemaField.FieldType);
  276. if (dataReader != null)
  277. {
  278. relationObject.FromDataReader(dataReader, aliasMap);
  279. }
  280. //else if (!relationObject.ReadUsingUniqueKey(schemaField.Relation.ForeignKey, fieldLocal.Value))
  281. else if (!relationObject.ReadUsingUniqueKey(schemaField.Relation.ForeignKey, values.ToArray()))
  282. {
  283. throw new CSException("Relation " + schemaField.Name + " could not be read");
  284. }
  285. _fieldData[schemaField.Name].ValueDirect = relationObject;
  286. _fieldData[schemaField.Name].ValueState = CSFieldValueState.Read;
  287. }
  288. else
  289. {
  290. _fieldData[schemaField.Name].ValueDirect = null;
  291. _fieldData[schemaField.Name].ValueState = CSFieldValueState.Read;
  292. }
  293. }
  294. catch (TargetInvocationException ex)
  295. {
  296. throw ex.InnerException;
  297. }
  298. }
  299. private void ReadRelationToMany(CSSchemaField schemaField)
  300. {
  301. // if (_schema.KeyColumns.Count != 1)
  302. // throw new CSException("...ToMany only supported with single primary key");
  303. //
  304. try
  305. {
  306. CSList relationCollection = (CSList)Activator.CreateInstance(schemaField.FieldType);
  307. relationCollection.Relation = schemaField.Relation;
  308. relationCollection.RelationObject = this;
  309. _fieldData[schemaField.Name].ValueDirect = relationCollection;
  310. _fieldData[schemaField.Name].ValueState = CSFieldValueState.Read;
  311. }
  312. catch (TargetInvocationException ex)
  313. {
  314. throw ex.InnerException;
  315. }
  316. }
  317. /// <summary>
  318. ///
  319. /// </summary>
  320. /// <returns>bool</returns>
  321. /// <see cref="Save"/>
  322. public bool Modify()
  323. {
  324. _dataState = CSObjectDataState.Modified;
  325. return Save();
  326. }
  327. /// <summary>
  328. ///
  329. /// </summary>
  330. /// <returns>bool</returns>
  331. /// /// <see cref="Save"/>
  332. public bool Insert()
  333. {
  334. _dataState = CSObjectDataState.New;
  335. return Save();
  336. }
  337. /// <summary>
  338. /// Saves the object to the database
  339. /// </summary>
  340. /// <returns>true if the object was saved successfully, otherwise false</returns>
  341. /// <remarks>
  342. /// The object will be saved together with all child objects which have been changed. If the object was new, it will be created in the database.
  343. /// If required, any default data which has been filled in by the database server can be retrieved after the object is saved. This includes the primary key
  344. /// that has been generated by the database server (for autonumber/identity fields)
  345. /// </remarks>
  346. public virtual bool Save()
  347. {
  348. bool cancelSave = false;
  349. if (!SaveHierarchy())
  350. {
  351. return false;
  352. }
  353. //------ Check for client canceling save ----------------//
  354. // Raise save events and check for cancel;
  355. cancelSave = OnObjectSaving();
  356. if (cancelSave)
  357. return false;
  358. //----------------------------------------------------------//
  359. return TransactionSave(_dataState);
  360. }
  361. private bool SaveHierarchy()
  362. {
  363. bool saveOk = true;
  364. SubClassAttribute subClassAttr = this.GetType().GetAttribute<SubClassAttribute>(true);
  365. if (subClassAttr != null)
  366. {
  367. PropertyInfo subClassPropertyInfo = this.GetType().GetProperty(
  368. subClassAttr.SuperClassType.Name,
  369. subClassAttr.SuperClassType);
  370. CSObject subClassObject = (CSObject)subClassPropertyInfo.GetValue(this, null);
  371. if (subClassObject != null)
  372. {
  373. saveOk = subClassObject.Save();
  374. }
  375. }
  376. return saveOk;
  377. }
  378. private bool OnObjectSaving()
  379. {
  380. bool cancelSave = false;
  381. Fire_ObjectSaving(ref cancelSave);
  382. if (!cancelSave)
  383. {
  384. if (_dataState == CSObjectDataState.New)
  385. Fire_ObjectCreating(ref cancelSave);
  386. else
  387. Fire_ObjectUpdating(ref cancelSave);
  388. }
  389. return cancelSave;
  390. }
  391. /// <summary>
  392. /// Saves the object to the database
  393. /// </summary>
  394. /// <returns>true if the object was saved successfully, otherwise false</returns>
  395. /// <remarks>
  396. /// The object will be saved together with all child objects which have been changed. If the object was new, it will be created in the database.
  397. /// If required, any default data which has been filled in by the database server can be retrieved after the object is saved. This includes the primary key
  398. /// that has been generated by the database server (for autonumber/identity fields)
  399. /// </remarks>
  400. private bool TransactionSave(CSObjectDataState oldState)
  401. //public bool Save()
  402. {
  403. //bool cancelSave = false;
  404. //Fire_ObjectSaving(ref cancelSave);
  405. //if (cancelSave)
  406. // return false;
  407. //if (_dataState == CSObjectDataState.New)
  408. // Fire_ObjectCreating(ref cancelSave);
  409. //else
  410. // Fire_ObjectUpdating(ref cancelSave);
  411. //CSObjectDataState oldState = _dataState;
  412. //if (cancelSave)
  413. // return false;
  414. using (CSTransaction csTransaction = new CSTransaction(_schema, IsolationLevel.ReadUncommitted))
  415. {
  416. bool saveOk = SaveChildrenBefore();
  417. if (saveOk)
  418. {
  419. if (_dataState == CSObjectDataState.MarkedForDelete)
  420. {
  421. saveOk = Delete();
  422. }
  423. else
  424. {
  425. if (_dataState == CSObjectDataState.New)
  426. saveOk = Create();
  427. else
  428. saveOk = Write();
  429. }
  430. }
  431. if (saveOk)
  432. saveOk = SaveChildrenAfter();
  433. if (saveOk)
  434. csTransaction.Commit();
  435. else
  436. csTransaction.Rollback();
  437. if (saveOk)
  438. {
  439. if (oldState == CSObjectDataState.New)
  440. {
  441. Fire_ObjectCreated();
  442. Reload();
  443. }
  444. else
  445. {
  446. Fire_ObjectUpdated();
  447. }
  448. Fire_ObjectSaved();
  449. }
  450. return saveOk;
  451. }
  452. }
  453. private bool SaveChildrenBefore()
  454. {
  455. foreach (CSFieldValue fieldValue in _fieldData)
  456. {
  457. if (fieldValue.SchemaField != null && fieldValue.SchemaField.Relation != null)
  458. {
  459. CSRelation relation = fieldValue.SchemaField.Relation;
  460. if (fieldValue.IsDirty)
  461. {
  462. switch (relation.RelationType)
  463. {
  464. case CSSchemaRelationType.OneToMany:
  465. {
  466. break;
  467. }
  468. case CSSchemaRelationType.ManyToMany:
  469. {
  470. break;
  471. }
  472. case CSSchemaRelationType.OneToOne:
  473. {
  474. goto case CSSchemaRelationType.ManyToOne;
  475. }
  476. case CSSchemaRelationType.ManyToOne: // Set local keys to correct values
  477. {
  478. if (fieldValue.Value == null)
  479. {
  480. //if (_fieldData["#" + relation.LocalKey].SchemaField.MappedColumn.AllowNull)
  481. // _fieldData["#" + relation.LocalKey].Value = null;
  482. //else
  483. // throw new CSException("Column [" + fieldValue.SchemaField.MappedColumn.Name + "] cannot be set to null");
  484. for (int i = 0; i < relation.LocalKeys.Count; i++)
  485. {
  486. if (Data[String.Format("#{0}", relation.LocalKeys[i])].SchemaField.MappedColumn.AllowNull)
  487. Data[String.Format("#{0}", relation.LocalKeys[i])].Value = null;
  488. else
  489. throw new CSException(String.Format("Column [{0}] cannot be set to null", fieldValue.SchemaField.MappedColumn.Name));
  490. }
  491. }
  492. else
  493. {
  494. CSObject valueObj = (CSObject)fieldValue.Value;
  495. if (valueObj.IsNew || valueObj.IsDirty)
  496. valueObj.Save();
  497. //_fieldData["#" + relation.LocalKey].Value = valueObj.Data["#" + relation.ForeignKey].Value;
  498. for (int i = 0; i < relation.LocalKeys.Count; i++)
  499. {
  500. Data[String.Format("#{0}", relation.LocalKeys[i])].Value =
  501. valueObj.Data[String.Format("#{0}", relation.ForeignKeys[i])].Value;
  502. }
  503. }
  504. break;
  505. }
  506. }
  507. }
  508. else
  509. {
  510. if (fieldValue.ValueState != CSFieldValueState.Unread)
  511. {
  512. if (fieldValue.BaseType.IsSubclassOf(typeof(CSObject)))
  513. {
  514. CSObject obj = (CSObject)fieldValue.ValueDirect;
  515. if (obj != null && !obj.Save())
  516. return false;
  517. }
  518. }
  519. }
  520. }
  521. }
  522. return true;
  523. }
  524. private bool SaveChildrenAfter()
  525. {
  526. foreach (CSFieldValue fieldValue in _fieldData)
  527. {
  528. if (fieldValue.SchemaField != null && fieldValue.SchemaField.Relation != null)
  529. {
  530. CSRelation relation = fieldValue.SchemaField.Relation;
  531. if (relation.RelationType == CSSchemaRelationType.OneToMany || (relation.RelationType == CSSchemaRelationType.ManyToMany && relation.PureManyToMany))
  532. {
  533. ((CSList)fieldValue.Value).Save();
  534. }
  535. }
  536. }
  537. return true;
  538. }
  539. private bool Write()
  540. {
  541. if (!_fieldData.IsDirty)
  542. return true;
  543. if (_dataState == CSObjectDataState.Deleted)
  544. return false;
  545. List<string> fieldNames = new List<string>();
  546. List<string> fieldValues = new List<string>();
  547. CSFilter whereClause = new CSFilter();
  548. CSParameterCollection parameters = new CSParameterCollection();
  549. foreach (CSSchemaColumn schemaColumn in _schema.Columns)
  550. {
  551. CSFieldValue fieldValue = _fieldData["#" + schemaColumn.Name];
  552. if (!schemaColumn.IsKey && (fieldValue == null || !fieldValue.IsDirty || fieldValue.SchemaField.ReadOnly))
  553. continue;
  554. fieldValue.ValueState = CSFieldValueState.Read;
  555. CSParameter parameter = parameters.Add();
  556. if (fieldValue.BaseType == typeof(DateTime))
  557. {
  558. parameter.Value = ((DateTime)fieldValue.ValueDirect).ToString("yyyy-MM-dd HH:mm:ss");
  559. }
  560. else
  561. {
  562. parameter.Value = fieldValue.ValueDirect;
  563. }
  564. if (schemaColumn.IsKey)
  565. {
  566. whereClause = whereClause.And(_schema.DB.QuoteField(schemaColumn.Name) + "=@" + parameter.Name.Substring(1));
  567. }
  568. else
  569. {
  570. fieldNames.Add(schemaColumn.Name);
  571. fieldValues.Add("@" + parameter.Name.Substring(1));
  572. }
  573. }
  574. if (whereClause.Expression.Length == 0)
  575. throw new CSException("No key fields");
  576. /*if (fieldValues.Count > 0)
  577. {
  578. string sqlQuery = Schema.DB.BuildUpdateSQL(Schema.TableName, fieldNames.ToArray(), fieldValues.ToArray(), whereClause.Expression);
  579. if (Schema.DB.ExecuteNonQuery(sqlQuery, parameters) != 1)
  580. return false;
  581. }
  582. return true;*/
  583. return Schema.DB.ExecuteUpdate(Schema.TableName, fieldNames.ToArray(), fieldValues.ToArray(), parameters, whereClause);
  584. }
  585. private bool Create()
  586. {
  587. CSParameterCollection parameters = new CSParameterCollection();
  588. List<string> fieldNames = new List<string>();
  589. List<string> fieldValues = new List<string>();
  590. List<string> sequenceNames = new List<string>();
  591. foreach (CSSchemaColumn schemaColumn in _schema.Columns)
  592. {
  593. CSFieldValue fieldValue = _fieldData["#" + schemaColumn.Name];
  594. if (fieldValue == null)
  595. continue;
  596. CSSchemaField schemaField = fieldValue.SchemaField;
  597. if (schemaField.ClientGenerated && schemaColumn.IsKey)
  598. {
  599. if (schemaField.FieldType == typeof(Guid))
  600. {
  601. fieldValue.Value = Guid.NewGuid();
  602. }
  603. }
  604. else
  605. {
  606. if (string.IsNullOrEmpty(schemaField.SequenceName) && (!fieldValue.IsDirty || schemaField.ReadOnly || schemaField.NoCreate || schemaColumn.Identity))
  607. continue;
  608. }
  609. if (schemaField.ServerGenerated && schemaColumn.IsKey)
  610. continue;
  611. if (!string.IsNullOrEmpty(schemaField.SequenceName))
  612. {
  613. sequenceNames.Add(schemaField.SequenceName);
  614. fieldValues.Add(null);
  615. }
  616. else
  617. {
  618. CSParameter parameter = parameters.Add();
  619. if (fieldValue.BaseType == typeof(DateTime))
  620. {
  621. parameter.Value = ((DateTime)fieldValue.ValueDirect).ToString("yyyy-MM-dd HH:mm:ss");
  622. }
  623. else
  624. {
  625. parameter.Value = fieldValue.ValueDirect;
  626. }
  627. fieldValues.Add("@" + parameter.Name.Substring(1));
  628. sequenceNames.Add(null);
  629. }
  630. fieldNames.Add(schemaColumn.Name);
  631. }
  632. string[] primaryKeys = new string[_schema.KeyColumns.Count];
  633. for (int i = 0; i < _schema.KeyColumns.Count; i++)
  634. primaryKeys[i] = _schema.KeyColumns[i].Name;
  635. string identityField = (_schema.IdentityColumn != null && _schema.IdentityColumn.MappedField != null) ? _schema.IdentityColumn.Name : null;
  636. //using (ICSDbReader reader = _schema.DB.ExecuteInsert(_schema.TableName, fieldNames.ToArray(), fieldValues.ToArray(), primaryKeys, sequenceNames.ToArray(), (_schema.IdentityColumn != null && _schema.IdentityColumn.MappedField != null) ? _schema.IdentityColumn.Name : null, parameters))
  637. using (ICSDbReader reader = _schema.DB.ExecuteInsert(_schema.TableName, fieldNames.ToArray(), fieldValues.ToArray(), primaryKeys, sequenceNames.ToArray(), identityField, parameters))
  638. {
  639. if (reader != null && !reader.IsClosed && reader.Read())
  640. {
  641. FromDataReader(reader, null);
  642. OnIdentityCreated(identityField);
  643. }
  644. }
  645. _dataState = CSObjectDataState.Loaded;
  646. return true;
  647. }
  648. private void OnIdentityCreated(string identityField)
  649. {
  650. if (IdentityCreated != null)
  651. {
  652. IdentityCreated(this, new EventArgs());
  653. }
  654. }
  655. public event EventHandler IdentityCreated;
  656. /// <summary>
  657. /// Reloads the object from the database, overwriting any changes you have made to the object
  658. /// </summary>
  659. public void Reload()
  660. {
  661. Fire_ObjectReading();
  662. foreach (CSFieldValue fieldValue in _fieldData)
  663. if (fieldValue.SchemaField.MappedColumn == null || !fieldValue.SchemaField.MappedColumn.IsKey)
  664. fieldValue.Reset();
  665. _dataState = CSObjectDataState.KeysLoaded;
  666. }
  667. internal object PrimaryKeyValue
  668. {
  669. get
  670. {
  671. if (_schema.KeyColumns.Count != 1)
  672. {
  673. return null;
  674. }
  675. else
  676. {
  677. return _fieldData["#" + _schema.KeyColumns[0].Name].ValueDirect;
  678. }
  679. }
  680. }
  681. internal string PrimaryKeyValues
  682. {
  683. get
  684. {
  685. if (Schema.KeyColumns.Count > 1)
  686. {
  687. string ret = "";
  688. foreach (CSSchemaColumn col in Schema.KeyColumns)
  689. ret += (string)Data[String.Format("#{0}", col.Name)].ValueDirect;
  690. return ret;
  691. }
  692. else
  693. {
  694. return null;
  695. }
  696. }
  697. }
  698. internal bool Read(object[] primaryKeys)
  699. {
  700. if (primaryKeys.Length == 0)
  701. throw new CSException("Read() requires parameters");
  702. Initialize();
  703. CSStringCollection columnNames = new CSStringCollection();
  704. List<object> keyValues = new List<object>();
  705. if (primaryKeys.Length != _schema.KeyColumns.Count)
  706. throw new CSException(GetType().Name + ".Read(..keys..) called with " + primaryKeys.Length + " parameters, but there are " + _schema.KeyColumns.Count + " key fields defined");
  707. for (int i = 0; i < primaryKeys.Length; i++)
  708. {
  709. columnNames.Add(_schema.KeyColumns[i].Name);
  710. keyValues.Add(primaryKeys[i]);
  711. }
  712. if (!ReadFields(_schema.ColumnsToRead, columnNames, keyValues))
  713. return false;
  714. _dataState = CSObjectDataState.Loaded;
  715. Fire_ObjectRead();
  716. return true;
  717. }
  718. protected bool ReadUsingUniqueKey(string uniqueColumnName, object value)
  719. {
  720. Initialize();
  721. Fire_ObjectReading();
  722. string[] ucl = uniqueColumnName.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
  723. //StringCollection keyList = new CSStringCollection(uniqueColumnName);
  724. CSStringCollection keyList = new CSStringCollection(ucl);
  725. List<object> valueList = new List<object>();
  726. if (value != null && value.GetType().IsArray)
  727. {
  728. ((object[])value).ForEach<object>(obj => valueList.Add(obj));
  729. }
  730. else
  731. {
  732. valueList.Add(value);
  733. }
  734. if (ReadFields(_schema.ColumnsToRead, keyList, valueList))
  735. {
  736. _dataState = CSObjectDataState.Loaded;
  737. Fire_ObjectRead();
  738. return true;
  739. }
  740. return false;
  741. }
  742. /// <summary>
  743. /// Fuerza el borrado de un objeto
  744. /// </summary>
  745. /// <returns></returns>
  746. public bool ForceDelete()
  747. {
  748. _dataState = CSObjectDataState.Loaded;
  749. return Delete();
  750. }
  751. /// <summary>
  752. /// Physically deletes the object from the database
  753. /// </summary>
  754. /// <returns><c>true</c> if the object was deleted successfully. Otherwise <c>false</c></returns>
  755. public bool Delete()
  756. {
  757. if (_dataState == CSObjectDataState.Deleted || _dataState == CSObjectDataState.New)
  758. return false;
  759. bool cancel = false;
  760. Fire_ObjectDeleting(ref cancel);
  761. if (cancel)
  762. return false;
  763. StringBuilder whereClause = new StringBuilder();
  764. CSParameterCollection parameters = new CSParameterCollection();
  765. foreach (CSSchemaColumn schemaColumn in _schema.Columns)
  766. {
  767. if (!schemaColumn.IsKey)
  768. continue;
  769. CSParameter parameter = parameters.Add();
  770. parameter.Value = _fieldData["#" + schemaColumn.Name].ValueDirect;
  771. if (whereClause.Length > 0)
  772. whereClause.Append(" and ");
  773. whereClause.Append(_schema.DB.QuoteField(schemaColumn.Name) + "=@" + parameter.Name.Substring(1));
  774. }
  775. if (whereClause.Length == 0)
  776. throw new CSException("No key fields");
  777. using (CSTransaction csTransaction = new CSTransaction(_schema))
  778. {
  779. string deleteSql = _schema.DB.BuildDeleteSQL(_schema.TableName, null, whereClause.ToString());
  780. int numDeleted = _schema.DB.ExecuteNonQuery(deleteSql, parameters);
  781. if (numDeleted == 1)
  782. csTransaction.Commit();
  783. else
  784. return false;
  785. }
  786. _dataState = CSObjectDataState.Deleted;
  787. Fire_ObjectDeleted();
  788. return true;
  789. }
  790. /// <summary>
  791. /// Mark the object for deletion. The object will be deleted next time it is saved
  792. /// </summary>
  793. public void MarkForDelete()
  794. {
  795. _dataState = CSObjectDataState.MarkedForDelete;
  796. }
  797. /// <summary>
  798. /// Marks for update.
  799. /// </summary>
  800. public void MarkForUpdate()
  801. {
  802. _dataState = CSObjectDataState.Modified;
  803. }
  804. /// <summary>
  805. /// Returns a <see cref="System.String"/> that represents the current <see cref="Vici.CoolStorage.CSObject"/>.
  806. /// </summary>
  807. /// <returns>
  808. /// A <see cref="System.String"/> that represents the current <see cref="Vici.CoolStorage.CSObject"/>.
  809. /// </returns>
  810. public override string ToString()
  811. {
  812. if (_schema.ToStringProperty != null)
  813. return _schema.ToStringProperty.GetValue(this, null).ToString();
  814. else
  815. return GetType().Name;
  816. }
  817. internal abstract void Fire_ObjectRead();
  818. internal abstract void Fire_ObjectReading();
  819. internal abstract void Fire_ObjectUpdating(ref bool cancel);
  820. internal abstract void Fire_ObjectUpdated();
  821. internal abstract void Fire_ObjectSaving(ref bool cancel);
  822. internal abstract void Fire_ObjectSaved();
  823. internal abstract void Fire_ObjectCreated();
  824. internal abstract void Fire_ObjectCreating(ref bool cancel);
  825. internal abstract void Fire_ObjectDeleting(ref bool cancel);
  826. internal abstract void Fire_ObjectDeleted();
  827. /// <summary>
  828. /// Gets a value indicating whether this object is new (not saved yet)
  829. /// </summary>
  830. /// <value><c>true</c> if this instance is new (not saved yet); otherwise, <c>false</c>.</value>
  831. public bool IsNew
  832. {
  833. get
  834. {
  835. return _dataState == CSObjectDataState.New;
  836. }
  837. }
  838. /// <summary>
  839. /// Gets a value indicating whether this object is deleted by a previous call to Delete()
  840. /// </summary>
  841. /// <value><c>true</c> if this instance is deleted; otherwise, <c>false</c>.</value>
  842. public bool IsDeleted
  843. {
  844. get
  845. {
  846. return _dataState == CSObjectDataState.Deleted;
  847. }
  848. }
  849. /// <summary>
  850. /// Gets a value indicating whether this object is marked for deletion by a previous call to MarkForDelete()
  851. /// </summary>
  852. /// <value><c>true</c> if this instance is marked for deletion; otherwise, <c>false</c>.</value>
  853. public bool IsMarkedForDelete
  854. {
  855. get
  856. {
  857. return _dataState == CSObjectDataState.MarkedForDelete;
  858. }
  859. }
  860. /// <summary>
  861. /// Gets a value indicating whether this object has been modified
  862. /// </summary>
  863. /// <value><c>true</c> if this object has been modified; otherwise, <c>false</c>.</value>
  864. public bool IsDirty
  865. {
  866. get
  867. {
  868. return _dataState == CSObjectDataState.Modified || _dataState == CSObjectDataState.MarkedForDelete || _fieldData.IsDirty;
  869. }
  870. }
  871. public bool IsPropertyDirty(string propertyName)
  872. {
  873. var value =_fieldData[propertyName];
  874. return (value != null) && (value.IsDirty);
  875. }
  876. // /// <summary>
  877. // /// Gets or sets the specified field (by field name)
  878. // /// </summary>
  879. // /// <value>The field value</value>
  880. // public object this[string fieldName]
  881. // {
  882. // get
  883. // {
  884. // int dotIndex = fieldName.IndexOf('.');
  885. // string rootField = fieldName;
  886. //
  887. // if (dotIndex > 0)
  888. // rootField = fieldName.Substring(0,dotIndex);
  889. //
  890. // object value = GetField(rootField);
  891. //
  892. // if (dotIndex > 0 && value is CSObject)
  893. // return (value as CSObject)[fieldName.Substring(dotIndex+1)];
  894. // else
  895. // return value;
  896. // }
  897. // set
  898. // {
  899. // SetField(fieldName,value);
  900. // }
  901. // }
  902. #region Protected Field Reading Methods
  903. protected T Get<T>(Expression<Func<T>> property)
  904. {
  905. return GetField(property);
  906. }
  907. protected T GetField<T>(Expression<Func<T>> property)
  908. {
  909. if (property == null)
  910. {
  911. throw new ArgumentException("cannot be null", "property");
  912. }
  913. var name = property.PropertyName();
  914. return (T)GetField(name);
  915. }
  916. protected object GetField(string fieldName)
  917. {
  918. CSFieldValue fieldValue = _fieldData[fieldName];
  919. if (fieldValue == null)
  920. throw new CSException("Type [" + GetType().Name + "] does not contain property [" + fieldName + "]");
  921. object value = fieldValue.Value;
  922. if (value == null)
  923. return fieldValue.SchemaField.NullValue;
  924. else
  925. return value;
  926. }
  927. // protected T GetField<T>(string fieldName)
  928. // {
  929. // return (T) GetField(fieldName);
  930. // }
  931. protected void SetField(string fieldName, object value)
  932. {
  933. CSFieldValue fieldValue = _fieldData[fieldName];
  934. if (fieldValue == null)
  935. throw new CSException("Type [" + GetType().Name + "] does not contain property [" + fieldName + "]");
  936. PropertyChangedEventArgs e = new PropertyChangedEventArgs(fieldName);
  937. fieldValue.Value = value;
  938. if (PropertyChanged != null)
  939. PropertyChanged(this, e);
  940. }
  941. protected void _S(string fieldName, object value)
  942. {
  943. SetField(fieldName,value);
  944. }
  945. protected T _G<T>(string fieldName)
  946. {
  947. return (T) GetField(fieldName);
  948. }
  949. #endregion
  950. #region Equality tests
  951. public override bool Equals(object obj)
  952. {
  953. if (obj is CSObject && obj.GetType() == GetType())
  954. {
  955. CSObject objData = (CSObject)obj;
  956. foreach (CSSchemaColumn schemaColumn in _schema.KeyColumns)
  957. {
  958. object value1 = _fieldData["#" + schemaColumn.Name].Value;
  959. object value2 = objData._fieldData["#" + schemaColumn.Name].Value;
  960. if (value1 == null || value2 == null || !value1.Equals(value2))
  961. return false;
  962. }
  963. return true;
  964. }
  965. return base.Equals(obj);
  966. }
  967. public override int GetHashCode()
  968. {
  969. int iHash = 0;
  970. if (IsNew)
  971. return GetType().GetHashCode();
  972. foreach (CSSchemaColumn schemaColumn in _schema.KeyColumns)
  973. iHash += _fieldData["#" + schemaColumn.Name].Value.GetHashCode();
  974. return iHash;
  975. }
  976. public static bool operator ==(CSObject x, CSObject y)
  977. {
  978. if ((object)x == null && (object)y == null)
  979. return true;
  980. if ((object)x == null)
  981. return y.Equals(x);
  982. else
  983. return x.Equals(y);
  984. }
  985. public static bool operator !=(CSObject x, CSObject y)
  986. {
  987. return !(x == y);
  988. }
  989. bool IEquatable<CSObject>.Equals(CSObject other)
  990. {
  991. return Equals(other);
  992. }
  993. #endregion
  994. #if !WINDOWS_PHONE && !SILVERLIGHT
  995. #region ISerializable Members
  996. void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  997. {
  998. info.AddValue("DataState", _dataState);
  999. foreach (CSFieldValue fieldValue in _fieldData)
  1000. {
  1001. //if (fieldValue.SchemaField.Relation != null && (fieldValue.SchemaField.Relation.RelationType == CSSchemaRelationType.OneToMany || fieldValue.SchemaField.Relation.RelationType == CSSchemaRelationType.ManyToMany))
  1002. // continue;
  1003. info.AddValue("FieldState_" + fieldValue.SchemaField.Name, fieldValue.ValueState);
  1004. info.AddValue("FieldValue_" + fieldValue.SchemaField.Name, fieldValue.ValueDirect);
  1005. }
  1006. }
  1007. #endregion
  1008. #endif
  1009. public CSPrimaryKey PrimaryKey()
  1010. {
  1011. return CSPrimaryKey.PrimaryKey(this);
  1012. }
  1013. /// <summary>
  1014. /// Devuelve el valor de todas las claves de la Tabla concatenadas como String
  1015. /// </summary>
  1016. /// <returns></returns>
  1017. public string GetKeysValue()
  1018. {
  1019. string ret = "";
  1020. try
  1021. {
  1022. //if (DataState != CSObjectDataState.New)
  1023. foreach (CSSchemaColumn col in Schema.KeyColumns)
  1024. ret += Convert.ToString(Data[String.Format("#{0}", col.Name)].Value);
  1025. //ret += (string)Data[String.Format("#{0}", col.Name)].Value;
  1026. }
  1027. catch
  1028. {
  1029. throw;
  1030. }
  1031. return ret;
  1032. }
  1033. }
  1034. }