Ok, first off I have seen several questions on this issue. I have gone through just about all of them and none of the solutions prescribed worked for me.
I am using asp.net core 1.1.0 and SQL Server 2016. I am also using the code first approach. The following is my mappings for the table in question
public class TierMap : IEntityTypeConfiguration<Tier>
{
public void Map(EntityTypeBuilder<Tier> builder)
{
builder.HasKey(a => a.TierID);
builder.Property(a => a.TierID).UseSqlServerIdentityColumn<int>();
builder.HasOne(a => a.Country)
.WithMany(a => a.Tiers)
.HasForeignKey(a => a.CountryID);
}
}
Below is the design view of the table in SQL Server:
When I create a new instance of a Tier object, all the properties except the TierID get some value (a random negative integer). So inserting into the database throws the following error
Cannot insert explicit value for identity column in table 'Tiers' when IDENTITY_INSERT is set to OFF.
Below is the code the attempts to save the data
[HttpPost("AddTier")]
public async Task<IActionResult> AddTier([FromBody]TierDataModel model)
{
if (!ModelState.IsValid)
{
Response.AddApplicationError("Data is invalid ");
return BadRequest();
}
Tier tier = new Tier();
tier.TierName = model.TierName;
tier.TierNo = model.TierNo;
tier.CountryID = model.CountryID;
_tierService.AddTier(tier);
await _unitOfWorkAsync.SaveChangesAsync();
return Ok();
}
Changing _dbSet.Attach(entity); to _dbSet.Add(entity); in my Repository Class solved the problem
Related
I'm new to using EF to handle data in SQL. In a MVC Core project we're testing EF (Microsoft.EntityFrameworkCore, version 2.2.3) to handle data.
When trying to update data and update failed for some reason (missing fields etc) it seemed like EF actually deleted the record from the database (MSSQL 2014) instead of throwing an update error...
Is it possible?
Code for updating:
public void Update(Contact contact)
{
_dbContext.Update(contact);
_dbContext.SaveChanges();
}
When trying to update data and update failed for some reason (missing fields etc) it seemed like EF actually deleted the record from the database (MSSQL 2014) instead of throwing an update error...
Is it possible?
It should not.
test it, try to debug here
_dbContext.Update(contact);
_dbContext.SaveChanges();
var updated = _dbContext.Contacts.FirstOrDefault(x => x.Id == contact.Id); //debug here
check if it has a value, if still none, these are the scenarios i can think of that may have caused your problem
investigate the missing field specially if it is not nullable.
is the _dbContext used here is the same connection string used with everything?
is the [Key] attribute listed on your Contact entity?
public class Contact
{
[Key]
public int Id
}
overridden the SaveChanges function?
is what you are passing Contact contains a Key and it is not 0?
is a delete function called after Update?
try using SQL Profiler to look at the Linq to SQL if it really generated an update query and if it is really pointing at the right [Key]
but if it is still not working properly, you could do
public void Update(Contact contact)
{
var selectedContactToBeUpdated = _dbContext.Contacts.FirstOrDefault(x => x.Id == contact.Id);
if (selectedContactToBeUpdated != null)
{
selectedContactToBeUpdated.PropertyToBeUpdated1 = newValue;
selectedContactToBeUpdated.PropertyToBeUpdated2 = newValue2;
//additional Properties
_dbContext.SaveChanges();
}
}
in the scenario above, it will only generate an Update statement with fields you have changed.
I am migrating a legacy database to a new database which we need to access and "manage" (as oxymoronic as it might sound) primarily through Entity Framework Code-First.
We are using MS SQL Server 2014.
The legacy database contained some tables with computed columns. Typical GUID and DateTime stuff.
Technically speaking, these columns did not have a computed column specification, but rather where given a default value with NEWID() and GETDATE()
We all know that it is very easy to configure the DbContext to deal with those properties as follows:
modelBuilder.Entity<Foo>()
.Property(t => t.Guid)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
modelBuilder.Entity<Bar>()
.Property(t => t.DTS)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
The above would instruct the Entity Framework to ignore submitting any supplied values for such properties during INSERTs and UPDATEs.
But now we need to allow for import of legacy records and maintain the OLD values, including the PRIMARY KEY, which is marked as IDENTITY
This means we would have to set the Id, Guid and DTS properties to DatabaseGeneratedOption.None while inserting those records.
For the case of Id, we would have to somehow execute SET IDENTITY_INSERT ... ON/OFF within the connection session.
And we want to do this
importing process via Code-First as well.
If I modify the model and "temporarily" and set those properties to DatabaseGeneratedOption.None after the database has been created, we would get the typical:
The model backing the context has changed since the database was created. Consider using Code First Migrations to update the database.
I understand that we could generate an empty coded-migration with -IgnoreChanges so as to "establish" this latest version of the context, but this wouldn't be an acceptable strategy as we would have to be run empty migrations back-and-forth solely for this purpose.
Half an answer:
We have considered giving these properties nullable types, i.e.
public class Foo
{
...
public Guid? Guid { get; set; }
}
public class Bar
{
...
public DateTime? DTS { get; set; }
}
While caring about the default values in an initial DbMigration:
CreateTable(
"dbo.Foos",
c => new
{
Id = c.Int(nullable: false, identity: true),
Guid = c.Guid(nullable: false, defaultValueSql: "NEWID()"),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Bars",
c => new
{
Id = c.Int(nullable: false, identity: true),
DTS = c.Guid(nullable: false, defaultValueSql: "GETDATE()"),
})
.PrimaryKey(t => t.Id);
The Question:
But the question remains: Is there a way to switch between DatabaseGeneratedOption.Identity, DatabaseGeneratedOption.Computed and DatabaseGeneratedOption.None at runtime?
At the very least, how could we turn DatabaseGeneratedOption.Identity on/off at runtime?
A certain amount of the configuration of the context is always going to be dependent on the runtime environment - for example, proxy generation and validation. As such, runtime configuration of the Entity Framework DbContext is something I leverage quite heavily.
Although I've never used this approach to switch the configuration of the context on a per use-case basis, I see no reason why this would not work.
In its simplest form, this can be achieved by having a set of EntityTypeConfiguration classes for each environment. Each configuration set is then wired to the DbContext on a per-environment basis. Again, in its simplest form this could be achieved by having a DbContext type per environment. In your case, this would be per use-case.
Less naively, I usually encapsulate the configuration of the context in an environment-specific unit of work. For example, the unit of work for an Asp.Net environment has an underlying DbContext configured to delegate validation to the web framework, as well as to turn off proxy generation to prevent serialisation issues. I imagine this approach would have similar usefulness to your problem.
For example (using brute force code):
// Foo Configuration which enforces computed columns
public class FooConfiguration : EntityTypeConfiguration<Foo>
{
public FooConfiguration()
{
Property(p => p.DateTime).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
Property(p => p.Guid).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
}
}
// Foo configuration that allows computed columns to be overridden
public class FooConfiguration2 : EntityTypeConfiguration<Foo>
{
public FooConfiguration2()
{
Property(p => p.DateTime).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(p => p.Guid).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
// DbContext that enforces computed columns
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new FooConfiguration());
}
}
// DbContext that allows computed columns to be overridden
public class MyContext2 : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new FooConfiguration2());
}
}
This can obviously be tidied up - we usually use a combination of factory and strategy patterns to encapsulate the creation of a runtime specific context. In combination with a DI container this allows the correct set up configuration classes to be injected on a per-environment basis.
Example usage:
[Fact]
public void CanConfigureContextAtRuntime()
{
// Enforce computed columns
using (var context = new EfContext())
{
var foo1 = new Foo();
context.Foos.Add(foo1);
context.SaveChanges();
}
// Allow overridden computed columns
using (var context = new EfContext2())
{
var foo2 = new Foo { DateTime = DateTime.Now.AddYears(-3) };
context.Foos.Add(foo2);
context.SaveChanges();
}
// etc
}
I have a method that performs an insert into a table using Entity Framework 6. The table has an ID field that is populated automatically when it is written to SQL Server. I would like the method to return all of the data that was previously in the ModelState as well as the new record. When I run this code, I get everything except for the new record's ID, which comes back as a 0.
How can I rework this so it returns the new ID as well as the rest of the data from ModelState?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditingInline_Create([DataSourceRequest] DataSourceRequest request, Book book)
{
if (bookViewModel != null && ModelState.IsValid)
{
MyRepository db = new MyRepository();
db.Books.Add(book);
db.SaveChanges();
}
return Json(ModelState.ToDataSourceResult());
}
Function Add return from db result of adding with correct id parameter.
Try this:
book = db.Book.Add(book);
It give you what you want.
If you want return model and modelstate use this:
return Json(new[] { book }.ToDataSourceResult(request, ModelState);
I'm following a tutorial on Silverlight and Prism, where WCF RIA Services are uses to access the Northwind database through an ADO.NET Entity Data Model.
In the Northwind database, there is a table Order_Details which is connected to two other tables (Orders and Products) via foreign keys:
The code to query the database looks like this:
EntityQuery<Order_Detail> detailQuery = _MyDomainContext.GetOrder_DetailsQuery();
_MyDomainContext.Load(detailQuery.Where(
det => det.Order.Customer.CustomerID == cust.CustomerID).OrderByDescending(
det => det.Order.OrderDate).Take(10), OnDetailsLoaded, null);
and then in OnDetailsLoaded:
var details = loadOp.Entities;
if (details != null)
{
var detailsList = details.ToList();
detailsList.ForEach(det => Orders.Add(
new OrderItem
{
ProductName = det.Product.ProductName,
Quantity = det.Quantity,
OrderDate = det.Order.OrderDate.Value
}));
This gives me a Null Exception at new OrderItem, because both Product and Order are null. I have set my EntityDataModel to "Include Foreign Key Columns in the Model". The code is exactly as in the tutorial, I only had to recreate the ADO.Net EntityDataModel and the DomainService to work with my local database, so the error is probably somewhere there.
How would I debug this error? I have no previous experience with databases. I'm using SQL Server 2012 with a Northwind database adapted for 2012, while the tutorial was written with SQL Server 2008 RC.
You probably need to include the Product / Order explicitly in the load method. Try doing a
.Include("Order").Include("Product")
inside the domain context load.
Here is what I had to do to solve the problem. Josh's answer was part of the solution, but I also had to add [Include] attributes to the metadata:
In MyDomainService.cs, add .Include():
public IQueryable<Order_Detail> GetOrder_Details()
{
return this.ObjectContext.Order_Details.Include("Order").Include("Product");
}
In MyDomainService.metadata.cs, add [Include]:
internal sealed class Order_DetailMetadata
{
// Metadata classes are not meant to be instantiated.
private Order_DetailMetadata()
{
}
[Include]
public Order Order { get; set; }
[Include]
public Product Product { get; set; }
}
}
This is described in detail at this website, thanks to Brian Noyes for pointing it out to me.
For the love of heaven and earth I really wish someone could help me out with this issue. It seems everyone has something to say about EF but nothing about Linq-to-SQL.
I am trying to grab some data from my table via a stored procedure, believe me, that's all.
I added the Linq-to-SQL model (LAMP.dbml)
added the stored procedure (getAffectedParcel) from the server explorer. getAffectedParcel takes 2 strings as parameters
Build the application.
Added a domain service class (LAMPService)
Selected the (LAMPDataContext) as the data context class (normally I would tick generate metadata, but since I am not working with tables it's not enabled for ticking)
Added the following function to the LAMPService.cs:
public IEnumerable < getAffectedParcelResult > GetTheAffectedParcels(String v, String vf)
{
return this.DataContext.getAffectedParcel(v, vf).AsEnumerable();
}
Added the following code to a Silverlight page in an attempt to consume the stored procedure:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcels("18606004005", "").Value;
foreach (getAffectedParcelResult GAP in q)
{
MessageBox.Show(GAP.Owner);
}
}
catch (Exception ex)
{
MessageBox.Show (ex.Message.ToString());
}
Build and run application. An error occurs stating:
Object reference not set to an instance of an object.
I have tried ~1000,000 ways to see if this thing would work, but to no avail. Please don't tell me to use Entity Framework, I want to use Linq-to-SQL. Can someone (anyone) help me out here.
//houdini
Calling a stored procedure from the Silverlight client happens in the Async world. Let's consider an example from the AdventureWorks database...
Here's what the Domain Service method looks like. It is calling the EF on a stored procedure in the database called 'BillOfMaterials'.
public IQueryable<BillOfMaterial> GetBillOfMaterials()
{
return this.ObjectContext.BillOfMaterials;
}
Back on the client side, here is the code for setting up the call...
public GetSp()
{
InitializeComponent();
DomainService1 ds1 = new DomainService1();
var lo = ds1.Load(ds1.GetBillOfMaterialsQuery());
lo.Completed += LoCompleted;
}
First, the Domain Service is created, and then it is used to load the results of the stored procedure. In this particular case, the result of this is an instance of 'LoadOperation'. These things are async, so the LoadOperation needs to have a callback for when it is finished. The callback code looks like this...
public ObservableCollection<BillOfMaterial> MyList { get; set; }
void LoCompleted(object sender, EventArgs e)
{
LoadOperation lo = sender as LoadOperation;
if(lo!=null)
{
MyList = new ObservableCollection<BillOfMaterial>();
foreach(BillOfMaterial bi in lo.AllEntities)
{
MyList.Add(bi);
}
dataGrid1.ItemsSource = MyList;
}
}
In this method, the 'sender' is dereferenced into the LoadOperation instance, and then all the goodies from the database can be accessed. In this trivial example, a list is built and passed to DataGrid as the ItemsSource. It's good for understanding, but you would probably do something else in practice.
That should solve your problem. :)
The best advice I can give on Silverlight and RIA is never do ANYTHING on your own until you have tried it in AdventureWorks. You will just waste your time and beat your head against the wall.
Firstly, it seems like your DomainService code is written for Invoke() rather than Query(). You should use Query as it enables you to update data back to the server.
Solution: you should add a [Query] attribute to GetTheAffectedParcels on the domain service.
[Query]
public IQueryable<Parcel>
GetTheAffectedParcels(string ParcelNumber, string LotNumber)
{
// etc.
}
Secondly, RIA Services needs to know which is the primary key on the Parcel class.
Solution: Apply a MetadataType attribute to the Parcel class, which allows you to add metadata to the Parcel class indirectly, since it is generated by Linq2Sql and you couldn't add annotations directly to the ParcelId - it'd get wiped away.
[MetadataType(typeof(ParcelMetadata)]
public partial class Parcel
{
}
public class ParcelMetadata
{
[System.ComponentModel.DataAnnotations.Key]
public int ParcelId {get; set; }
}
Thirdly, modify your client like this. Instead try this on the Silverlight client:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
label1.Text = op.Error.Message;
op.MarkErrorAsHandled();
}
else
{
foreach (var parcel in op.Entities)
{
// your code here
}
}
}
}
catch (Exception ex)
{
label1.Text = op.ex.Message;
}
Much thanks to Chui and Garry who practically kicked me in the right direction :) [thanks guys...ouch]
This is the procedure I finally undertook:
-After adding the data model(LINQ2SQL) and the domain service, I created a partial class [as suggested by Chui] and included the following metadata info therein:
[MetadataTypeAttribute(typeof(getAffectedParcelResult.getAffectedParcelResultMetadata))]
public partial class getAffectedParcelResult
{
internal sealed class getAffectedParcelResultMetadata
{
[Key]
public string PENumber { get; set; }
}
}
Then, Adjusted the Domain Service to include the following:
[Query]
public IQueryable<getAffectedParcelResult> GetTheAffectedParcels(string v, string vf)
{
// IEnumerable<getAffectedParcelResult> ap = this.DataContext.getAffectedParcel(v, vf);
return this.DataContext.getAffectedParcel(v, vf).AsQueryable();
}
Then Build the app, afterwhich the getAffectedParcelResult store procedure appeared in the Data Sources panel. I wanted to access this via code however. Therefore, I accessed it in silverlight [.xaml page] via the following:
LAMPContext db = new LAMPContext();
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
MessageBox.Show(op.Error.Message);
op.MarkErrorAsHandled();
}
else
{
foreach (getAffectedParcelResult gap in op.Entities)
{
ownerTextBlock.Text = gap.Owner.ToString();
}
}
},false);
This worked nicely. The thing is, my stored procedure returns a complex type so to speak. As of such, it was not possible to map it to any particular entity.
Oh and by the way this article helped out as well:
http://onmick.com/Home/tabid/154/articleType/ArticleView/articleId/2/Pulling-Data-from-Stored-Procedures-in-WCF-RIA-Services-for-Silverlight.aspx