Entity framework and Stored procedure - c#

I have a stored procedure :
CREATE PROCEDURE SELECT_Some_Data
#Sreachstr nvarchar(200)
AS
BEGIN
SELECT ROW_NUMBER() OVER(ORDER BY [Document].DocNo DESC) AS Row,*
FROM Document WHERE DocNo=#Sreachstr
END
when I execute it with #Sreachstr='153', it returns 15 records.
I use Entity Framework to get the data returned by the stored procedure:
public static List<DocumentInfo_Full_Data> SelectByDocNo(string SearchStr)
{
using (LibEntities_new db = new LibEntities_new())
{
return SelectByDocNo(db, SearchStr);
}
}
private static List<DocumentInfo_Full_Data> SelectByDocNo(LibEntities_new db, String SearchStr)
{
return db.SelectByDocNo(SearchStr).ToList();
}
public ObjectResult<DocumentInfo_Full_Data> SelectByDocNo(global::System.String searchStr)
{
ObjectParameter searchStrParameter;
if (searchStr != null)
{
searchStrParameter = new ObjectParameter("SearchStr", searchStr);
}
else
{
searchStrParameter = new ObjectParameter("SearchStr", typeof(global::System.String));
}
return base.ExecuteFunction<DocumentInfo_Full_Data>("SelectByDocNo", searchStrParameter);
}
When I call this method with parameter SearchStr="15" , I see one record that 15 times is repeated instead of 15 different records.

I had this happen to me once when I was selecting rows from a view in EF.
Since the view itself doesn't have a primary key, EF wasn't able to determine the key - instead, EF created a "guessed" key based on all non-nullable columns from the view.
My view returned four rows of data, e.g.
Col1 Col2 Col3 Col4
1 2 'ABC' 42
1 2 'DEF' 57
1 2 'GHI' 4711
1 2 'JKL' 404
--> my query worked just fine in SQL Server Management Studio.
The "key" that EF had guessed was based on (Col1, Col2).
Now when I retrieved the rows using EF, this happen:
the first row got selected - EF saw it didn't have any data yet, so it stored that row in its result set
the next row was selected - EF determined that the key was the same ( (1,2) again) so it assumed this was the same row again; same key -> same row, so that same entity got stored a second, third and fourth time
So in the end, what I got back from EF was
Col1 Col2 Col3 Col4
1 2 'ABC' 42
1 2 'ABC' 42
1 2 'ABC' 42
1 2 'ABC' 42
because the key that determines uniqueness of an entity in EF was the same for each of the four columns from the database.
So this might be happening in your case, too - especially if your created a new complex type for your data returned from the stored procedure - and if your key on the EF entity (DocumentInfo_Full_Data) is not properly set to an actual, really identifying column (or set of columns) from the database. Check it out!

Related

How to auto-increment oracle table from Entity Framework code-first approach?

