1163bf8f89187c6b907c10af4bcc32a52dab99b4
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / Utils / CommandHelper.cs
1 //---------------------------------------------------------------------
2 // <copyright file="CommandHelper.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.Common.Utils
11 {
12     using System.Data.EntityClient;
13     using System.Data.Metadata.Edm;
14     using System.Data.Spatial;
15     using System.Diagnostics;
16
17     /// <summary>
18     /// Contains utility methods for construction of DB commands through generic
19     /// provider interfaces.
20     /// </summary>
21     internal static class CommandHelper
22     {
23         /// <summary>
24         /// Consumes all rows and result sets from the reader. This allows client to retrieve
25         /// parameter values and intercept any store exceptions.
26         /// </summary>
27         /// <param name="reader">reader to consume</param>
28         internal static void ConsumeReader(DbDataReader reader)
29         {
30             if (null != reader && !reader.IsClosed)
31             {
32                 while (reader.NextResult())
33                 {
34                     // Note that we only walk through the result sets. We don't need
35                     // to walk through individual rows (though underlying provider
36                     // implementation may do so)
37                 }
38             }
39         }
40
41         /// <summary>
42         /// requires: commandText must not be null
43         /// The command text must be in the form Container.FunctionImportName.
44         /// </summary>
45         internal static void ParseFunctionImportCommandText(string commandText, string defaultContainerName, out string containerName, out string functionImportName)
46         {
47             Debug.Assert(null != commandText);
48
49             // Split the string
50             string[] nameParts = commandText.Split('.');
51             containerName = null;
52             functionImportName = null;
53             if (2 == nameParts.Length)
54             {
55                 containerName = nameParts[0].Trim();
56                 functionImportName = nameParts[1].Trim();
57             }
58             else if (1 == nameParts.Length && null != defaultContainerName)
59             {
60                 containerName = defaultContainerName;
61                 functionImportName = nameParts[0].Trim();
62             }
63             if (string.IsNullOrEmpty(containerName) || string.IsNullOrEmpty(functionImportName))
64             {
65                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_InvalidStoredProcedureCommandText);
66             }
67         }
68
69         /// <summary>
70         /// Given an entity command, returns the associated entity transaction and performs validation
71         /// to ensure the transaction is consistent.
72         /// </summary>
73         /// <param name="entityCommand">Entity command instance. Must not be null.</param>
74         /// <returns>Entity transaction</returns>
75         internal static EntityTransaction GetEntityTransaction(EntityCommand entityCommand)
76         {
77             Debug.Assert(null != entityCommand);
78             EntityTransaction entityTransaction = (EntityTransaction)entityCommand.Transaction;
79
80             // Check to make sure that either the command has no transaction associated with it, or it
81             // matches the one used by the connection
82             if (entityTransaction != null && entityTransaction != entityCommand.Connection.CurrentTransaction)
83             {
84                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_InvalidTransactionForCommand);
85             }
86             // Now we have asserted that EntityCommand either has no transaction or has one that matches the
87             // one used in the connection, we can simply use the connection's transaction object
88             entityTransaction = entityCommand.Connection.CurrentTransaction;
89             return entityTransaction;
90         }
91
92
93         /// <summary>
94         /// Given an entity command and entity transaction, passes through relevant state to store provider
95         /// command.
96         /// </summary>
97         /// <param name="entityCommand">Entity command. Must not be null.</param>
98         /// <param name="entityTransaction">Entity transaction. Must not be null.</param>
99         /// <param name="storeProviderCommand">Store provider command that is being setup. Must not be null.</param>
100         internal static void SetStoreProviderCommandState(EntityCommand entityCommand, EntityTransaction entityTransaction, DbCommand storeProviderCommand)
101         {
102             Debug.Assert(null != entityCommand);
103             Debug.Assert(null != storeProviderCommand);
104
105             storeProviderCommand.CommandTimeout = entityCommand.CommandTimeout;
106             storeProviderCommand.Connection = ((EntityConnection)entityCommand.Connection).StoreConnection;
107             storeProviderCommand.Transaction = (null != entityTransaction) ? entityTransaction.StoreTransaction : null;
108             storeProviderCommand.UpdatedRowSource = entityCommand.UpdatedRowSource;
109         }
110
111
112         /// <summary>
113         /// Given an entity command, store provider command and a connection, sets all output parameter values on the entity command.
114         /// The connection is used to determine how to map spatial values.
115         /// </summary>
116         /// <param name="entityCommand">Entity command on which to set parameter values. Must not be null.</param>
117         /// <param name="storeProviderCommand">Store provider command from which to retrieve parameter values. Must not
118         /// be null.</param>
119         /// <param name="connection">The connection on which the command was run.  Must not be null</param>
120         internal static void SetEntityParameterValues(EntityCommand entityCommand, DbCommand storeProviderCommand, EntityConnection connection)
121         {
122             Debug.Assert(null != entityCommand);
123             Debug.Assert(null != storeProviderCommand);
124             Debug.Assert(null != connection);
125
126             foreach (DbParameter storeParameter in storeProviderCommand.Parameters)
127             {
128                 ParameterDirection direction = storeParameter.Direction;
129                 if (0 != (direction & ParameterDirection.Output))
130                 {
131                     // if the entity command also defines the parameter, propagate store parameter value
132                     // to entity parameter
133                     int parameterOrdinal = entityCommand.Parameters.IndexOf(storeParameter.ParameterName);
134                     if (0 <= parameterOrdinal)
135                     {
136                         EntityParameter entityParameter = entityCommand.Parameters[parameterOrdinal];
137                         object parameterValue = storeParameter.Value;
138                         TypeUsage parameterType = entityParameter.GetTypeUsage();
139                         if (Helper.IsSpatialType(parameterType))
140                         {
141                             parameterValue = GetSpatialValueFromProviderValue(parameterValue, (PrimitiveType)parameterType.EdmType, connection);
142                         }
143                         entityParameter.Value = parameterValue;
144                     }
145                 }
146             }
147         }
148
149         private static object GetSpatialValueFromProviderValue(object spatialValue, PrimitiveType parameterType, EntityConnection connection)
150         {
151             DbProviderServices providerServices = DbProviderServices.GetProviderServices(connection.StoreConnection);
152             StoreItemCollection storeItemCollection = (StoreItemCollection)connection.GetMetadataWorkspace().GetItemCollection(DataSpace.SSpace);
153             DbSpatialServices spatialServices = providerServices.GetSpatialServices(storeItemCollection.StoreProviderManifestToken);
154             if (Helper.IsGeographicType(parameterType))
155             {
156                 return spatialServices.GeographyFromProviderValue(spatialValue);
157             }
158             else
159             {
160                 Debug.Assert(Helper.IsGeometricType(parameterType));
161                 return spatialServices.GeometryFromProviderValue(spatialValue);
162             }
163         }
164
165         // requires: all arguments must be given
166         internal static EdmFunction FindFunctionImport(MetadataWorkspace workspace, string containerName, string functionImportName)
167         {
168             Debug.Assert(null != workspace && null != containerName && null != functionImportName);
169             // find entity container
170             EntityContainer entityContainer;
171             if (!workspace.TryGetEntityContainer(containerName, DataSpace.CSpace, out entityContainer))
172             {
173                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnableToFindFunctionImportContainer(
174                     containerName));
175             }
176
177             // find function import
178             EdmFunction functionImport = null;
179             foreach (EdmFunction candidate in entityContainer.FunctionImports)
180             {
181                 if (candidate.Name == functionImportName)
182                 {
183                     functionImport = candidate;
184                     break;
185                 }
186             }
187             if (null == functionImport)
188             {
189                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnableToFindFunctionImport(
190                     containerName, functionImportName));
191             }
192             if (functionImport.IsComposableAttribute)
193             {
194                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_FunctionImportMustBeNonComposable(containerName + "." + functionImportName));
195             }
196             return functionImport;
197         }
198     }
199 }