so there are a few other similar type posts about, but none matching the simple task I am attempting.
just running a stored proc through EF6 code first in the following manner
var results = context.DataBase.SqlQuery<MyClass>("exec spGetStuff #param1, #param2", new SqlParameter[] {new SqlParameter("param1",value), new SqlParameter("param2", value2)});
I have used this method on many occasions with no issue.
the class I am mapping the results to is pretty nasty with many properties, but all that need it are marked with the [Column("dbfieldname")] attribute.
all the stored proc is doing is returning some results by using a
SELECT * FROM(
SELECT
ROW_NUMBER() OVER ( PARTITION BY X,Y,Z ORDER BY A) [RowNumber]
,*
FROM
MyTableNAme
WHERE
...) S
WHERE s.RowNumber = 1
not inserting, updating or anything fantastical like that.
The data reader is incompatible with the specified 'MyClass'. A member of the type, 'PropertyNameName', does not have a corresponding column in the data reader with the same name.
if I do change the class properties to the db column names it seems to work fine:I can change the first few properties and it will then fail on other ones in the class... however I do not really want to do that if I can possibly avoid it as most of the columns in the DB are named very badly indeed, so I guess my question is why is it ignoring the [Column()] attributes that have never failed me before.
or is the issue the row_number, which I have tried adding to the class an/or removing from the query resultsset.
We're seeing this issue too since upgrading and refactoring for EF6 from 5. Stored procedure returns and we have a Complex Type defined in our edmx. It seems that everything matches up but we get the same type of error when calling like this
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<GetColumnValueSet_Result>("GetColumnValueSet", param1, param2, param3);
So after trying a few things this is what I found. I opened my edmx and went to the Model Browser within Visual Studio. Under Function Imports I found my sproc. Right clicked and chose Function Import Mapping. It turned out that even though the complex type was correct and it appeared everything should have matched up the Function Import Mapping was thinking the sproc was returning a column called CodeValue instead of Value (which is what it was actually importing).
So for some reason when the sproc columns were gotten it go a wrong name for the mapping.
Related
I need to read the result from a stored procedure called from EF Core that includes things intended for spreadsheet formatting purposes such as rows with only null values. My code is mostly working, but there are two columns that cause the query to fail when I include them.
Here's my model class (only a few props included for brevity):
public class StoredProcRecord
{
[Column("Prod Line")]
public string ProductLine { get; set; }
[Column("Current Sales")]
public decimal? CurrentSales { get; set; }
[Column("Current Margin $")]
public decimal? CurrentMargin { get; set; }
}
Here's a snippet of part of the output if I run the stored procedure from SQL Server Management Studio:
Here's how I registered the type as an entity model with EF Core in the db context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Query<StoredProcRecord>();
}
Here's how I'm calling the stored procedure:
return _context.Query<StoredProcRecord>().FromSql("EXECUTE my_stored_proc").ToList();
In this example, EF Core always throws an exception when trying to call FromSql() with a message stating:
The required column 'Current Margin $' was not present in the results
of a 'FromSql' operation.
According to the EF Core docs, as long as a column is nullable, then it is not required. Yet, I receive that exception that claims "Current Margin $" is required. It also only happens with the CurrentMargin property, not the CurrentSales property, which uses the same type. If I remove CurrentMargin, the whole thing works fine and I'm just missing that one property.
I have tried using [Column(Order = 2)] instead of a named column, thinking maybe the dollar sign in the column name had something to do with it, but that results in:
The required column 'CurrentMargin' was not present in the results of
a 'FromSql' operation.
Including both order and name results in the same type of error.
Worth noting, this is not a stored procedure that I have the option to make updates to.
What is different about the CurrentMargin property compared to CurrentSales that causes it to fail like this? Is there some special way I need to handle the dollar sign in the column name? Am I missing something else completely?
Edit:
Here's a snippet from the very end of the stored procedure where the resulting table is output. I'm including this to show that "Current Margin $" is indeed one of the column names. I actually copied/pasted right from the stored procedure just to make sure I hadn't mis-typed the column name.
BEGIN
-- bulk of stored procedure that builds #tmp_tbl omitted for length
SELECT
'Prod Line' = pl_key,
'Current Sales' = curr_sales,
'Current Margin $' = curr_margin
FROM #tmp_tbl
END
The embarrassing answer to this question is make sure you read your code carefully, especially things like string values.
In this case, I have two stored procedures that will eventually get used by my app, bv_xls_profit_sum_v2 and bv_xls_prod_sum_v2. I was implementing usage of the results of the first stored procedure, but I accidentally provided the name of the second stored procedure to EF. The columns from those two do not match, so the error I was receiving about missing required column wasn't about whether or not the column was nullable, but was actually a case where the column was indeed missing from the result set completely. This wasn't obvious when testing through SQL Server Management Studio because I didn't realize I was running a different stored procedure.
Bonus "Answer":
Prior to figuring out my stupid mistake, I did figure out a workaround that I'll leave here just in case it proves useful for anyone. My app has its own SQL Server instance, but also queries against another SQL Server instance that is not controlled by the app (that's where the stored procedures in question live). To work around the issue I was struggling with, I connected the other SQL Server instance to mine as a linked server. I then duplicated the stored procedure I wanted to call, updated it to have it query the linked server, eliminate unwanted null rows, and return using column names to match my model class. This does work and allows me to manage the stored procedure, though it does likely add additional latency to getting a result set back from querying the modified stored procedure.
I'm working with Entity Framework but have a hierarchical datasource. It takes too long time to make db-queries and convert to json in the controller.
For that reason, I have a stored procedure.
Executing the stored procedure directly in the Microsoft SQL Server Management Studio, I get the expected output.
But trying to do the following in my Controller throws the following exception.
`IEnumerable<MyItem> itemsJson=Context.Database.SqlQuery<MyItem>("Get_JSON_MyItems").ToList();`
System.Data.Entity.Core.EntityCommandExecutionException: 'The data
reader is incompatible with the specified 'MyDataModel.MyItem'.
A member of the type, 'Id', does not have a corresponding column in
the data reader with the same name.'
I guess the data reader splits the string into many fields because it is too long? But I'm not sure and don't know how I'd fix if so.
--
(Also tried to write the command in the Controller instead executing the stored procedure)
FOR JSON results in the query returning a single row and single column, with a non-meaningful column name (essentially a single cell containing the full JSON payload). As an example - it doesn't have a Id column in it - so EF doesn't know how to populate the model.
I'd suggest you need to:
Remove the use of FOR JSON.
Ensure that the columns returned in the query are exactly the same as the properties in your class. Only try this if (1) doesn't work.
Alternatively, if you really want to keep FOR JSON, then you need to read the single JSON element and then use JSON.NET (or similar) to map it to your List<MyItem>.
One way you could do this, as an example, is to change your stored proc to have an nvarchar(MAX) OUTPUT parameter - which you set to the result of your query. Your calling code can then read that output parameter.
Another way would be to bypass Entity Framework altogether and use ADO.NET directly. Call the stored proc and then use SqlDataReader.GetString(0) to get the raw JSON.
I use orm dapper from c# application for mysql database access. It works fine. There's only syntax question. I have a class with a many properties. All this properties matches database table fields exactly. So Select request is pretty short:
var listOfInstances = con.Query<MyClass>("Select * From myTable");
but when I need to insert something into database I have to write all those properties names that looks a little bit ugly:
con.Execute(#"Insert into myTable values(#Id, #Property1, #Property2, #Property3, #Property4, ....)", listOfInstances);
I wonder if there are any shorter syntax to insert data, at least for such a case when all class properties matches database table fields exactly.
P.S. The same thing about Update request
P.P.S. If say honestly I just start to work with a database which contains many tables, so I had to write basic functions get/add/change instance for each of those tables and it is quite annoing to list all of their fields.
Basically you need to install a nuget package called Dapper.Contrib
here's the repo
https://github.com/StackExchange/dapper-dot-net/tree/master/Dapper.Contrib
Also check this out https://samsaffron.com/archive/2012/01/16/that-annoying-insert-problem-getting-data-into-the-db-using-dapper
I have a business object type representing customers which, when its .Save() method is called, attempts to retrieve (using LINQ) a matching entity from the database based on the object's ID property. If it does not find a matching entity, it creates a new one; if it does find a matching entity it updates that entity's properties and calls [my datacontext].SubmitChanges().
That last part is the problem. Much of the data for a user is stored in an XML field in the database, named content_xml. There is a bug in the code which is failing to retrieve two of those data items ("coordinates" and "sales_groups") when constructing the business object, and so when the .Save() method goes to update the entity, the XML it's sending is missing those elements.
For some reason this is throwing a ChangeConflictException, stating that "1 of 12" updates failed. In order to identify what was causing the problem, I used the code from http://msdn.microsoft.com/en-us/library/Bb386985%28v=vs.100%29.aspx to extract information about the change conflict.
From that, I see that [my datacontext].ChangeConflicts contains a single ObjectChangeConflict, which itself contains a single MemberChangeConflict representing the XML field.
The part I do not understand is that when checking the values of currVal, origVal and databaseVal, I see that XML data held in currVal is what I would expect based on the bug (it's missing the two elements), and the XML for origVal and databaseVal are identical. That shouldn't cause a conflict, should it?
Even more strange is that when I manually correct the bug by providing the correct (according to the database) values for the missing elements, just before the .SubmitChanges() call, it still causes a conflict, even though the XML from all three (currVal, origVal and databaseVal) all now look identical.
Can anyone suggest what might be causing the conflict?
Edit:
OK, this is a bit of a surprise, but even if I never set the content_xml property value of the retrieved entity before submitting changes, I still get a conflict on the XML field.
I would guess that the change conflict stems from L2S comparing old vs new value in an incorrect way for your xml field.
A possible workaround for this is to add a timestamp or rowversion column to the table and updating the L2S model. If a table contain rowversion/timestamp, only that column will be used for detecting change conflicts...
So I am just trying to get RavenDB up and running and I have struggled with several issues but finally got it working. I was able to successfully insert and pull records for display.
However, I decided to move the class I was using to generate documents from to another spot which cause a namespace change. I ran ran everything and I can still insert documents to Raven. But when I try to pull them for display purposes I get the following error:
Unable to cast object of type 'Library.Logging.RewardProviderLog' to type 'Admin.ViewModels.ImportMonitorViewModel'.
So after going through all of the other posts I could find online it seems that the issue has something to do with the Raven-Clr-Type that essentially tracks the namespace information of the object you are saving as a document.
Ok. So I went in and deleted all the documents I created since I am still just testing and trying to get things running. I even went ahead and blew away the index and recreated it. I ran my process of inserting a new log. But I still get the same error message when I try to pull them and display them.
Note: ViewModels.ImportMonitorViewModel and Library.Logging.RewardProviderLog are identical. They contain the exact same properties.
Update
Index (named ImportMonitorLogs):
from doc in docs.RewardProviderLogs
select new {doc.status, doc.newItemsCount, doc.additionalInfo, doc.lastRun};
Query:
DocumentStore RavenDBStore = new Raven.Client.Document.DocumentStore { Url = "myurl" };
RavenDBStore.DefaultDatabase = "yei-logs";
RavenDBStore.Initialize();Raven.Client.Indexes.IndexCreation.CreateIndexes(System.Reflection.Assembly.GetCallingAssembly(), RavenDBStore);
using(var session = RavenDBStore.OpenSession())
{
model = (from log in session.Query<ViewModels.ImportMonitorViewModel>("ImportMonitorLogs")
orderby log.lastRun descending
select log).ToList();
}
Putting aside the rename and what might have worked before, the error matches the query you are attempting. You are indexing documents of type RewardProviderLog, and retrieving them directly as type ImportMonitorViewModel.
You say all of the properties are the same in both classes, but that alone won't get RavenDB to duck-type them for you. You have to be a little more explicit. This will probably work:
model = (from log in session.Query<RewardProviderLog>("ImportMonitorLogs")
orderby log.lastRun descending
select log).As<ViewModels.ImportMonitorViewModel>().ToList();
Or if you want slightly cleaner syntax (IMHO), this is equivalent:
model = session.Query<RewardProviderLog>("ImportMonitorLogs")
.OrderByDescending(x=> x.lastRun)
.As<ViewModels.ImportMonitorViewModel>()
.ToList();
The key here is that you are querying based on the type that matches the entity your index is returning, and that you use the As method to duck-type it into your view model. (This is the same thing as OfType<T>, and you can read more in the docs here).
If you want to get a bit fancier and project different fields or project from the index directly, you can look at AsProjection in the docs here.
If you're still scratching your head as to why this worked before, I can see that it might have worked if your viewmodel an entity were named the same thing - even if they were from different namespaces. They would still have the same Raven-Entity-Name metadata value.