I am observing a weird behavior in EF6 DbContext. My custom DbContext class reads its connection string from my App.config:
public partial class WorkobjectContext : DbContext
{
public WorkobjectContext()
: base("name=MainConnectionString")
{
// Set the initializer to null to disable intializtion otherwise EF tries to create the table if it does not exist (CreateDatabaseIfNotExists)
Database.SetInitializer<WorkobjectContext>(null);
}
}
As I am using the same connection string for my Ado.NET functions, I try to read the connection string out of DbContext:
var connectionstring = this.MyContext.Database.Connection.ConnectionString;
But now I am observing that this.MyContext.Database.Connection.ConnectionString changes as I call ExecuteCommand:
...
Console.WriteLine(this.WorkobjectContext.Database.Connection.ConnectionString);
// Output: DATA SOURCE=XXXX:1000/XXXX.XXXX;PASSWORD=MyPassword;USER ID=MyUserId
var ret = this.WorkobjectContext.Database.ExecuteSqlCommand(
"BEGIN WPK_Inbox.Check_WOB_User(:pn_UserID, :pn_WOBID, :pn_Return, :pvErrorMsg, :pnErrorCode); end;",
new object[]
{
userIdParameter,wobIdParameter,returnValueParameter,errorMsgParameter,errorCodeParameter
}
);
Console.WriteLine(this.WorkobjectContext.Database.Connection.ConnectionString);
// Output: DATA SOURCE=XXXX:1000/XXXX.XXXX;PUSER ID=MyUserId
As you can see, after calling ExecuteSqlCommand the connection string changes and the password part disappears.
Why is this happening? Is this a bug?
Related
I have a very simple Azure function which updates rows on a database.
This works fine locally via Postman.
This is the very simple call I have in my Azure function
string connectionString = ConfigurationManager.ConnectionStrings["CatsDBEntities"].ConnectionString;
using (var context = new CatsDBEntities(connectionString))
{
// using (var db = new CatsDBEntities())
{
Cat cat = new Cat
{
Name = "BengA",
Id = id
};
context.Cats.Add(cat);
context.SaveChanges();
}
response = req.CreateResponse(HttpStatusCode.OK);
}
}
catch (System.Exception)
And here is my context
public CatsDBEntities(string connectionString) : base(connectionString) { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Cat> Cats { get; set; }
Here is my connection string in local settings (I know this doesn't matter when deploying to Azure but I am using this as an example of my connection string):
metadata=res://*/CatModel.csdl|res://*/CatModel.ssdl|res://*/CatModel.msl;provider=System.Data.SqlClient;provider connection string='data source=x.database.windows.net;initial catalog=CatsDB;user id=x;password=!;MultipleActiveResultSets=True;App=EntityFramework'",
and within Azure, I am entering my connection string like this:
This works great locally through Postman get a 200 but when deployed to Azure I get a 500 and this is my latest error:
This is error means nothing anyway because I have tried several times now and I keep changing the string and the error sometimes changes and sometimes not.
I have added the EF6 database first project as a separate project to my Azure function and I have put a reference between the two projects.
As you mentioned in the post that EF6 DataBase is added first then,
This is caused by how EF model first connection strings are generated.
The EF connection string builder requires a plain connection string in the constructor.
You can Refer this SO thread link for more information.
Basically the problem i have is that i want to run a query in a database that it's not a representation of my model.
This is my code to create the connection to another database:
public static OtherContext GetNewContextGeneric(string connectionString)
{
var builder = new DbContextOptionsBuilder();
builder.UseSqlServer(connectionString);
OtherContext db = new OtherContext(builder.Options);
return db;
}
And this is my code to execute the query:
public List<IQueryble> Query (string connectionString, string query)
{
try
{
using(var contextGeneric = ContextFactory.GetNewContextGeneric(connectionString))
{
//I want something like this
return contextGeneric.Query(query).ToList();
}
}
catch(System.Data.SqlClient.SqlException ex)
{
throw new SQLIncorrectException(ex);
}
catch(System.InvalidOperationException ex)
{
throw new NotImplementedException();
}
}
Can somebody help me?
You can use DbDataReader:
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "SELECT * From Make";
context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
// Do something with result
reader.Read(); // Read first row
var firstColumnObject = reader.GetValue(0);
var secondColumnObject = reader.GetValue(1);
reader.Read(); // Read second row
firstColumnObject = reader.GetValue(0);
secondColumnObject = reader.GetValue(1);
}
}
Here you can learn more how to read values from DbDataReader.
Alternatively you could use FromSql() method, but that works only on predefined DbSet of some entity, which is not the solution you wanted.
In the question you say:
Basically the problem i have is that i want to run a query in a database that it's not a representation of my model.
and then in comments you add:
Because i don't know how is created the database, i don't know what tables are in the database i want to insert the sql query
Well, if you don't know the database, then you cannot use Entity Framework, as it requires you to have a detailed knowledge of the database you are connecting to.
You should use plain ADO.NET (or Dapper if you want to map results back to a known class) for this.
Your use case of an external database can still be achieved using EF Core, there is no need to resort to ADO.Net, this solution is based on this generic solution for EF Core by #ErikEj. (Note that some functions and namespaces changed for EF Core 3, so and remain in .Net 5+ but the same generic concept can still be applied)
public static IList<T> Query<T>(string connectionString, string query, params object[] parameters) where T : class
{
try
{
using (var contextGeneric = new ContextForQuery<T>(connectionString))
{
return contextGeneric.Query<T>().FromSql(query, parameters).ToList();
}
}
catch (System.Data.SqlClient.SqlException ex)
{
throw new SQLIncorrectException(ex);
}
catch (System.InvalidOperationException ex)
{
throw new NotImplementedException();
}
}
private class ContextForQuery<T> : DbContext where T : class
{
private readonly string connectionString;
public ContextForQuery(string connectionString)
{
this.connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString, options => options.EnableRetryOnFailure());
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<T>().HasNoKey();
base.OnModelCreating(modelBuilder);
}
}
Then the usage of this requires a concrete type definition, to add support for anonymous types is a fair bit more effort, but creating a concrete type for this is not a bad thing, the whole point here is to try you towards more declarative code styles as they enhance the readability and inspection of the code as well as providing documentation and other extended configuration like related entities.
public class NamedObject
{
public int Id { get; set; }
public string Name { get; set; }
}
...
var connectionString = "Insert your connection string here...";
var data = Query<NamedObject>(connectionString, "SELECT TOP 10 Id, FullName as Name FROM Employee");
foreach (var emp in data)
{
Console.WriteLine(emp.Name);
}
Background
In EF 6 (.Net Framework) we could use DbContext.Database.FromSQL<T>() to execute ad-hoc SQL that would be automatically mapped to the specified type of T. This functionality was not replicated in EF Core because the result of FromSQL was inconsistent with the rest of EF, the result was a single use IEnumerable<T>. You could not further compose this query to Include() related entities nor could you add a filter to the underlying query.
In EF Core to Execute Raw SQL the type T that you want to return needs to be defined in the DbContext as a DbSet<T>. This set does not need to map to a table in the database at all, in fact since EF Core 2.1 we do not event need to specify a key for this type, it is simply a mechanism to pre-define the expected structure instead of executing Ad-Hoc requests on demand, it offers you the same functionality as the legacy FromSQL but also allows for you to define a rich set of navigation properties that would enable further composition of the query after your RawSQL is interpolated with the LINQ to SQL pipeline.
Once the type is defined in the context you simply call DbSet<T>.FromSqlRaw(). The difference is that now we have an IQueryable<T> that we can use to futher compose to include related entities or apply filters that will be evaluated within the database.
The solution posted in this response doesn't allow for composition, but uses the EF runtime in the expected sequence to give the same behaviours as the original EF 6 implementation.
In more recent versions of EF Core, and now in .Net 5+ the following subtle change need to be applied:
Core 2.1: return contextGeneric.Query<T>().FromSql(query, parameters).ToList();
Core 3+: return contextGeneric.Set<T>().FromSqlRaw(query, parameters).ToList();
You can use context.Database.ExecuteSqlRaw("select 1")
Don't forget to import the right namespace : using Microsoft.EntityFrameworkCore;
It worked like this:
private void SqlCommand (string connectionString, string query)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(query, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
var a = reader[0];
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
}
Or
using (var connection = ContextFactory.GetNewContextGeneric(connectionString).Database.GetDbConnection())
{
connection.Open();
DbCommand command = connection.CreateCommand();
command.CommandText = query;
using (var reader = command.ExecuteReader())
{
// Do something with result
reader.Read(); // Read first row
var firstColumnObject = reader.GetValue(0);
/*var secondColumnObject = reader.GetValue(1);
reader.Read(); // Read second row
firstColumnObject = reader.GetValue(0);
secondColumnObject = reader.GetValue(1);*/
connection.Close();
return firstColumnObject.ToString();
}
}
I am trying to share a connection in between 2 different DB context objects using EF 6 .
I have got the code mentioned here
https://msdn.microsoft.com/en-us/data/dn456843.aspx
to work for a single DB Context but as soon as I try to share the connection with another DB Context object I am running into trouble. Below is what I have done so for.
public class MyUnitOfWork
{
// BAD CODE : having static connection / transaction is bad design , i use DI to implement this properly without need for static fields , this code is used here to avoid having to mention my DI configuration;
public static EntityConnection _commonConnection ;
public static System.Data.Entity.DbContextTransaction _commonTransaction;
// Generic function to create DBContext object based on T supplied.
public static T GetDbContext<T>()
{
if(_commonConnection == null)
{
// generates a generic connection string
_commonConnection = new EntityConnection(DbContextBase.GenerateConnectionString());
_connection.Open();
_commonTransaction = _connection.BeginTransaction(System.Data.IsolationLevel.Snapshot);
}
T myContextObject = (T)Activator.CreateInstance(typeof(T), new object[1] { _connection });
myContextObject .Database.UseTransaction(_transaction);
return myContextObject;
}
}
The code for generation of connection string is mentioned below:
string GenerateConnectionString()
{
var entityBuilder = new EntityConnectionStringBuilder
{
Provider = "System.Data.SqlClient",
ProviderConnectionString = SharedDatabase.SQLBase.DBconnection + "multipleactiveresultsets=True;" ,
Metadata = #"res://*/;"
};
return entityBuilder ;
}
I can then call the GetDbContext from different places in my function like so
GetById(int id)
{
var dbcontext = MyUnitOfWork.GetDbContext<OrdersDbContext>();
return dbcontext.Orders.Where(.......);
}
GetCustomerInfo( int id)
{
var dbcontext = MyUnitOfWork.GetDbContext<CustomerDbContext>();
return dbcontext.Customer.Where(.......);
}
Because I am using the generic metadata portion , I am getting errors in entity framework "cannot have more than one entity with same name regardless of the namespace"
However if I specify the name of the .csdl/.ssdl file , the connection ( and hence the transaction ) can no more be common and I have to create a connection and Transaction for each DBContext ( this is what i wanted to avoid )
It seems I have hit a block . Is there a way for me to use the same connection without getting the duplicate entities error ? Changing the names of entities to be different is not an option for me as it will be a very time consuming change which I would have to do across 30 + db context / EDMX files with huge production impact.
As seen in Working with Transactions (EF6 Onwards), use the following code to provide an existing transaction to your context :
string GenerateConnectionString()
{
return SharedDatabase.SQLBase.DBconnection + "multipleactiveresultsets=True;";
}
public class MyUnitOfWork
{
SqlConnection _commonConnection;
DbTransaction _commonTransaction;
// Generic function to create DBContext object based on T supplied.
public T GetDbContext<T>()
{
if (_commonConnection == null)
{
// generates a generic connection string
_commonConnection = new SqlConnection(DbContextBase.GenerateConnectionString());
_commonConnection.Open();
_commonTransaction = _connection.BeginTransaction(IsolationLevel.Snapshot);
}
MetadataWorkspace workspace = new MetadataWorkspace(
string.Format("res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;", typeof(T).Name).Split('|'),
new Assembly[] { Assembly.GetExecutingAssembly() });
var connection = new EntityConnection(workspace, _commonConnection);
T myContextObject = (T)Activator.CreateInstance(typeof(T), new object[] { connection });
myContextObject.Database.UseTransaction(_commonTransaction);
return myContextObject;
}
}
I'm new to .Net MVC and I wanted to run a raw query on my UserInRoles table.
I think I will need a database context to run it.
I'm unsure what context to use. Can some one recommend me a direction to take? Currently, the ObjectContext does not allow me to instantiate without a connection string. Is directly grabbing the connection string from web config correct?
Error 1 'System.Data.Entity.Core.Objects.ObjectContext' does not contain a constructor that takes 0 arguments
using (var ctx = new ObjectContext())
{
string query = "INSERT INTO dbo.webpages_UsersInRoles (RoleId,UserId) values ("+chk+","+id+");";
ExecuteSql(ctx,query);
}
ExecuteSql is using ADO.net connections different from what EF recommends but I need to do this manual insert in order for this section to work.
static void ExecuteSql(ObjectContext c, string sql)
{
var entityConnection = (System.Data.EntityClient.EntityConnection)c.Connection;
DbConnection conn = entityConnection.StoreConnection;
ConnectionState initialState = conn.State;
try
{
if (initialState != ConnectionState.Open)
conn.Open(); // open connection if not already open
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
}
finally
{
if (initialState != ConnectionState.Open)
conn.Close(); // only close connection if not initially open
}
}
The ObjectContext (MSDN Link) requires you to provide a connection string to execute against. The ObjectContext is not your EF container but a context to query against a database and transform to objects. Because it is a generic query mechanism it does not know which connection string to use. In addition, your query is a simple insert query making the ObjectContext the wrong tool for the job. As the ObjectContext provides mapping mechanisms that you will not require I would suggest using your EF container to execute the query. This can be done by calling (sample code)
dbContainer.Database.ExecuteSqlCommand(query)
This command will grab your existing connection string from your configuration file and execute the query provided. Finally there is another option if you really want a ObjectContext without having to enter the connection string. You can do this by providing a wrapper, or static method to create an ObjectContext without the connection string. Such as.
Method 1: Wrapper (inheritance)
class MyObjectContext : ObjectContext
{
public MyObjectContext()
: base(MyObjectContext.connectionString)
{ }
/// <summary>
/// the connection string id in the config
/// </summary>
const string connectionStringID = "dbCon";
/// <summary>
/// gets the connection string
/// </summary>
static string connectionString
{
get
{
return ConfigurationManager.ConnectionStrings[connectionStringID].ConnectionString
}
}
}
Method 2. Static Property (or method)
static ObjectContext New
{
get
{
return new ObjectContext(ConfigurationManager.ConnectionStrings["dbCon"].ConnectionString);
}
}
Now personally I prefer method 1 (if i had to do this) as it also gives me the ability to extend this class to define my queries in one class such as.
class MyObjectContext : ObjectContext
{
//.....
public void Insert_UserInRole(string roleID, string id)
{
///TODO DO: insert role
}
//.....
}
And can be called such as.
using (var context = new MyObjectContext())
{
context.Insert_UserInRole("abc", "123");
}
[Just a thought]
Important In addition your query is begging for SQL Injection. Use "parameters" in your queries. Using parameters will prevent SQL inject atacks. Here is a SO topic regarding parameters. How to pass parameters to the DbContext.Database.ExecuteSqlCommand method?
I hope this helps.
I am getting timeouts using the Entity Framework (EF) when using a function import that takes over 30 seconds to complete. I tried the following and have not been able to resolve this issue:
I added Default Command Timeout=300000 to the connection string in the App.Config file in the project that has the EDMX file as suggested here.
This is what my connection string looks like:
<add
name="MyEntityConnectionString"
connectionString="metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|
res://*/MyEntities.msl;
provider=System.Data.SqlClient;provider connection string="
Data Source=trekdevbox;Initial Catalog=StarTrekDatabase;
Persist Security Info=True;User ID=JamesTKirk;Password=IsFriendsWithSpock;
MultipleActiveResultSets=True;Default Command Timeout=300000;""
providerName="System.Data.EntityClient" />
I tried setting the CommandTimeout in my repository directly like so:
private TrekEntities context = new TrekEntities();
public IEnumerable<TrekMatches> GetKirksFriends()
{
this.context.CommandTimeout = 180;
return this.context.GetKirksFriends();
}
What else can I do to get the EF from timing out? This only happens for very large datasets. Everything works fine with small datasets.
Here is one of the errors I'm getting:
System.Data.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
OK - I got this working and it's silly what happened. I had both the connection string with Default Command Timeout=300000 and the CommandTimeout set to 180. When I removed the Default Command Timeout from the connection string, it worked. So the answer is to manually set the CommandTimeout in your repository on your context object like so:
this.context.CommandTimeout = 180;
Apparently setting the timeout settings in the connection string has no effect on it.
There is a known bug with specifying default command timeout within the EF connection string.
http://bugs.mysql.com/bug.php?id=56806
Remove the value from the connection string and set it on the data context object itself. This will work if you remove the conflicting value from the connection string.
Entity Framework Core 1.0:
this.context.Database.SetCommandTimeout(180);
Entity Framework 6:
this.context.Database.CommandTimeout = 180;
Entity Framework 5:
((IObjectContextAdapter)this.context).ObjectContext.CommandTimeout = 180;
Entity Framework 4 and below:
this.context.CommandTimeout = 180;
If you are using a DbContext, use the following constructor to set the command timeout:
public class MyContext : DbContext
{
public MyContext ()
{
var adapter = (IObjectContextAdapter)this;
var objectContext = adapter.ObjectContext;
objectContext.CommandTimeout = 1 * 60; // value in seconds
}
}
If you are using DbContext and EF v6+, alternatively you can use:
this.context.Database.CommandTimeout = 180;
If you are using Entity Framework like me, you should define Time out on Startup class as follows:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), o => o.CommandTimeout(180)));
Usually I handle my operations within a transaction. As I've experienced, it is not enough to set the context command timeout, but the transaction needs a constructor with a timeout parameter. I had to set both time out values for it to work properly.
int? prevto = uow.Context.Database.CommandTimeout;
uow.Context.Database.CommandTimeout = 900;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(900))) {
...
}
At the end of the function I set back the command timeout to the previous value in prevto.
Using EF6
I know this is very old thread running, but still EF has not fixed this. For people using auto-generated DbContext can use the following code to set the timeout manually.
public partial class SampleContext : DbContext
{
public SampleContext()
: base("name=SampleContext")
{
this.SetCommandTimeOut(180);
}
public void SetCommandTimeOut(int Timeout)
{
var objectContext = (this as IObjectContextAdapter).ObjectContext;
objectContext.CommandTimeout = Timeout;
}
}
In .Net Core (NetCore) use the following syntax to change the timeout from the default 30 seconds to 90 seconds:
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
this.Database.SetCommandTimeout(90); // <-- 90 seconds
}
}
This is what I've fund out. Maybe it will help to someone:
So here we go:
If You use LINQ with EF looking for some exact elements contained in the list like this:
await context.MyObject1.Include("MyObject2").Where(t => IdList.Contains(t.MyObjectId)).ToListAsync();
everything is going fine until IdList contains more than one Id.
The “timeout” problem comes out if the list contains just one Id. To resolve the issue use if condition to check number of ids in IdList.
Example:
if (IdList.Count == 1)
{
result = await entities. MyObject1.Include("MyObject2").Where(t => IdList.FirstOrDefault()==t. MyObjectId).ToListAsync();
}
else
{
result = await entities. MyObject1.Include("MyObject2").Where(t => IdList.Contains(t. MyObjectId)).ToListAsync();
}
Explanation:
Simply try to use Sql Profiler and check the Select statement generated by Entity frameeork. …
For Entity framework 6 I use this annotation and works fine.
public partial class MyDbContext : DbContext
{
private const int TimeoutDuration = 300;
public MyDbContext ()
: base("name=Model1")
{
this.Database.CommandTimeout = TimeoutDuration;
}
// Some other codes
}
The CommandTimeout parameter is a nullable integer that set timeout
values as seconds, if you set null or don't set it will use default
value of provider you use.
Adding the following to my stored procedure, solved the time out error by me:
SET NOCOUNT ON;
SET ARITHABORT ON;
There are 2 timeout parameters you can set in connection string
Timeout=300;CommandTimeout=300;
Host=localhost;Port=5432;database=mydatabase;username=postgres;password=postgres;Timeout=300;CommandTimeout=300;
one for command and one for connection.