Basically I know that find() works when it checks the entity by the stated primary id. My question is I have a Model and one of its property have [Key] data annotation but this column is not the actual primary key in the database.
Something like this
public class ModelMetadata (I'm using a metadata)
{
public int ID{get;set;} <<--actual rownum or db id
[Key]
public guid ItemId{get;set;} <<-- my desired id for find()
public string prop1{get;set;}
}
now if I use dbcontext.Model.Find(id) will it return the result based on the 'ItemId'? or the actual assign Primary key 'ID'?
Thank you.
Use a where conditional single or default like
User myUser = myDBContext.Users.SingleOrDefault(user => user.Username == username);
From Programming Entity Framework: DbContext:
One of the great things about Find is that it doesn't unnecessarily
query the database. It's also capable of finding newly added objects
that haven't yet been saved to the database. Find uses a simple set of
rules to locate the object (in order of precedence):
Look in memory for an existing entity that has been loaded from the database or attached to the context.
Look at added objects that have not yet been saved to the database.
Look in the database for entities that have not yet been loaded into memory.
Related
What would be the best practice for storing some entity in a SQL Server database, when I have an Id property which is autoincremented (identity)?
This is for a .NET Core application, using Entity Framework Core. I suppose that I could just create some new entity without the identity id, and move the values of my old entity to my new entity the store it in the .Add method of my current context, or execute a command for enable the 'SET IDENTITY_INSERT ON', but both of those approaches looks messy, I'm guessing that there is a cleaner way to achieve this.
//user has autoincremented property
public IEnumerable<User> SaveUser(User user)
{
context.add(user);
context.SaveChanges(); // Exception Cannot insert explicit value for identity column in table
}
I expect to get to do it in a way that I could reuse it in the whole application, because if my entities keep increasing in size, I would have to write this messy code all around.
To start with, I would like to ask/point at your model class. Not sure how you are having your EF on .net core, but if you are to have a model (be it code first or model first),
lets just say, your Entity model looks similar to below:
User
{
UserId (as int),
UserName (as string),
BirthDate (as date)
}
You can achieve the identity insert by below approach:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId {get;set;}
public string UserName {get;set;}
public DateTime BirthDate {get;set;}
}
Please explore and learn about Code first approach, Modelling your data, repository patterns (may be the ideal in my perspective but depends on case or could be a good learning) and see to the attributes, annotations decorations for EF models.
To explain on what actually drives the auto identity by the above annotations,
Identity generation (auto identity prop)
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
is the driving factor, also depends on the data type. some data types may need additional param configurations. Explore when you try out on your solution. For example, you can use Guid data type and see how it turns out in your Db.
primary key or key attr
[Key]
is as simple to denote as the primary key field in the model/entity structure. This may not be helping or running the auto identity but, i am explaining about this on why i added this attribute in my sample code above.
Please see the class below:
public class UndergraduateEntityTypeConfiguration : IEntityTypeConfiguration<Undergraduate>
{
public void Configure(EntityTypeBuilder<Barclaycard> undergraduateConfiguration)
{
undergraduateConfiguration.HasData(
new Undergraduate(1, "Undergraduate")
);
}
}
and the code below:
public class StudentEntityTypeConfiguration : IEntityTypeConfiguration<Student>
{
public void Configure(EntityTypeBuilder<CreditCard> studentConfiguration)
{
studentonfiguration.ToTable("Student", StudentContext.DEFAULT_SCHEMA);
studentConfiguration.HasKey(o => o.Id);
studentConfiguration.Property(o => o.Id)
.ForSqlServerUseSequenceHiLo("studentseq", StudentContext.DEFAULT_SCHEMA);
studentConfiguration.HasDiscriminator<string>("Type")
.HasValue<Graduate>("Graduate")
.HasValue<Vanquis>("Undergraduate");
}
}
Notice the Student ID field (in the Student table) uses a HiLo sequence. However, in UndergraduateEntityTypeConfiguration I have to explicitly pass an ID number to the Undergraduate constructor to add the Undergraduate record to the Student table.
The code works as expected. However, I am confused why I have to pass an ID (1) myself. Is there a way of creating the Undergraduate record like this:
undergraduateConfiguration.HasData(
new Undergraduate("Undergraduate")
);
Note that this time no ID is passed to the constructor. If I do this, then I see an error, which says: "The seed entity for entity type 'Undergraduate' cannot be added because there was no value provided for the required property 'Id'".
No. This is a specific requirement for EF Core data seeding with HasData (a.k.a Model seed data) coming from the design of the feature.
It's not specific for HiLo sequence generated columns, but all auto generated columns (including the most used auto increment (identity) and database sequence generated columns). The explanation in the documentation is as follows (pay special attention to the first bullet):
This type of seed data is managed by migrations and the script to update the data that's already in the database needs to be generated without connecting to the database. This imposes some restrictions:
The primary key value needs to be specified even if it's usually generated by the database. It will be used to detect data changes between migrations.
Previously seeded data will be removed if the primary key is changed in any way.
If it's a problem for you, consider switching to the more traditional way of data seeding.
How can I get the value of a foreign key without accessing the navigation property so that I don't need to load the full entity?
public class A
{
public int Id {get;set;}
// ...
}
public class B
{
public virtual A A {get;set;}
// ...
}
int idOfA = MyB.A.Id; // slow way of doing it.
I'm using Entity Framework 6 ModelFirst+DbContext in VS2012.
Previously I used an old EF Version+ModelFirst+ObjectContext.
I have bitten the bullet and migrated by creating the model from the old database.
Getting the entity key was possible with the old ObjectContext:
EntityReference<EntityType> reference = MyB.AReference; // AReference was generated by EF
int id = (int)reference.EntityKey.EntityKeyValue[0].Value;
But now the code generator no longer generates EntityReferences for each One or ZeroOrOne navigation property.
So how can I get the foreign key in EF6+DbContext?
Here are some ideas of what I tried/could do but failed/didn't want:
I could just ignore the bad performance and load the full entity to get its primary key.
Using the [ForeignKey]attribute or EF's Fluent API. But I don't know how I can or if I should do that. Maybe it doesn't even work with a model first approach, because "OnModelCreated" isn't called.
I modify the database-entity mapping (the xml stuff in the edmx code) so that for each foreign key an additional property (public int *Id {get;set;}) will be mapped. But I never did that and don't know where to start reading.
When I created the first EF 6.0 model from my old database, there was an option "Include foreign key columns in the model". I didn't activate that last time, which in hindsight was wrong. But doing it again would be a lot of work (manually setting entity inheritance, etc.).
Using the relationship manager. But my generated entities no longer seem to implement the IEntityWithRelationships interface. Even though I think I satisfy the conditions for proxies and I checked in the debugger that proxies classes are created.
(edit: Someone else had a similar problem with IEntityWithRelationships+IEntitiyWithChangeTracker. See solution in the comments of this question.)
Here's the code that I use (granted it's not Model First, but there's nothing specific in it to either approaches).
In the sample I have products, and to each product belongs a category. Here's how I can query the CategoryId in a Product entity wihtout loading the Category:
Model1 context = new Model1();
var product = context.Products.First();
RelationshipManager relMgr = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetRelationshipManager(product);
IEnumerable<IRelatedEnd> relEnds = relMgr.GetAllRelatedEnds();
IRelatedEnd relEnd = relEnds.Where(r => r.RelationshipName.EndsWith("Product_Category")).Single();
EntityReference<Category> entityRef = relEnd as EntityReference<Category>;
var entityKey = entityRef.EntityKey;
int categoryId = (int)entityKey.EntityKeyValues[0].Value;
I'm having problems sorting through all the Google results for my search terms; too much information that is close but not what I'm looking for, so... off to StackOverflow!
I have three tables, Stocks, StockProperties, and StockPropertyTypes.
One Stock record has zero or more StockProperties associated with it, and has a unique column symbol. Each StockProperty record has exactly one reference to a StockPropertyType, and exactly one reference to a Stock. The StockPropertyTypes table has a unique column code. The entities for these tables do not have the FK id's for the references, just the classes. For example, the StockProperty entity looks like:
public class StockProperty
{
public virtual Guid StockPropertyId { get; set; }
public virtual Stock Stock { get; set; }
public virtual string PropertyValue { get; set; }
public virtual StockPropertyType Type { get; set; }
}
I want to pass into a method a stock symbol, a type code, and a value, and create a StockProperty record using HQL, but I'm not sure how to write that query. The Stocks table and the StockPropertyTypes have no relation. Seems like this should be some nested HQL query, but I'm not sure how to differentiate between a property and a referenced entity.
Can someone educate me what that HQL query should look like?
I should add, my goal here is to do this with one db trip; I don't want to load the Stock and StockPropertyType entities before creating the StockProperty record.
The typical way to do this is to load the Stock and StockPropertyType from the ISession. Then create a StockProperty to save using ISession.Save().
As you mention, this requires a few extra trips to the DB. One way to avoid this is to execute SQL directly as follows:
session
.CreateSQLQuery(#"insert
into StockProperty(StockSymbol, Value, TypeCode)
values (:stockSymbol, :value, :typeCode)")
.SetProperty("stockSymbol", stockSymbol)
.SetProperty("value", value)
.SetProperty("typeCode", typeCode)
.ExecuteUpdate();
You are kind of bypassing NHibernate here, but it is more efficient.
Personally, I would consider loading the related entities into memory unless you are experiencing a bottleneck. You can load both the Stock and StockPropertyType in a single DB call by using the Future<T>() paradigm.
Alternatively...
You could try fiddling with <sql-insert> inside of your hibernate mapping file. This allows you more control over how the insert is generated. You might want to add some properties StockId and StockPropertyTypeId that are only used during insert.
Update
Added mappings below
Question summary
I have a database with many required foreign key fields and a code base with many unidirectional associations. I want to use NHibernate, but as far as I can tell, I either have to make the foreign key fields in the database NULLable (not a realistic option) or change the associations to bidirectional (not ideal either). Any other options that I've missed?
Backgrounds
I've joined a project that uses NHibernate to map tables 1:1 to so-called "technical" objects. After data retrieval, the objects are mapped to the actual domain model (AutoMapper style,implemented differently). I know that this is an unnecessary step and I want to propose removing it to the team. However, I'm running into an issue.
The domain model contains many unidirectional associations: the Case object has a list of Persons associated with the case, but the Persons do not hold a reference to the Case object. In the underlying database scheme, the Person table has a required foreign key field that references the case Id. The data model:
[ERD]
PERSON
CASE Id* Ids are generated by the DB
Id* <--FK-- CaseId* * denotes required fields
(other) (other)
The domain model looks like this:
public class Person : DomainEntity
{ // DomainEntity implements Id. Non-essential members left out }
public class Case : DomainEntity
{
public virtual IList<Person> Persons { get; set; }
}
Calling session.Save() on a Case leads to a database error (CaseId required when inserting into Person), because NHibernate starts with inserting the Person entries, followed by the Case entry and finishes by updating the CaseId column in the Person entries. If the CaseId column in the database is altered to non-required (allow NULLs), everything works as it should... however, that change is not an option at the moment (the database model is shared by several apps for at least another year).
The only way I have found to get NHibernate to execute the database actions correctly is by changing the association to bidirectional, i.e., by changing Person to
public class Person : DomainEntity
{
public virtual Case Case { get; set; }
}
This would involve significant changes to the existing codebase however, so I would prefer alternatives, if they exist. I've played around with component mappings, but that is a bad fit since most associations in our model are not actual (UML) compositions. Are there any other options that I've missed? TIA!
EDIT
The (Fluent) mapping for Case looks like this:
public class CaseMapping : ClassMap<Case>
{
public CaseMapping()
{
Not.LazyLoad();
Id(c => c.Id).GeneratedBy.Identity();
Map(x => x.Code).Not.Nullable().Length(20);
Map(x => x.Name).Not.Nullable().Length(100);
HasMany<Person>(x => x.Persons)
.AsBag()
.KeyColumn("CaseId")
.ForeignKeyConstraintName("FK_Person_Case")
.Cascade.AllDeleteOrphan();
}
}
If I use SessionSource.BuildSchema for a test database, this generates a Person table with a nullable CaseId column. I have not found a way for this to work with a non-nullable CaseId field without bidirectional associations. The executed (pseudo) SQL statements:
INSERT INTO Case...
select ##identity
INSERT INTO Person /* (all columns except CaseId) */
select ##identity
UPDATE Person SET CaseId = ? WHERE Id = ?;#p0 = 2, #p1 = 1
I think you may be out of luck here. The docs at http://nhibernate.info/doc/nh/en/index.html#collections-onetomany state:
If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true"