I have following scenario...
Massive Micro-ORM
.NET framework 4.0
SQL Server 2008R2
Model:
public class sUser : DynamicModel
{
public sUser() : base(Model.strConnection, "Users", "UserId") { }
}
public class Test
{
public void UpdateUser(string user)
{
dynamic User = GetUser(user);
//Dynamically generated User object has many columns
// I Update following fields...
User.Address1 = "123 Main Street";
User.Address2 = "Suite# 456";
User.CityName = "Princeton";
User.State = "NJ";
User.Zipcode = "08540";
//And update some more fields in Users object
//I could do this...
//var upd = new { Address1 = "123 Main Street", Address2 = "Suite# 456", ...};
//User.Update(upd, User.UserID);
//But would like to pass entire object, so I do not have to form all updated name/value in the update statement
User.Update(User, User.UserID);
//But Users table has a column with IDENTITY SEED,
//And if I pass entire object for update it errors out
//Cannot update identity column 'RefNo'
}
public dynamic GetUser(string userName)
{
dynamic table = new sUser();
var objUser = table.First(UserName: userName);
return objUser;
}
}
Users table has a column RefNo with IDENTITY SEED=1, and when I update entire User object, it errors out Cannot update identity column 'RefNo'. I would like to pass entire object for update rather than forming long update statement.
How can I handle this?
Thanks.
If your Users table has a column RefNo with IDENTITY SEED = 1, then that would imply it is your primary key. In you sUser class, you are calling the base constructor with:
base(Model.strConnection, "Users", "UserId")
This call is telling Massive that the primary key column is UserId - what happens if you instead pass it RefNo?
Edit:
I think I see the problem you are having: Massive will generate an update statement including all the properties of your object, including RefNo. Because the database is taking care of this column (via IDENTITY SEED), you can't modify or set this value.
What I would suggest instead is taking advantage of the fact that User is returned as an ExpandoObject. What you could do is this:
((IDictionary<string, object>)User).Remove("RefNo");
User.Update(User, User.UserID);
What this will do is remove the RefNo property from the object, meaning that it won't be included in the update statement that gets created, which should in turn result in the Update call succedding.
Modify Massive.cs
- Add following under DynamicModel class
private string IdentityColumn { get; set; }
private string GetIdentityColumn()
{
return (string)Scalar("SELECT C.name AS IdentityColumn FROM sys.columns C Inner Join sys.tables T ON T.object_id = C.object_id WHERE C.is_identity = 1 And T.name = '" + TableName + "'");
}
And under CreateUpdateCommand method add following...
IdentityColumn = GetIdentityColumn();
And under foreach loop modify if statement to following...
if (!item.Key.Equals(PrimaryKeyField, StringComparison.OrdinalIgnoreCase) && item.Value != null && item.Key != IdentityColumn)
Above change into Massive library would allow us to update model with identity column. Limitation: Works for Table with one IDENTITY column.
Related
First time using LinqtoSQL. So to give you guys a bit of context:
I have a simple SQL Server table called Inventory with the following fields
InventoryID (int)(autoincrementing)
InventoryItemName (varchar)(255)
InventoryCategory (int)
Next I have another table called InventoryCategories for with the following fields:
InventoryCategoryID (int)(autoincrementing)
InventoryCategoryName (varchar)(255)
InventoryCategoryDescription (varchar)(255)
Next, Currently I have a combo box which selects which query to update the DataGrid.ItemSource, The code fo for this is below
if (searchcategory == "All Stock")
{
InventoryDataContext dc = new InventoryDataContext();
var q =
from a in dc.GetTable<Inventory>()
select a;
SearchResults.ItemsSource = q;
}
Now this result returns the Full table of Inventory with the columns of InventoryID, InventoryItemName, and InventoryCategory. However it returns the ID Number of InventoryCategory, in the InventoryCategory Column.
Would anyone be able to help me get the InventoryCategoryName from InventoryCategories Table in this query instead of the ID? What would be required for this?
try using left join:
var qry = from inv in context.Inventory
from invCategory in context.InventoryCategories.Where(u => inv.InventoryCategory == u.InventoryCategoryID).DefaultIfEmpty()
select new myViewModel {id = invCategory.InventoryCategoryID, categoryName = invCategory .InventoryCategoryName }
and don't forget to create myViewModel class with id and categoryName properties
I'm having issue in Insertion of data in Temporal table using C# Entity Framework
The Table schema is
CREATE TABLE People(
PeopleID int PRIMARY KEY NOT NULL,
Name varchar(50) Null,
LastName varchar(100) NULL,
NickName varchar(25),
StartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
EndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (StartTime,EndTime)
) WITH (SYSTEM_VERSIONING = ON(HISTORY_TABLE = dbo.PeopleHistory));
I created an EDMX asusal and I tried to Insert a record using following C# Code
using (var db = new DevDBEntities()) {
People1 peo = new People1() {
PeopleID = 1,
Name = "Emma",
LastName = "Watson",
NickName = "ICE"
};
db.Peoples.Add(peo);
db.SaveChanges();
}
I got an exception while on db.SaveChanges()
"Cannot insert an explicit value into a GENERATED ALWAYS column in
table 'DevDB.dbo.People'. Use INSERT with a column list to exclude the
GENERATED ALWAYS column, or insert a DEFAULT into GENERATED ALWAYS
column."
I tried direct insertion using SQL Server using the following Insert Query, its inserting fine.
INSERT INTO [dbo].[People]
([PeopleID]
,[Name]
,[LastName]
,[NickName])
VALUES
(2
,'John'
,'Math'
,'COOL')
Kindly assist me how to insert an record using C# Entity Framework.
Light summary: The problem occurs when EF trying to update values inside PERIOD system versioning column which the column property values are managed by SQL Server itself.
From MS Docs: Temporal tables, the temporal table works as a pair of current table and history table which explained as this:
System-versioning for a table is implemented as a pair of tables, a
current table and a history table. Within each of these tables, the
following two additional datetime2 columns are used to define the
period of validity for each row:
Period start column: The system records the start time for the row in this column, typically denoted as the SysStartTime column.
Period end column: The system records the end time for the row in this column, typically denoted at the SysEndTime column.
As both StartTime & EndTime column are automatically generated, they must be excluded from any attempt to insert or update values on them. Here are these steps to get rid of the error, assuming you're in EF 6:
Open EDMX file in designer mode, set both StartTime & EndTime column properties as Identity in StoreGeneratedPattern option. This prevents EF refreshing values on any UPDATE events.
Create a custom command tree interceptor class which implements System.Data.Entity.Infrastructure.Interception.IDbCommandTreeInterceptor and specify set clauses which should be set as ReadOnlyCollection<T> (T is a DbModificationClause) which cannot be modified by EF in insert or update modifications:
internal class TemporalTableCommandTreeInterceptor : IDbCommandTreeInterceptor
{
private static ReadOnlyCollection<DbModificationClause> GenerateSetClauses(IList<DbModificationClause> modificationClauses)
{
var props = new List<DbModificationClause>(modificationClauses);
props = props.Where(_ => !_ignoredColumns.Contains((((_ as DbSetClause)?.Property as DbPropertyExpression)?.Property as EdmProperty)?.Name)).ToList();
var newSetClauses = new ReadOnlyCollection<DbModificationClause>(props);
return newSetClauses;
}
}
Still in the same class above, create list of ignored table names and define actions in INSERT and UPDATE commands, the method should be looks like this (credits to Matt Ruwe for this method):
// from /a/40742144
private static readonly List<string> _ignoredColumns = new List<string> { "StartTime", "EndTime" };
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var insertCommand = interceptionContext.Result as DbInsertCommandTree;
if (insertCommand != null)
{
var newSetClauses = GenerateSetClauses(insertCommand.SetClauses);
var newCommand = new DbInsertCommandTree(
insertCommand.MetadataWorkspace,
insertCommand.DataSpace,
insertCommand.Target,
newSetClauses,
insertCommand.Returning);
interceptionContext.Result = newCommand;
}
var updateCommand = interceptionContext.Result as DbUpdateCommandTree;
if (updateCommand != null)
{
var newSetClauses = GenerateSetClauses(updateCommand.SetClauses);
var newCommand = new DbUpdateCommandTree(
updateCommand.MetadataWorkspace,
updateCommand.DataSpace,
updateCommand.Target,
updateCommand.Predicate,
newSetClauses,
updateCommand.Returning);
interceptionContext.Result = newCommand;
}
}
}
Register the interceptor class above before database context usage in another code part either by using DbInterception:
DbInterception.Add(new TemporalTableCommandTreeInterceptor());
or attach it in context definition using DbConfigurationTypeAttribute:
public class CustomDbConfiguration : DbConfiguration
{
public CustomDbConfiguration()
{
this.AddInterceptor(new TemporalTableCommandTreeInterceptor());
}
}
// from /a/40302086
[DbConfigurationType(typeof(CustomDbConfiguration))]
public partial class DataContext : System.Data.Entity.DbContext
{
public DataContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
// other stuff or leave this blank
}
}
Related issues:
Entity Framework not working with temporal table
Getting DbContext from implementation of IDbCommandInterceptor
Hooking IDbInterceptor to EntityFramework DbContext only once
Probably the simplest solution would be manually edit the .EDMX file and remove all traces of the StartTime and EndTime columns.
I am developing an App using MVC5 and using Entity Framework Code first Approach for development. I have an ID column in Employee.cs Model class which auto increments as the Record is Inserted. I want to know How can I add another Column in my table with varchar type which will Look something Like this. EMP233 where 233 i an ID Generated by Identiy Column.
Note:
I know how to do this task using SQL Server but I am curious to Know How it Can be done using EF Code first Approach.
Many people will Suggest me to get the latest max ID from db, Increment it and add it to the object something Like
int EmpMaxID = db.Employees.OrderByDescending(u => u.ID).FirstOrDefault();
employee.VarcharID = "EMP" + EmpMaxID;
This Approach is not the Best because lets suppose if the user add a record and system generates its ID as 234 and Moment after he deletes it. Then next time in this case system will return Max ID as 233 i.e. EmpMaxID = 233 but the ID Generated by System is going to be 235. So what we will get in return is a Record with ID = 235 AND EmpID = "EMP233" Which is wrong. I hope you got my Point.
Here is what I have tried but It didn't Work. IN THE MODELS I wrote something like this
public int ID { get; set; } // Unique numeric ID for each Employee
public string strEmpID // A varchar ID
{
get { return strEmpID}
set { strEmpID= "EMP" + ID; } // ID is Auto Generated
}
This Doesn't Work. Please help me completing this Task. Thanks
When you save this record for the first time, get the Id, update the strEmpID property as well.
var emp = new Employee { Name="Scott" };
db.Employees.Add(emp);
db.SaveChanges();
if (String.IsNullOrEmpty(emp.strEmpID))
{
e.strEmpID = "EMP" + e.Id;
db.Entry(e).State=EntityState.Modified;
db.SaveChanges();
}
The only safe way is to add the record and then get the id and update the record.
using (var context = new MyContext())
{
context.MyEntities.AddObject(newEmployee);
context.SaveChanges();
int id = newEmployee.Id; // Your Identity column ID
newEmployee.streEmpId = "EMP" + id;
context.Entry(newEmployee).State = EntityState.Modified;
context.SaveChanges();
}
I'm using Dapper with C# to get result from a store procedure.
The Table (TPTable) has Pid as Primary key, and Value as name for second column, and some other columns.
I have a store that is: select * from TPTable.
When i call QueryAsync<TPTableModel> method the result comes the value of Value Column is allways the same as Primary Key
Let say in DB
Pid = 1, Value = 2
Pid = 2, Value = 568
The Result will be a list of {{Pid=1,Value=1},{Pid=2,Value=2}}.
Why i will getting Value equals to PrimaryKey, everytime?
I found the problem. Is not with Dapper. I was repro a small project and i found the store has a inner join with an sql user defined type. This type has a Column named Value that has the values of primary key. When i call Dapper with select * on store it will fill the value of user defined type.
This works fine locally:
[Fact]
public async void SO35470588_WrongValuePidValue()
{
// nuke, rebuild, and populate the table
try { connection.Execute("drop table TPTable"); } catch { }
connection.Execute(#"
create table TPTable (Pid int not null primary key identity(1,1), Value int not null);
insert TPTable (Value) values (2), (568)");
// fetch the data using the query in the question, then force to a dictionary
var rows = (await connection.QueryAsync<TPTable>("select * from TPTable"))
.ToDictionary(x => x.Pid);
// check the number of rows
rows.Count.IsEqualTo(2);
// check row 1
var row = rows[1];
row.Pid.IsEqualTo(1);
row.Value.IsEqualTo(2);
// check row 2
row = rows[2];
row.Pid.IsEqualTo(2);
row.Value.IsEqualTo(568);
}
public class TPTable
{
public int Pid { get; set; }
public int Value { get; set; }
}
I'd be happy to investigate, but a repro would be much appreciated.
I have a DataGridView called dataGridView1 that populates on form load. How do I add an extra column to the end with a date time stamp of "now" and make sure this Created column is on the end or the row?
I have tried:
dataGridView1.Columns.Add("Created","GETDATE()");
I populate the dataGridView1 like this:
private void DisplayAppropriateMessage(FacebookOAuthResult facebookOAuthResult)
{
if (facebookOAuthResult != null)
{
if (facebookOAuthResult.IsSuccess)
{
var fb = new FacebookClient(facebookOAuthResult.AccessToken);
dynamic result = fb.Get("/me");
var name = result.name;
{
var query = string.Format(#"SELECT uid, username, first_name, last_name, friend_count, pic_big
FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = me())");
dynamic parameters = new ExpandoObject();
parameters.q = query;
dynamic results = fb.Get("/fql", parameters);
List<MyFriends> q = JsonConvert.DeserializeObject<List<MyFriends>>(results.data.ToString());
dataGridView1.DataSource = q;
}
}
else
{
MessageBox.Show(facebookOAuthResult.ErrorDescription);
}
}
}
List<MyFriends> q contains data returned by facebook.
You store it as MyFriends type (which BTW should be called MyFriend because each object stores data about one of your friends). Then you display this list of friends in datagridview. I don't know how you configured gridview but If I remember well it can create columns from displayed object's properties automatically or you can define them on your own.
So solution is to add property to MyFriends type with name Created and after you get results from Facebook just iterate over results and set their Created property to DateTime.Now. Depending on how you configured datagridview you will have to add column to it or not.