I have the following table where I just added the decorator DatabaseGenerated like so:
public class Reference
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public decimal ReferenceId { get; set; }
public string AddressType { get; set; }
public string RefferenceType { get; set; }
public string RefferenceValue { get; set; }
[ForeignKey("Shipment")]
public decimal? TrackingNumber { get; set; }
public Shipment Shipment { get; set; }
}
And my latest up migration that is pushed to the oracle 19c DB is:
public override void Up()
{
DropPrimaryKey("SALOGSEARCH.References");
AlterColumn("SALOGSEARCH.References", "ReferenceId", c => c.Decimal(nullable: false, precision: 20, scale: 0, identity: true));
AddPrimaryKey("SALOGSEARCH.References", "ReferenceId");
}
Yet when I execute my context save after adding:
using (var context = new DbContext())
{
context.Reference.Add(shipperReference);
context.SaveChanges();
}
I get an exception
ORA-01400: cannot insert NULL
I assumed that the tag DatabaseGeneratedOption.Identity would generate a sequence in the Oracle database and call it from the default value on next or something. But that is not the case.
Do I have to manually create a trigger and sequence for each column now?
If so then what is the point the of code first if I have to meddle with the database myself.
I'm not sure if this has any effect but I am adjusting the OnModelCreating like so:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("SALOGSEARCH");
modelBuilder.Properties<string>().Configure(s => s.HasMaxLength(400).HasColumnType("Varchar2"));
modelBuilder.Properties<decimal>().Configure(s => s.HasPrecision(20, 0).HasColumnType("Number"));
}
Any pointers to solve this from c# side would be much appreciated.
EDIT1:
Following this tutorial I've come to realize that the Id of the model class (table) needs to be an int which is number(10, 0) in oracle terms in order to automatically create the desired sequence (as the column default calling nextVal) on the DB side.
Looking at the up migration however, there doesn't seem to be any indication of this sequence creation, so it just leads me to believe that the creation happens somewhere deeper and outside of my field of knowledge.
DatabaseGeneratedOption.Identity expects a sequence or an identity column.
Identity columns are supported since version 12c, and are neither referenced by triggers nor sequences. They are handled internally by Oracle. If you get an error
ORA-01400: cannot insert NULL
it is probably because you are making an insert without explicitly naming the columns, or because you are using the wrong IDENTITY type, or because you don't have a sequence.
You have these options for IDENTITY columns
GENERATED ALWAYS: Oracle always generates a value for the identity column. Attempt to insert a value into the identity column will cause an error.
GENERATED BY DEFAULT: Oracle generates a value for the identity column if you provide no value. If you provide a value, Oracle will insert that value into the identity column. For this option, Oracle will issue an error if you insert a NULL value into the identity column.
GENERATED BY DEFAULT ON NULL: Oracle generates a value for the identity column if you provide a NULL value or no value at all.
Example
SQL> create table t ( c1 number generated by default as identity ( start with 1 increment by 1 ) , c2 number ) ;
Table created.
SQL> insert into t ( c2 ) values ( 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
SQL> insert into t ( c1 , c2 ) values ( null , 1 ) ;
insert into t ( c1 , c2 ) values ( null , 1 )
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("MY_SCHEMA"."T"."C1")
However I can make an insert and explicitly refer to the IDENTITY
SQL> insert into t ( c1, c2 ) values ( 2, 2 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
2 2
SQL> insert into t ( c2 ) values ( 3 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1
2 2
2 3
In your case, I would use GENERATED BY DEFAULT ON NULL:
SQL> create table t ( c1 number generated by default on null as identity ( start with
1 increment by 1 ) , c2 number ) ;
Table created.
SQL> insert into t values ( null , 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 1

Copy column in one table to another column in another table

I have three tables and there respective columns
tReferences:
PK_Reference
FK_ReferenceType
ReferenceValue
thePKofMain_FK
tReferencesTypes:
PK_ReferenceType
ReferenceName
tMain:
PK_Main
FirstReferenceValue
SecondReferenceValue
ThirdReferenceValue
Three references have become important enough that they are going to have to go to the tMain table. Let's say the PK of the three references from tReferenceTypes are 321, 654, and 987. I need to copy the references values from tReference to tMain table where each reference has their own columns now, but I have to make sure that I am adding the value from tReference to the correct PK_Main which is suppose to be the same value as the thePKofMain_FK in tReference table, and that I amtReferenceType PK.
I need something like this...
UPDATE tMain
SET
tMain.FirstReferenceValue = (SELECT ReferenceValue FROM tReference WHERE FK_referenceType =321)
FROM tReference
WHERE tReference. thePKofMain_FK = tMain.PK_Main
But I get this messages which makes sense:
Sub query returned more than 1 value. This is not permitted when the
sub query follows =, !=, <, <= , >, >= or when the sub query is used
as an expression. The statement has been terminated.
UPDATE tMain
SET
tMain.FirstReferenceValue = (SELECT ReferenceValue FROM tReference
JOIN tReference on tReference.thePKofMain_FK = tMain.PK_Main
WHERE FK_referenceType =9001649
WHERE FK_referenceType =321)
FROM tReference
WHERE tReference. thePKofMain_FK = tMain.PK_Main
Msg 209, Level 16, State 1, Line 18. Ambiguous column name
'FK_referenceType'. Msg 209, Level 16, State 1, Line 15. Ambiguous
column name 'ReferenceValue'.
OR should I look into doing it in C#?
Some example data:
tReferences:
PK_Reference
123
456
789
FK_ReferenceType:
321
654
987
ReferenceValue
111
a
1
-------------- why I got the first error messages since some reference values are the same
111
c
2
thePKofMain_FK
147
258
369
tReferencesTypes:
PK_ReferenceType
321
654
987
ReferenceName:
FirstReferenceValue
SecondReferenceValue
ThirdReferenceValue
tMain:
PK_Main:
147
258
369
FirstReferenceValue:
Empty
SecondReferenceValue:
Empty
ThirdReferenceValue:
Empty
There is duplicates of ReferenceValues that why the first sql query doesn't work, but even if there are duplicates of the same referenceValue I still need to copy it over to the new table
The second query it accidently typed in WHERE FK_referenceType =9001649 when I was trying to post the question.
Use a Pivot to get the tMain values
INSERT tMain (PK_Main, FirstReferenceValue, SecondReferenceValue, ThirdReferenceValue)
SELECT P.thePKofMain_FK, P.[321], P.[654], P.[987]
FROM (
SELECT FK_ReferenceType, ReferenceValue, thePKofMain_FK
FROM tReference
) T
PIVOT (
MAX(ReferenceValue) FOR FK_ReferenceType IN ([321], [654], [987])
) P

Algorithm: two tables are in connection with ID, calculating a new table from them

I have two Excel tables for input.
The structure of the first one is like (first_table, fields: id, value, no primary key):
ID1 4
ID1 5
ID1 2
ID2 3
ID2 1
ID3 1
ID4 1
ID4 3
etc till the end of the document (it's not determined)
The second one is like (second_table, fields: id, value, ID is primary key):
ID1 2
ID2 5
ID3 1
ID4 2
etc till the end of the document (it's not determined)
I would like to create a new table (let's call it output_table) from these. The new table should contain the same fields: ID and value. In this new table I want to write each records from the first table keeping its sequence (it's very important, because it's in a timeline). The values should change according to the following conditions:
- ID's are in connection
- if the first_table.value is higher than or equals with the second_table.value, the output.value := second_table.value. And this point I would like to omit records under it with the same IDs (in the first table) and step to the next ID type.
- if the the second_table.value is higher than the first_table.value, then output_table.value := first_table.value and the reference of second_table.value := (second_table.value - first_table.value) for the next step (and if this case happens, I want to investigate the next record with the calculated reference till a new ID comes up from first_table, or the first condition will be true)
I could not figure out the proper algorithm for it, please help me! Thank you for your help!!
I'm working in C#, and I have already created list of (record) objects from the two input table. (so maybe Linq would help me)
ok.
You may create a class (you could use a Tuple<string, int> instead, but it maybe easier to understand) for first_table and output table values.
public class MyDatas {
public string Id {get;set;}
public int Value {get;set;}
}
Imagine you have, from first_table, a List of MyDatas
And for second_table a Dictionary<string, int>
which seems reasonable.
then you create an empty list of MyDatas which is your output.
var table1 = new List<MyDatas>(<content of first_table>);
var table2 = new Dictionary<string, int>(<content of second_table>);
var output = new List<MyDatas>();
//good old foreach, this may be clearer than linq in this case.
foreach (var l1 in table1) {
var id = l1.Id;
//default : we take table 1 Value (if Id is not in table2 or table 2 value > table1 value
var newData = new MyDatas{Id = id, Value = l1.Value};
output.Add(newData);
//id of table1 is not in table 2, go to next line.
if (!table2.ContainsKey(id)) continue;
//if table 1 value > = table 2 value => take table 2 value
if (l1.Value >= table2[id])
newData.Value = table2[id];
//if table 2 value => table 1 value, keep table 1 value for output and decrement table2 value
else {
table2[id] -= l1.Value;
}
}

How to insert a dynamic id to table in sql?

I want to add to a table in my DB ("InjuryScenario") a dynamic id (because i work with visual studio c#) and i tried this:
declare #InjuryScenarioTMPp int;
set #InjuryScenarioTMPp = (select MAX (InjuryScenario_id) from InjuryScenario) +1;
print #InjuryScenarioTMPp;
when i printed it, it doesn't show me anything.
when i tried to add a row to the table and i tried the 3 rows (up) again it does print "2".
maybe when i don't have any rows in the table it doesn't know how to do (NULL+1)?
does anyone have an idea why?
It depends on what kind of database would you like to use. For example, in MySQL you have to use AUTO INCREMENT on one field. But in Postgres or in Oracle, you should create first a sequence and later than add the next value of this sequence to new record.
A NULLvalue is an unknown value, and the result of arithmetic on unknown values is also unknown. What you can do is to use either theCOALESCEor theISNULLfunction to replace the NULLvalue with 0when you use it:
declare #InjuryScenarioTMPp int;
set #InjuryScenarioTMPp = (select ISNULL(MAX(InjuryScenario_id),0) from InjuryScenario) + 1;
print #InjuryScenarioTMPp;
See MSDN: COALESCE and ISNULL
For an example:
declare #i int
set #i = null
select #i+1, coalesce(#i, 0)+1, isnull(#i, 0)+1
set #i = 1
select #i+1, coalesce(#i, 0)+1, isnull(#i, 0)+1
Output:
----------- ----------- -----------
NULL 1 1
(1 row(s) affected)
----------- ----------- -----------
2 2 2
(1 row(s) affected)

Remove rows from a DataTable where an entry exist in another DataTable

Sorry about the confusing subject line :)
I want to make a SQLlike query with my DataTable:s: I want to do something like this
// Is named "BadValues" Rows contain: id1, id2
DataTable tableReadFromFile = readFromFile();
// Is named "AllValues" Rows contain id1, id2
DataTable tableReadFromSql = readFromSql
DataTable resultTable =
tableReadFromFile.select("where AllValues.id1 not in (select id1 from BadValues) and AllValues.id2 not in (select id2 from BadValues)");
So if my "BadValues" table would look like this:
id1 id2
0 1
10 11
20 21
and my "AllValues" table would look like this:
id1 id2
0 1
0 2
1 1
10 11
10 12
12 11
20 21
20 22
22 21
I would like the resultTable to look like this:
id1 id2
0 2
1 1
10 12
12 11
20 22
22 21
In other words: if the pair id1,id2 exists in the table "BadValues" and in "AllValues" I want to remove them so that they don't exist in the result table.
This would have been rather simple to do in SQL if the table "BadValues" would exist in the SQL database, but since it is loaded from file that is not possible.
As it is now, I loop through all rows in the "BadValues" and construct individual SQL queries with the id1 and id2 values set. Since I have quite a lot of data, that is very time consuming.
Any tip is appreciated!
I think this will do it:
DataTable tblBadValues; // filled however
DataTable tblAllValues; // filled however
tblBadValues.Merge(tblAllValues); // this will add to tblBadValues all records
// that aren't already in there
DataTable tblResults = tblBadValues.getChanges(); // this will get the records
// that were just added by the merge, meaning it will return all the records
// that were originally in tblAllValues that weren't also in tblBadValues
tblBadValues.RejectChanges(); // in case you need to re-use tblBadValues
Using Linq to dataset:
var badValues = new HashSet<Tuple<int, int>>(
tableReadFromFile.AsEnumerable().
Select(row =>
new Tuple<int, int>(row.Field<int>("id1"), row.Field<int>("id2"))));
var result = tableReadFromSql.AsEnumerable().
Where(row => !(badValues.Contains(
new Tuple<int, int>(row.Field<int>("id1"), row.Field<int>("id2")))));
The first statement basically creates a hashset of the tuples which represent the bad values.
The second searches in the second table the rows which ids are not in the hashset.
I have an idea, although you would have to do LINQ to SQL.
var query = from data in AllObjects
select data;
foreach (DataObject o in BadData)
{
DataObject temp = o;
query = query.Where(x => !((x.id1 == temp.id1) && (x.id2 == temp.id2)));
}
//query now contains the expression to get only good rows.
Only when query gets iterated (or .ToArray etc.) does it execute a call to you database server.

Categories

Resources