Just stumbled across the new ClientId (aka client_identifier) variable that is available in Oracle 10g onwards, and would love to incorporate that into an app to assist with audit trails.
The app is using a generated Enterprise Library DAAB based layer (netTiers) that is wired to use the ODP.NET drivers with EntLibContrib, so CRUD functions create an OracleDatabase object and then retrieve generic DbCommand objects from it
It looks like the OracleConnection class has the ClientId property, so what is the cleanest way to get to the Connection object within that pattern? Should I be grabbing the connection out of every DbCommand I create and setting it there, or is that overkill?
Because EntLib is doing much of the connection management behind the scenes, I'm not sure whether I can set the ClientId somewhere outside of the CRUD functions and expect it to persist?
If the connection is a partial class you can implement a trigger that sets the client id whenever the connection changes state to open.
That's the way I implemented it.
I don't know if you can use part of this:
public partial class DataContext
{
partial void OnContextCreated()
{
if ( null == this.Connection ) return;
this.Connection.StateChange += Connection_StateChange;
}
private EntityConnection EntityConnection
{
get { return this.Connection as EntityConnection; }
}
private OracleConnection OracleConnection
{
get { return this.EntityConnection.StoreConnection as OracleConnection; }
}
private void Connection_StateChange( object sender, StateChangeEventArgs e )
{
if ( e.CurrentState != ConnectionState.Open ) return;
OracleConnection conn = this.OracleConnection;
if ( null == conn ) return;
//closes connection on DataContext (bug?), and passes closed/broken connection
//conn.ClientId = HttpContext.Current == null ? "Anonymous" : HttpContext.Current.Profile.UserName;
//working solution
string identity = HttpContext.Current == null ? "Anonymous" : HttpContext.Current.Profile.UserName;
OracleCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "DBMS_SESSION.SET_IDENTIFIER";
cmd.Parameters.Add( new OracleParameter { ParameterName = "client_id", Value = identity } );
cmd.ExecuteNonQuery();
cmd.Dispose();
return;
}
protected override void Dispose( bool disposing )
{
if ( null != this.Connection )
this.Connection.StateChange -= Connection_StateChange;
base.Dispose( disposing );
}
}
Related
I need to get a NEXTVAL from a SEQUENCE in an Oracle database. The modelbuilder does have a
builder.HasSequence("TABLE_SEQ");
But I have no clue on how to use that. The only way I can think of is scalar executing a raw SQL to retrieve the next value. Is that the way to go or are there better ways to do this?
I've found several posts that say I should use context.Database.SqlQuery() but in my solution that is missing. Do I need to add a library to get this functionality for EF 6.0?
Examples I found:
Example 1:
public int GetNewCertificateTradeRequestIdentity()
{
using var command = _context.Database.GetDbConnection().CreateCommand();
command.CommandText = "SELECT ts.seq_certificate_trade_request.NEXTVAL FROM DUAL";
_context.Database.OpenConnection();
using var reader = command.ExecuteReader();
reader.Read();
return reader.GetInt32(0);
}
Example 2:
users = await context.Database.SqlQuery<User>("Select * from User", new object[] { }).ToListAsync();
Both the _context.Database.GetDbConnection() context.Database.SqlQuery<x> are missing. Where can I find them?
Ok, in EF6 you have the context.Database.GetDbConnection().CreateCommand(). With that command you can execute a query on the database and receive the result. I've also found a solution for getting the tablename from the EF6 Metadata and added an extension method to handle that. Now I can do the the following:
private Tijdverantwoording Create(decimal? mdwid, decimal? deelprjid, Datum? date)
{
if (mdwid == null || deelprjid == null || date == null) throw new ArgumentNullException();
Weekstaatstatus weekstaatStatus = _WeekstaatStatusService.GetOrCreate(mdwid.Value, date.Jaarweekcode, WeekStaatStatussen.InBewerking, DateTime.Now);
var tijdverantwoording = new Tijdverantwoording
{
Tijdverantwoordingid = GetId<Tijdverantwoording>(), // <= Generate id
Mdwid = mdwid.Value,
Deelprjid = deelprjid.Value,
Datum = date.DagDatum,
Syncstatus = (decimal)SyncStatuses.InBewerking,
Syncdate = DateTime.Now.Date,
Weekstaatstatusid = weekstaatStatus.Weekstaatstatusid
};
_modelContext.Tijdverantwoordingen.Add(tijdverantwoording);
return tijdverantwoording;
}
The base class used for a service.
using Microsoft.EntityFrameworkCore;
using MyProjects.Core.Extensions;
using MyProjects.Core.Model;
namespace MyProjects.Core.Services
{
public class ServiceBase
{
private ModelContext? _modelContext;
public ServiceBase(ModelContext modelContext)
{
_modelContext = modelContext;
}
public decimal GetId<T>()
where T : class
{
var command = _modelContext.Database.GetDbConnection().CreateCommand();
var tableName = _modelContext.TableName(typeof(T));
command.CommandType = System.Data.CommandType.Text;
command.CommandText = $"SELECT {tableName}_SEQ.NEXTVAL FROM DUAL";
_modelContext.Database.OpenConnection();
try
{
var result = (decimal?)command.ExecuteScalar();
return result.Value;
}
finally
{
_modelContext.Database.CloseConnection();
}
}
}
}
And the extension method
using Microsoft.EntityFrameworkCore;
namespace MyProjects.Core.Extensions
{
public static class DatabaseExtensions
{
public static string? TableName(this DbContext context, Type type)
{
var entityType = context.Model.FindEntityType(type);
return entityType?.GetTableName() ?? throw new NullReferenceException($"Can't find name for type {type.Name}");
}
}
}
I was wondering the best way of accessing the a user's AuthSession from within a custom validator we have hooked up via the servicestack's fluent-validation API hooks. Basically, the requirements are forcing us to access the database through this validator using a class called "DocNumberValidator ". The user's Id is saved into session when they authenticate, down the line we need this information to complete a successful SQL query. How does one access this session information (see below....). The IoC container does not have reference to the AuthSession?
I guess the question is, how does someone pass the necessary session value into a class invoked by SS validation framework?
Sample Code :
public class MyValidator : AbstractValidator<Statuses>
{
public IDocNumberValidator DocNumberValidator { get; set; }
public StatusesValidator()
{
RuleFor(s => s.DocNumber)
.Must(docNum => this.DocNumberValidator.Validate(docNum))
.WithMessage("Document Number is invalid.")
.WithErrorCode("S-001");
}
}
public class DocNumberValidator : IDocNumberValidator
{
public IDbConnectionFactory Db { get; set; }
public bool Validate(string docNum)
{
var isFound = false;
this.Db.Run(conn =>
{
var sql = "SELECT COUNT([docNumber]) FROM [TABLE] WHERE [docNumber] = #DocNum AND [UserId] = #UserID";
var cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql;
cmd.Parameters.Add(new SqlParameter("#DocNum", docNum));
cmd.Parameters.Add(new SqlParameter("#UserID", ????????)); // how does one get access to saved users session here
int cnt = (int) cmd.ExecuteScalar();
if (cnt == 1)
isFound = true;
});
return isFound;
}
}
Not really sure this is the best way to do it. Open to suggestions.
Use SessionFeature.GetSessionKey() to get the session key
Add public ICacheClient CacheClient { get; set; } to the validator.
Will be injected by IoC container
Get the AuthUserSession (or whatever type you're using) from the
cache using the key
Added into your example
public class DocNumberValidator : IDocNumberValidator
{
public IDbConnectionFactory Db { get; set; }
public ICacheClient CacheClient { get; set; }
public bool Validate(string docNum)
{
var isFound = false;
var sessionKey = SessionFeature.GetSessionKey();
var user = CacheClient.Get<AuthUserSession>(sessionKey); //Use whatever class you stored in the session
this.Db.Run(conn =>
{
var sql = "SELECT COUNT([docNumber]) FROM [TABLE] WHERE [docNumber] = #DocNum AND [UserId] = #UserID";
var cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql;
cmd.Parameters.Add(new SqlParameter("#DocNum", docNum));
cmd.Parameters.Add(new SqlParameter("#UserID", user.UserAuthId)); // user whatever property you need access to
int cnt = (int) cmd.ExecuteScalar();
if (cnt == 1)
isFound = true;
});
return isFound;
}
}
I am impressed with the speed that I can create services using ServiceStack, but for a while now I have not been able to grasp the concept of using OrmLite and Dapper simultaneously in my project. I am registering a IDbConnectionFactory like this in my Global.asax.cs
public override void Configure(Funq.Container container)
{
var dbConnectionFactory =
new OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("test"), true, OracleDialect.Provider);
container.Register<IDbConnectionFactory>(dbConnectionFactory);
container.Register<ISubscriberRepository>(
c => new SubscriberRepository(c.Resolve<IDbConnectionFactory>()));
}
That works fine for OrmLite but it is a not as simple for Dapper. Maybe I am just thinking this should be more convenient than it really is. In my repository I am trying to call a Oracle stored procedure. That is my main reason for using Dapper and not OrmLite for this process. This is my repository:
public class SubscriberRepository : ISubscriberRepository {
public SubscriberRepository(IDbConnectionFactory conn) {
_conn = conn;
}
public IDbConnectionFactory _conn { get; set; }
public SubscriberResponse GetSubscriber(SubscriberRequest request) {
using (IDbConnection db = _conn.OpenDbConnection()) {
var resp = new SubscriberResponse();
List<Subscriber> s = db.Select<Subscriber>(
q => q.Subscribernum == request.Subscribernum &&
q.Personcode == request.Personcode &&
q.Clientcode == request.Clientcode);
resp.Subscriber = s[0];
return resp;
}
}
public SubscribersResponse SearchSubscribers(SubscribersRequest request) {
var response = new SubscribersResponse();
using (var cnn = new OracleConnection("this is my conneciton string")) {
cnn.Open();
var p = new OracleDynamicParameters();
p.Add("#username", "uname", OracleDbType.Varchar2);
p.Add("#Subscribernum", "", OracleDbType.Varchar2);
p.Add("#Personcode", "", OracleDbType.Varchar2);
p.Add("#Lastname", "TEST", OracleDbType.Varchar2);
p.Add("#Firstname", "HA", OracleDbType.Varchar2);
p.Add("#Mi", "", OracleDbType.Varchar2);
p.Add("#Dob", null, OracleDbType.Date);
p.Add("#MaxResults", 200, OracleDbType.Int32);
p.Add("#Curs", dbType: OracleDbType.RefCursor, direction: ParameterDirection.Output);
using (SqlMapper.GridReader multi = cnn.QueryMultiple("SEARCHSUBSCRIBER", p,
commandType: CommandType.StoredProcedure)) {
List<SearchSubscriberResults> r = multi.Read<SearchSubscriberResults>().ToList();
response.Results = r;
}
}
return response;
}
}
This works. But it isn't really using the IDbConnectionFactory at all in the SearchSubscribers function. I don't want to look at connection strings in my repository really since I could really register them all up front in the service itself.
I tried to use ServiceStack.Razor.Dapper.SqlMapper.QueryMultiple() but that doesn't work because I can't map the Oracle sys_refcursor back to anything without using the OracleDynamicParamaters workaround.
So, my question is can I create a connection for Dapper from the IDBConnectionFactory?
Thanks!
I haven't used Oracle in .NET (not since my Perl days), but OracleConnection implements the interface IDbConnection.
You should be able to cast the db connection you grab from:
IDbConnection db = _conn.OpenDbConnection()
and cast it to OracleConnection (assuming the OrmLite provider creates that same instance).
var cnn = db as OracleConnection;
... then you can try calling all that Oracle-specific stuff, like OracleDynamicParameters.
You may have to set this in AppHost.Configure() or somewhere:
OrmLiteConfig.DialectProvider = new OracleOrmLiteDialectProvider();
I am having trouble creating generic delete method, not even sure if this possible. I have a delete method which will delete record from db based on entity type and row id value (pk),
that works ok but it needs to know the type in advance. In some cases I can only get object
type using Object.GetType() at runtime (like from viewstate) and that is when trouble starts. Here is my method that works when type is known, is there a way to modify it so that it will use Object.GetType() instead of T ?
public void Delete<T>(long Id) where T : class,new()
{
#region PerformaneMonitor
IDbEntities Db=null;
T item=null;
try
{
Db = this.GetDatabase();
item = new T();
Type itemType = item.GetType();
EntityContainer entityContainer = Db.MetadataWorkspace.GetEntityContainer(Db.DefaultContainerName, DataSpace.CSpace);
var entity = entityContainer.BaseEntitySets.First(b => b.ElementType.Name == itemType.Name);
if (entity.ElementType.KeyMembers.Count == 0)
{
throw new Exception("Unable to delete a record witout unique id");
}
string PrimaryKeyName = entity.ElementType.KeyMembers[0].Name;
itemType.GetProperty(PrimaryKeyName).SetValue(item, Id, null);
}
catch (Exception ex)
{
Close(Db);
throw(ex);
}
this.Delete<T>(item, Db);
Close(Db);
#region PerformaneMonitor
}
so I am trying to convert it to Delete(object EntityType,long Id ) but no luck.
Here what it looks like :
public void Delete(object target,long Id)
{
#region PerformaneMonitor
IDbEntities Db = null;
try
{
Db = this.GetDatabase();
Type itemType = (Type)target;
EntityContainer entityContainer = Db.MetadataWorkspace.GetEntityContainer(Db.DefaultContainerName, DataSpace.CSpace);
var entity= entityContainer.BaseEntitySets.First(b => b.ElementType.Name == itemType.Name);
if (entity.ElementType.KeyMembers.Count == 0)
{
throw new Exception("Unable to delete a record witout unique id");
}
string PrimaryKeyName = entity.ElementType.KeyMembers[0].Name;
itemType.GetProperty(PrimaryKeyName).SetValue(target, Id, null);
}
catch (Exception ex)
{
Close(Db);
throw (ex);
}
this.Delete(target, Db);
Close(Db);
//_method_tag_end_
#region PerformaneMonitor
}
I am getting 'Object does not match target type' on
this line:
itemType.GetProperty(PrimaryKeyName).SetValue(target, pkey, null);
the object target is actaul instance of specific type which I do in the calling method from Type of object and using reflection and pass to this function but still I have no idea what type it is at run time.
If someone can help it will be greatly appreciated.
It sounds like you should do something along these lines: (Sorry, can't test to make sure it works as written.)
object o = itemType.GetProperty(PrimaryKeyName);
MethodInfo mi = o.GetType().GetMethod("SetValue");
mi.Invoke(o, new object [] { Id, null });
I'm using ASP.NET Membership. I'm running it on a shared hosting site where I have an db schema I run things off.
In the scripts to generate the DB on the server I changed the schema from 'dbo' to this other schema; on the tables, views and SP's.
Thing work fine, with everything except the Membership; I'm able to contact the DB and pull up records.
However, the Membership-login fails with the message: "Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'."
This of course is called 'DBxx.aspnet_CheckSchemaVersion' in my database.
Where is this being called from and how can I make it call the correct schema?
It is being called in System.Web.Util.SecUtility and it is hardcoded. Unless you want to re-invent the wheel you need to re-provision your database. I have done it. Is not brain surgery but is a lot of work and the interest of segregating a database does not qualify in my book.
internal static void CheckSchemaVersion(ProviderBase provider, SqlConnection connection, string[] features, string version, ref int schemaVersionCheck)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
if (features == null)
{
throw new ArgumentNullException("features");
}
if (version == null)
{
throw new ArgumentNullException("version");
}
if (schemaVersionCheck == -1)
{
throw new ProviderException(SR.GetString("Provider_Schema_Version_Not_Match", new object[] { provider.ToString(), version }));
}
if (schemaVersionCheck == 0)
{
lock (provider)
{
if (schemaVersionCheck == -1)
{
throw new ProviderException(SR.GetString("Provider_Schema_Version_Not_Match", new object[] { provider.ToString(), version }));
}
if (schemaVersionCheck == 0)
{
SqlCommand command = null;
SqlParameter parameter = null;
foreach (string str in features)
{
command = new SqlCommand("dbo.aspnet_CheckSchemaVersion", connection);
command.CommandType = CommandType.StoredProcedure;
parameter = new SqlParameter("#Feature", str);
command.Parameters.Add(parameter);
parameter = new SqlParameter("#CompatibleSchemaVersion", version);
command.Parameters.Add(parameter);
parameter = new SqlParameter("#ReturnValue", SqlDbType.Int);
parameter.Direction = ParameterDirection.ReturnValue;
command.Parameters.Add(parameter);
command.ExecuteNonQuery();
if (((parameter.Value != null) ? ((int) parameter.Value) : -1) != 0)
{
schemaVersionCheck = -1;
throw new ProviderException(SR.GetString("Provider_Schema_Version_Not_Match", new object[] { provider.ToString(), version }));
}
}
schemaVersionCheck = 1;
}
}
}
}