I've mapped some function that is being used as a wrapper for the one of the Oracle functions (I really need to call that Oracle function from my LINQ).
My mapping looks like (Storage Model):
<Function Name="RunTranslate" IsComposable="false" BuiltIn="false">
<CommandText>
SELECT Translate(#DataToTranslate using char_cs) AS ResultData FROM dual
</CommandText>
<Parameter Name="DataToTranslate" Type="char" Mode="In" />
</Function>
And:
<FunctionImport Name="OracleTranslate" ReturnType="Collection(String)">
<Parameter Name="DataToTranslate" Mode="In" Type="String" />
</FunctionImport>
I try to use it from the code this way:
using (var context = new TestEntities())
{
....
var result = from myData in context.OracleTranslate("test")
select myData; // Error ORA-00936!
....
}
I tried to use Entity Framework Profiler (EFProf) to see the query that is actually being sent to the DB. Intercepted query is the next:
SELECT Translate('test' /*#DataToTranslate*/ using char_cs) AS ResultData FROM dual
(looks fine)
But my application stops with the next error:
ORA-00936: missing expression
If I copy-paste the intercepted query into the VS2010's Server Explorer Query Window - it works! DB returns correct data to me!
So, where the problem can be? What is the difference between the code and the query window in my case? I thought they use the same provider, etc...
Thank you very much!
EDIT:
I've just used standard Inet-sniffer to check the real data that is being sent over the sockets and Internet when I run my query.
And it's not the same with the first one! It's exactly my source SQL query:
SELECT Translate(#DataToTranslate using char_cs) AS ResultData FROM dual
It seems, that parameter is not recognized, but why?
I've found a solution, I hope this info will help someone:
DataToTranslate parameter should be mapped with the colon symbol (":DataToTranslate"), not "#".
Another important experience (as to me): Entity Framework Profiler (EFProf) shows something that it wants, not the real data - much better to use TCP-sniffers like Wireshark to see that data.
Related
How do I "tell" EF to log queries globally? I was reading this blog post: EF logging which tells in general how to log sql queries. But I still have a few questions regarding this logger.
Where would I need to place this line context.Database.Log = s =>
logger.Log("EFApp", s);?
Can it be globally set? Or do I have to place it everywhere I do DB
operations?
In the "Failed execution" section, the blogger wrote that, and I
quote:
For commands that fail by throwing an exception, the output contains the message from the exception.
Will this be logged too if I don't use the context.Database.Log?
Whenever you want the context to start logging.
It appears to be done on the context object so it should be done every time you create a new context. You could add this line of code in your constructor though to ensure that it is always enabled.
It will not log if you do not enable the logging.
I don't recommend to use that's functionality, because, it hasn't reason to exists in the real case.
Thats it use a lot of to debug code only. But, wether you wanna know more than details ... access link... https://cmatskas.com/logging-and-tracing-with-entity-framework-6/
In this case you can put code like this
public void Mylog()
{
//Thats a delegate where you can set this property to log using
//delegate type Action, see the code below
context.Database.Log = k=>Console.Write("Any query SQL")
//Or
context.Database.Log = k=>Test("Any query SQL")
}
public void Test(string x){
Console.Write(x)
}
I hope thats useufull
Initially our solution had System.Data.Entity.Infrastructure.LocalDbConnectionFactory set as the defaultConnectionFactory type in our web.config.
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
I'm not sure we really need it like that since we use local SQL Server for dev and SQL Azure for real deployments. We use EF Code First and our tables get created with keys named like PK_dbo.Customers and FK_dbo.Customers_dbo.Employers_EmployerID and an index is created for each foreign key like IX_EmployerID.
We have switched to a custom connectionfactory based on the ideas in this post for a ReliableDbProvider created by Robert Moore because we want to have built in retry logic for transient failures with SQL Azure. It seems to work fine but it also seems like it causes the keys to be named differently (PK__Customer__A4AE64B8BB3388DF, Customer_Employer) and indexes to not be generated.
I didn't expect the factory to influence the generation. Any idea how it contributes?
After reflecting some code, seems like it has to do with the way the DbMigrationsConfiguration class which is used inside the DropCreateDatabaseIfModelChanges initializer works so we'll have to see if that can be overridden somehow.
public DbMigrationsConfiguration()
{
this.SetSqlGenerator("System.Data.SqlClient", new SqlServerMigrationSqlGenerator());
this.SetSqlGenerator("System.Data.SqlServerCe.4.0", new SqlCeMigrationSqlGenerator());
this.CodeGenerator = new CSharpMigrationCodeGenerator();
}
Still open to ideas!
Based on some reflected code, it looks like the issue is that there is hardcoded logic for non-System.Data.SqlClient or sqlce providers in DatabaseCreator class that forces the generation down a different path.
public void CreateDatabase(InternalContext internalContext, Func<DbMigrationsConfiguration, DbContext, DbMigrator> createMigrator, ObjectContext objectContext)
{
if (internalContext.CodeFirstModel == null || !(internalContext.ProviderName == "System.Data.SqlClient") && !(internalContext.ProviderName == "System.Data.SqlServerCe.4.0"))
{
internalContext.DatabaseOperations.Create(objectContext);
internalContext.SaveMetadataToDatabase();
}
else
{
Type type = internalContext.Owner.GetType();
DbMigrationsConfiguration dbMigrationsConfiguration = new DbMigrationsConfiguration();
dbMigrationsConfiguration.ContextType = type;
dbMigrationsConfiguration.AutomaticMigrationsEnabled = true;
dbMigrationsConfiguration.MigrationsAssembly = type.Assembly;
dbMigrationsConfiguration.MigrationsNamespace = type.Namespace;
dbMigrationsConfiguration.TargetDatabase = new DbConnectionInfo(internalContext.OriginalConnectionString, internalContext.ProviderName);
createMigrator(dbMigrationsConfiguration, internalContext.Owner).Update();
}
internalContext.MarkDatabaseInitialized();
}
In the end, we updated our datacontext constructor so that the DefaultConnectionFactory is set in code and not in config. In development (debug mode) only, if db doesn't exist, we set first to the SqlConnectionFactory since it generates the db with indexes and better naming that we want. After that or in release mode we want to use the custom provider which has retry logic we want.
i want to create one function in edmx which return scaler value,
how to create it in SSDL and how to access it in code?
One problem you have is your SSDL is automatically generated by the 'EntityModelGenerator', so editing it will be wiped out by a rebuild. Your edits need to be done in th EDMX file.
So firstly, you have to decide is (1) your return value a calculation of sorts (i.e. adding values together in the application, rather than at database level), or (2) is it a direct call to a database stored procedure?
(1) First step is to add the function XML definition into your EDMX file:
<Function Name="LineTotal" ReturnType="decimal">
<Parameter Name="lineTotal" Type="MyDbModel.OrderDetail">
<DefiningExpression>
od.Price * od.Quantity
</DefiningExpression>
</Parameter>
</Function>
Now, although your EDMX knows about this function, your IntelliSense won't. So you have to add some code to make this work. It is a best practice to place these functions in a seperate class.
public class ModelDefinedFunctions
{
[EdmFunction("MyDbModel" , "LineTotal")] //Model Name and Function Name
public static decimal LineTotal(OrderDetail od)
{
throw new NotSupportedException("LineTotal cannot be directly used.");
}
}
Entity Framework will know to redirect this function call to the EDMX instead. Any direct call to this method, where the model does not exist will throw an exception.
You can then call it in your LINQ queries like
var productValues = from line in model.OrderDetails
select new
{
od.ProductID,
od.Price,
od.Quantity,
LineTotal = ModeDefinedFunctions.LineTotal(line)
};
(2) If you are adding a stored procedure directly, it is easier to drag and drop it onto the EDMX designer. There is a [FunctionImport()] attribute, but I haven't used it. You can drag and drop and see what code it generates in the EDMX file?
Alternatively, you can call the model.ExecuteCommand(<spname> , params object[] values
) stored procedure execution method.
I'm working on an application which accesses data from an Oracle 11g database. I'm using EF4 and data is accessed using LINQ.
I have come across a scenario where I need to call a function stored in a package. This function also has a return value. I have added this function to the entity data model but am unable to perform "Add Function Import" on it. Hence I'm unable to access it using LINQ.
How can I call this function and also get it's return value?
I asked this question a while back, but have not got any answer for it yet. Anyways, I'm updating it with some details so that it becomes convenient for others to understand the issue and guide me in the right direction. I have tried to implement the solution proposed in this question, but am getting an exception.
I've added the following to the designer.cs file of my entity data model:
[EdmFunction("TestModel.Store", "TestFunction")]
public int TestFunction(decimal ALNR, decimal ATID, decimal AUKENR)
{
throw new ApplicationException();
}
Following is a small portion of the edmx file:
<edmx:Edmx Version="2.0" xmlns:edmx="http://schemas.microsoft.com/ado/2008/10/edmx">
<!-- EF Runtime content -->
<edmx:Runtime>
<!-- SSDL content -->
<edmx:StorageModels>
<Schema Namespace="TestModel.Store" Alias="Self" Provider="Oracle.DataAccess.Client" ProviderManifestToken="11g" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<Function Name="TestFunction" ReturnType="number" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" StoreFunctionName="PKG_TURNUS.SUMUKE" Schema="SYSTEM">
<Parameter Name="ATID" Type="number" Mode="In" />
<Parameter Name="ALNR" Type="number" Mode="In" />
<Parameter Name="AUKENR" Type="number" Mode="In" />
</Function>
Here is how I am calling this function:
var selectQuery = from T in _context.Table1
join A in _context.Table2 on T.columnA equals A.columnB
join TU in _context.Table3 on T.columnC equals TU.columnD
where T.columnD == 5 && T.columnE == someVariable
select new someType
{
propertyA = A.columnG,
propertyB = _context.TestFunction(T.columnE, A.columnF, TU.columnH)
};
But when I do any of the following:
ObservableCollection<someType> weekTotaldata = new ObservableCollection<someType>(selectQuery); //Exception thrown at runtime
or
foreach (var ttu in selectQuery) //Exception thrown at runtime
{
double testval = ttu.propertyB;
}
I get the exception that is thrown in that function "ApplicationException". Shouldn't this
exception be thrown when this function is called anywhere else exception the L2E query?
What am I doing wrong? How can I call an oracle function from Linq-To-Entities? Please note that the function that I am trying to call is not a built-in oracle function, but a user-defined function.
As far as i have worked on EF4 using Oracle , Function importing doesn't seems to work here. I faced the same problem few months back and tried many ways to import a function but without any luck. But during searching I found a link on OTN which states that (Oracle Stored Functions are not supported). EF4 doesn't give us the option to call oracle function yet. Even using Stored Procedures, you will need to select stored procedures that return a ref cursor. Supported Stored procedures include procedures and package methods that do not have a return value, but may have OUT or IN OUT parameters.
Here is the link
But if you are using Sql server some how you can accomplish the User Defined Function import in EF4. Here are some links that might helps you:
Link1
Link2
I have a little problem. I have application running on localhost and everything is OK, but when I upload it to production web server and I want to connect the database I get just "Sorry, an error occurred while processing your request.". When I try page without database interaction it works. I'm using Entity Framework, but I'm not sure if I have connection strings correct.
This is connection string for localhost:
<connectionStrings>
<add name="ApplicationServices" connectionString="data source=localhost\MSSQL;Integrated Security=SSPI;Initial Catalog=KravmagaIS" />
<add name="KravmagaISEntities" connectionString="metadata=res://*/Models.KravmagaModel.csdl|res://*/Models.KravmagaModel.ssdl|res://*/Models.KravmagaModel.msl;provider=System.Data.SqlClient;provider connection string="Data Source=ISKUDA-NTB\MSSQL;Initial Catalog=KravmagaIS;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" />
</connectionStrings>
and this one is for production server:
<connectionStrings>
<add name="ApplicationServices" connectionString="Data Source=192.168.1.5;User ID=db3542;Password=****;Initial Catalog=db3542" />
<add name="KravmagaISEntities" connectionString="metadata=res://*/Models.KravmagaModel.csdl|res://*/Models.KravmagaModel.ssdl|res://*/Models.KravmagaModel.msl;provider=System.Data.SqlClient;provider connection string='Data Source=192.168.1.5;User ID=db3542;Password=*****;Initial Catalog=db3542'" providerName="System.Data.EntityClient" />
</connectionStrings>
Is there any mistake? I'm sure that password is correct.
Thanks for helping.
EDIT:
Sorry my mistake. :-/ The connection is OK. But the relations in production server are the cause of the problem.
I have class Training and class Instructor. (Training has one instructor).
When I call in view:
#training.InstructorID
I get normaly the ID of training instructor, but when I call:
#training.Instructor.Fullname
I get the error message. On localhost is everything working.
Any ideas? Thanks.
I suppose that the same code runs in your development environment so it should not be a problem of your code. Also because you mentioned that Training is loaded it should not be a problem of connecting to the database server.
It looks like lazy loading is not working which turns back to my former note that MARS (MultipleActiveResultSets) is not enabled in the production connection string. MARS allows you to read results from one query in the loop and in the same time execute another query to get details. Typical example is:
// Executes something kile SELECT * FROM Trainings and has opened
// DataReated for the whole duration of the loop.
foreach(var training in context.Trainings)
{
// Executes something like SELECT * FROM Instructors WHERE Id = #Id
// and opens its own DataReader to get the result.
// If MARS is not enabled or not supported you will get an exception
var fullName = training.Instructor.FullName;
}
Btw. the code is example of N+1 problem because it will exacute 1 outer query and for each result of the outer query it will execute 1 inner query. So for N results from the outer query you it will execute N subqueries => THIS IS BAD and should be avoided as much as possible by using another loading technique. For example:
// Loads both trainings ana related instructors in the same query.
foreach(var training in context.Trainings.Include("Instructor"))
{
// No query is executed here because instructor is already loaded.
var fullName = training.Instructor.FullName;
}