[Table("tblClients")]
public class ClientsTbl
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ClientID { get; set; }
[Key]
public int userid { get; set; }
}
If i have old data (from unknown source) to import which has userid that related with other tables for example invoice etc without using Foreign Key, how can i design my DB model? If i would design like that insert must be like that :
Random a = new Random();
ClientsTbl c = new ClientsTbl()
{
userid = a.Next(-1000, -1),
}
dataContext.Clients.Add(c);
dataContext.SaveChanges();
var client = dataContext.Clients.FirstOrDefault(x => x.userid == c.userid);
if(client!=null)
{
client.userid = c.ClientID;
}
dataContext.SaveChanges();
after inserting a record I need to update "userid" : `` And it gives an error
The property 'userid' is part of the object's key information and cannot be modified.
If i would change all operations to Stored Procedure's (insert and update) everything is ok.
Why Entity Framework doesn't allow to update Key ? (Sql server does)
This is due to the fact that you're referencing the same object. This has been asked before and answered here:
The property 'Id' is part of the object's key information and cannot be modified
I hope this helps you.
OO paradigm has this thing about "object identity". RM does not have this (not in the sense that every identifier of a thing MUST MANDATORILY remain unchanged during the whole lifetime of the thing it identifies).
Tools that attempt to bridge the gap between the two are therefore inevitably faced with a kind of mismatch, and must do one of two things to address it : (a) break the OO paradigm of cast-in-concrete-immutable identity, or (b) enforce the OO paradigm and rule out perfectly legitimate RM operations.
Most if not all of them opt for (b) because that is the easiest way out, at least for the developers.
Related
I've joined a team that uses non standard names for tables and columns, and have trouble building database-first projects with Entity Framework.
Here's my problem:
tFWAClientProcessing (Table)
FWAClientHandling (Primary Key, INT)
iClientID (Foreign Key, INT)
.
tClients (Table)
AClientID (Primary Key, INT)
sClientName (VARCHAR(255))
I need Entity Framework to detect the relationship between these two tables without making changes to those tables in production.
I'd long given up on EDMX and convention-based mapping for relationships and just set up EF via EntityConfiguration classes. Attributes in the entity definitions are another option which should work for simple cases like identifying column names. You can also wire up mapping in the OnModelCreating override directly.
For instance: To have entities called Client and FWAClientProcessing for that table structure:
public class Client
{
public int ClientId { get; set; }
public string ClientName { get; set; }
}
public class FWAClientProcessing
{
public int FWAClientProcessingId { get; set; }
public virtual Client Client { get; set; }
}
public class ClientConfiguration : EntityTypeConfiguration<Client>
{
public ClientConfiguration()
{
ToTable("tClients"); // assumes default schema, i.e. "dbo" in SQL Server. Can add schema name as 2nd parameter otherwise.
HasKey(x => x.ClientId)
.Property(x => x.ClientId)
.HasColumnName("iClientID");
Property(x => x.ClientName)
.HasColumnName("sClientName");
}
}
public class FWAClientProcessingConfiguration : EntityTypeConfiguration<FWAClientPrcessing>
{
public FWAClientProcessingConfiguration()
{
ToTable("tFWAClientProcessing");
HasKey(x => x.FWAClientProcessingId)
.Property(x => x.FWAClientProcessingId)
.HasColumnName("FWAClientHandling");
HasRequired(x => x.Client)
.WithMany()
.Map(x => x.MapKey("iClientID"));
}
}
Assuming that the EntityTypeConfiguration classes are in the same assembly as the entities, and the DBContext, registering them in the context becomes:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.AddFromAssembly(TypeOf(YourDbContex).Assembly);
base.OnModelCreating(modelBuilder);
}
These examples are for EF6, EF Core uses the concept of Shadow Properties for mapping FK relationships without exposing FK properties, and can accommodate the different column naming. EntityTypeConfiguration is available as an Interface with a Configure method accepting the builder.
I favor using the explicit entity type configuration by default as it keeps the configuration nicely isolated and out of the way and can handle all mapping scenarios that might come up that annotations cannot do. It's a bit of a one-off cost to set up, but at least then you have full visibility and control over how the schema is mapped and not simply hoping EF works things out. :)
Use the modern replacement for EDMX-based Database-First and reverse-engineer a code-first model from the existing database. Customizing an EDMX-based model with its mappings is a rabbit-hole of obsolete technology.
This is available for EF Core and EF6.
The reverse-engineered model is then a starting point for you to make model customizations, like mapping the tables and columns to sensible names, and configuring any Navigation Properties that for whatever reason didn't get picked up by the tooling.
You are right, it is easier if people follow the entity framework conventions. However, if you have to deviate from them, OnModelCreating is your friend.
In OnModelCreating, from every Table, column, relation between tables, that are not standard, you can inform entity framework about these deviations.
You can give different table names
You can use other column names
You can say that certain properties should be saved in certain database formats, for instance ProductPrice is a decimal with 2 digits after the decimal point, instead of the default number of digits.
etc.
There seems to be a one-to-many relation between Clients and ClientsProcessing: every Client with primary key Id, has zero or more ClientsProcessings, every ClientProcessing belongs to exactly one Client, namely the Client that the foreign key ClientId refers to.
You want to use unconventional table names, unconventional names for you primary and foreign keys, and you need to inform about what keys are used to define the one-to-many relation.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configure DbSet<Client>:
ver clients = modelBuilder.Entity<Client>();
clients.ToTable("tClients")
.HasKey(client => client.Id)
// property Id should be in "AClientID"
clients.Property(client => client.Id).HasColumnName("AClientID");
clients.Property(client => client.Name).HasColumnName("sClientName");
Apart from different names of the columns, you can also declare whether the properties are required or optional, what format they should have (is a decimal with two digits after the decimal point, or does it have four digits?), etc.
Do something similar for modelBuilder.Entity<ClientProcessing>();
For the one-to-many relation: every Client has zero or more ClientProcessings; every ClientProcessing belongs to exactly one (required!) Client, namely the foreign key that ClientId refers to:
clients.HasMany(client => client.ClientProcessings)
.WithRequired(clientProcessing => clientProcessing.Client)
.HasForeignKey(clientProcessing => clientProcessing.ClientId);
Or if you want, you can start at ClienProcessing: every ClientProcessing has exactly one Client (required!), using foreign key ClientId. Every Client has many ClientProcessings.
modelBuilder.Entity<ClientProcessing>()
.HasRequired(clientProcessing => clientProcessing.Client)
.WithMany()
.HasForeignKey(clientProcessing => clientProcessing.ClientId);
Note: by default this will cascade on delete: whenever you delete a client, you will also delete all its processings: you did define there are no processings without a client.
In some relations, you don't want this, especially many-to-many relations or one-to-zero-or-one relation: a Student may have zero or one School-supplied-Laptop. If you delete the Laptop, you don't want to delete the Student as well. In that case you'll have to add .WillCascadeOnDelete(false)
I know that you set a nullable key in your entity if you want that FK to be nullable:
class ChildEntity
{
// Other properties not shown for brevity
public int? ParentId { get; set; }
public virtual ParentEntity Parent; { get; set; }
}
This will result in a nullable FK. It was suggested here that we should also set the optional relationship in Fluent:
modelBuilder.Entity<ChildEntity>()
.HasOptional(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
but this still doesn't set delete set null. The FK ParentId still has delete set to No Action.
Later in the article, it was suggested that we should run the SQL command in the Seed method of the Configuration class? I'm not sure if it's a problem, but I run Update-Database quite often, and I'd be changing this setting back and forth.
So, is it safe, then, to "go behind EF's back" and change the delete rule to SET NULL in SQL Management Studio (or other app)? Since we're using SqlCommand in the seed method in plain SQL language, I want to say yes, we can go ahead and manually change the delete rule, but I'm not sure. I can't afford to experiment at this point, so I would appreciate an answer for this.
That example puts the sql in the Seed method and that means that it runs every time you call Update-Database. You avoid that by making the modification to the database using the Sql method in a migration. That way it only runs once.
public void Up()
{
Sql(#"ALTER TABLE Products DROP CONSTRAINT Product_Category");
Sql(#"ALTER TABLE Products ADD CONSTRAINT Product_Category
FOREIGN KEY (CategoryId) REFERENCES Categories (CategoryId)
ON UPDATE CASCADE ON DELETE SET NULL");"
}
Error Message: "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
Hello All,
I've created a Code First TPH(Table Per Hierarchy) within MVC and EF with a SQL compact db.
Here's the Class diagram/Hierarchy:
Class Diagram
Client and SalesRep both inherit the BaseUser class. The Key is "UserID" and it's coded with the Data Annotation [Key](I'm aware that 'ID' should set it as well)
Here's where I'm at: I can seed the database with a few entries. When I try to set the "UserID" in the seeding method it seems to ignore it and just apply the UserID in numerical order...(Seems ok to me?)
furthermore here's my DbContext
public class SiteDB:DbContext
{
public DbSet<BaseUser> AllUsers { get; set; }//enable TPH
public DbSet<SalesRep> SalesReps { get; set; }
public DbSet<Client> Clients { get; set; }
}
Next,
I have created a controller for Clients -> ClientsController with strongly typed Razor Views. With this. I now have a CRUD for the Clients. I can create new Clients without any issue, but when I try to edit a Client entry, I get the error message stated above.
I did notice something interesting I stepped through the code and the error is happening on the db.SaveChanges();
When the client is passed back into the ActionResult Edit method the UserID=0! Bizarre? I'm not sure if this is a bug or if it's an actual issue that's causing this.
UserID=0
Your help with this is appreciated. Thanks!
To modify an entity you need to get it from the database: when you pass back the modified values in the Edit action you need to retrive the entity from the db object, getting it by ID, apply the modified values and save it.
Here an example:
public ActionResult Edit(int id, MyModel model){
using (SiteDBdb = new SiteDB()){
Client cl = (from c in db.Clients where c.id == id select c).First();
cl.MyProp = model.MyProp;
...
db.SaveChanges();
}
...
}
I am working on an NHibernate project and have a question regarding updating transient entities.
Basically the workflow is as follows:
Create a DTO (projection) and send over the wire to client. This has a small subset of properties from the entity.
Client sends back the changed DTO
Map the DTO properties back onto the appropriate enitity so an UPDATE statement can be generated and executed by NH.
Save the entity
Point 4 is where I have the issue. Currently I can achieve this update using the session.Merge() method, however it must first load the entity from the db (assume no 2LC) before updating. So, both a select and an update statement are fired.
What I would like to do is create a transient instance of the entity, map the new values from the DTO, then have NH generate a SQL statement using only the properties I have changed. The additional select should be unnecessary as I already have the entity ID and the values required for the SET clause. Is this possible in NH?
Currently using session.Update(), all properties will be included in the update statement and an exception is raised due to the uninitialized properties that are not part of the DTO.
Essentially I need a way to specify which entity properties are dirty so only these are included in the update.
== EDIT ==
For example...
public class Person
{
public virtual int PersonId { get; set; }
public virtual string Firstname { get; set; }
public virtual string Nickname { get; set; }
public virtual string Surname { get; set; }
public virtual DateTime BirthDate { get; set; }
}
And the test case.
// Create the transient entity
Person p = new Person()
p.id = 1;
using (ISession session = factory.OpenSession())
{
session.Update(p);
// Update the entity – now attached to session
p.Firstname = “Bob”;
session.Flush();
}
I was hoping to generate a SQL statement similar to ‘UPDATE Persons SET Firstname = ‘Bob’ WHERE PersonID = 1’. Instead I get a DateTime out of range exception due to BirthDate not being initialised. It shouldn’t need BirthDate as it is not required for the SQL statement. Maybe this isn’t possible?
== /EDIT ==
Thanks in advance,
John
Dynamic-update is what you're looking for. In your mapping file (hbm.xml):
<class name="Foo" dynamic-update="true">
<!-- remainder of your class map -->
Be aware of the potential problems that this may cause. Let's say you have some domain logic that says either FirstName or Nickname must not be null. (Completely making this up.) Two people update Jon "Jonboy" Jonson at the same time. One removes his FirstName. Because dynamic-update is true, the update statement just nulls out Jon and the record is now "Jonboy" Jonson. The other simultaneous update removes his Nickname. The intent is Jon Jonboy. But only the null-out of the Nickname gets sent to the database. You now have a record with no FirstName or Nickname. If dynamic-update had been false, the second update would have set it to Jon Jonboy. Maybe this isn't an issue in your situation, but setting dynamic-update="true" has consequences and you should think through the implications.
UPDATE: Thanks for the code. That helped. The basic problem is NHibernate not having enough information. When you say session.Update(p), NHibernate has to associated a disconnected entity with the current session. It has a non-default PK. So NHibernate knows that it's an update and not an insert. When you say session.Update(p), NHibernate sees the whole entity as dirty and sends it to the database. (If you use session.Merge(obj), NHibernate selects the entity from the database and merges obj with it.) This is not what you really mean. You want to associate your object with the current session, but mark it as clean. The API is somewhat non-intuitive. You use session.Lock(obj, LockMode.None) as below.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var p = new Person {PersonId = 1};
session.Lock(p, LockMode.None); // <-- This is the secret sauce!
p.Firstname = "Bob";
// No need to call session.Update(p) since p is already associated with the session.
tx.Commit();
}
(N.B. dynamic-update="true" is specified in my mapping.)
This results in the following SQL:
UPDATE Person
SET Firstname = 'Bob' /* #p0_0 */
WHERE PersonId = 1 /* #p1_0 */
I need to write a row to the database regardless of whether it already exists or not. Before using NHibernate this was done with a stored procedure. The procedure would attempt an update and if no rows were modified it would fallback to an insert. This worked well because the application doesn't care if the record exists.
With NHibernate, the solutions I have found require loading the entity and modifying it, or deleting the entity so the new one can be inserted. The application does have to care if the record already exists. Is there a way around that?
Does the Id Matter?
Assigned Id
The object has a keyword as an assigned id and is the primary key in the table.
I understand that SaveOrUpdate() will call the Save() or Update() method as appropriate based on the Id. Using an assigned id, this won't work because the id isn't an unsaved-value. However a Version or Timestamp field could be used as an indicator instead. In reality, this isn't relevant because this only reflects on whether the object in memory has been associated with a record in the database; it does not indicate if the record exists or not in the database.
Generated Id
If the assigned id were truly the cause of the problem, I could use a generated id instead of the keyword as the primary key. This would avoid the NHibernate Insert/Update issue as it would effectively always insert. However, I still need to prevent duplicate keywords. With a unique index on the keyword column it will still throw an exception for a duplicate keyword even if the primary key is different.
Another Approach?
Perhaps the problem isn't really with NHibernate, but the way this is modeled. Unlike other areas of the application, this is more data-centric rather object-centric. It is nice that NHibernate makes it easy to read/write and eliminates the stored procedures. But the desire to simply write without regard to existing values doesn't fit well with the model of an object's identity model. Is there a better way to approach this?
I`m using
public IList<T> GetByExample<T>(T exampleInstance)
{
return _session.CreateCriteria(typeof(T))
.Add(Example.Create(exampleInstance))
.List<T>();
}
public void InsertOrUpdate<T>(T target)
{
ITransaction transaction = _session.BeginTransaction();
try
{
var res=GetByExample<T>(target);
if( res!=null && res.Count>0 )
_session.SaveOrUpdate(target);
else
_session.Save(target);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
}
but FindByExample method returns all objects alike not objects with the exact ID what do you suggest ? since I have only object as parameter I don't have access to its specific ID field so I cannot use session.get(Object.class(), id);
Typically, NHibernate can rely on the unsaved-value to determine whether it should insert or create the entity. However, since you are assigning the ID, to NHibernate it looks like your entity has already been persisted. Therefore, you need to rely on versioning your object to let NHibernate know that it is a new object. See the following link for how to version your entity:
http://web.archive.org/web/20090831032934/http://devlicio.us/blogs/mike_nichols/archive/2008/07/29/when-flushing-goes-bad-assigned-ids-in-nhibernate.aspx
Use the session.SaveOrUpdate(object) method.
You can do
Obj j = session.get(Object.class(), id);
if (j != null)
session.merge(myObj);
else
session.saveOrUpdate(myObj);
Query objects where keyword = x, take FirstOrDefault. If it's null, Add new object, if it exists, update object that you got and call saveOrUpdate on it.
This worked for me:
Implementation
public void InsertOrUpdate<TEntity, TId>(TEntity entity) where TEntity : IIdentificableNh<TId>
{
var anyy = session.Get<TEntity>(entity.Id);
if (anyy != null)
{
session.Evict(anyy); //dispatch all data loaded, to allow updating 'entity' object.
session.Update(entity);
}
else
{
session.Save(entity);
}
session.Flush();
}
Entity
public class Caracteristica : IIdentificableNh<int>
{
public virtual int Id { get; set; }
public virtual string Descripcion { get; set; }
}
I had to create an interface (IIdentificableNh) that allows me to access the Id property value.
Usage example:
session.InsertOrUpdate<Caracteristica, int>(new Caracteristica { Id = 2, Descripcion = "Caracteristica2" });
call hibernate.saveOrUpdate() which will check if the object is in the database, update it if it is, and save (i.e. insert) it if it is not.