Customize Primary Key on ObjectContext.SaveChanges() - c#

I just wan't to know if it's possible to assign a customized primary key only when the ObjectContext.SaveChanges() is called?
I'm trying to search for any solutions but it seems like I'am the only one thinking about this.
It is the same as what is happening when you have an identity column which the EF will automatically assign a primary key once the ObjectContext.SaveChanges() is called but instead of the primary key being an auto identity column, I want to customize my own primary key.
Thanks in advance guys.
Edit: Additional Details
example I have this class:
public class Transaction()
{
public string ID { get; set; }
public DateTime TransactionDate { get; set; }
}
I wan't to add the class in the objectset like this:
Transaction trans = new Transaction()
{
ID = null,
TransactionDate = DateTime.Now
};
ObjectSet.AddObject(trans);
Notice that I haven't assign the ID yet, I want it to be assigned only when the user click save which will call the
ObjectContext.SaveChanges();
One the user call this, I will get a new primary key and assign it to the trans instance.

Not ideal but consider changing your model to this. Your POCO now contains some logic which is not good but it allows you to customize the save to create your id from the third party class library. The string ID column will resolve to a primary key of type nvarchar(128) by default. Your ID will be null prior to the save.
public abstract class Entity
{
public virtual void OnSave();
}
public class Transaction : Entity
{
public string ID { get; set; }
public DateTime TransactionDate { get; set; }
public override void OnSave()
{
ID = Guid.NewGuid().ToString(); //call to class library
}
}
You can hook into the SaveChanges method by overriding it in your DbContext
public class MyContext : DbContext
{
public DbSet<Transaction> Trans { get; set; }
public MyContext () : base()
{
}
public override int SaveChanges()
{
var changedEntities = ChangeTracker.Entries();
foreach (var changedEntity in changedEntities)
{
var entity = (Entity)changedEntity.Entity;
entity.OnSave();
}
return base.SaveChanges();
}
}

Related

How can I specify to add item to the database without also adding the foreign key?

I have the following Models:
public interface Item
{
public int Id { get; set; }
}
public class ComponentOwner : Item
{
public int Id { get; set; }
public Component Component { get; set; }
public AppUser? User { get; set; }
public DateTime ModifiedDate { get; set; }
public AppUser? UpdatedBy { get; set; }
}
public class AppUser : IdentityUser
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
}
and the following async Task that saves the item to the database:
private async Task<Item> SaveItem(Item item)
{
Item updatedItem = null;
using var context = _dbContextFactory.CreateDbContext();
try
{
if (item.Id == 0)
{
context.Add(item);
await context.SaveChangesAsync();
}
When I Save a new ComponentOwner, context.Add(Item) adds the item, but also tries to add a new 'AppUser' at the same time. This causes issues because the AppUser is already created.
Is there a way that I can specify to add the ComponentOwner but not the AppUser?
as soon as 'Context.Add(item)' is hit, it wants to add an AppUser as well as the Component. I only want it to add the ComponentOwner however..
EF Core relies on tracking to determine what to do with entities. In this case it seems that item.User is not tracked, so EF tries to add it. There are multiple possible solution to this. To name a few:
If you are sure that user exists, you can just attach the entity:
if(item.User is not null)
context.Users.Attach(item.User); // or just context.Attach(item.User);
Fetch user from database and assign it to the root entity:
if (item.User is not null)
{
var user = context.Users.FirstOrDefault(u => u.Id == item.User.Id); // TODO: handle null
item.User = user;
}
Use Find:
Finds an entity with the given primary key values. If an entity with the given primary key values is being tracked by the context, then it is returned immediately without making a request to the database. Otherwise, a query is made to the database for an entity with the given primary key values and this entity, if found, is attached to the context and returned. If no entity is found, then null is returned.
if (item.User is not null)
{
var user = context.Users.Find(item.User.Id); // TODO: handle null
item.User = user;
}

My database entity has a generic base BaseEntity<key>, but how to map Id value of base entity to database entity?

How can I interchangeably use entity base Id or database column Id
public partial class Orders : EntityBase<int>
{
protected Orders() { }
public int Orderid { get; set; }
public override int Id { get => base.Id; set => base.Id = Orderid; }
}
Entitybase:
public abstract class EntityBase<T> : IEntity<T>
{
public virtual T Id { get; set; }
}
Question: Can I map this Id of EntityBase and db column's primary key(for eg: order entity's key -> orderId) to sync values ( In app code, either user set Id of base or orderId of entity both should contain same value and also when retrieved also these values, at a given time, should return same value. Is there any way to achieve above synch feature via fluent API in the OnModelCreating() method?
P.S: If you have not understood the question, please say which part does not have clarification, instead of using authority :)
Basically If I understand your question correctly, you want to have a base class with an Id, but in SQL you've named your Id Column differently. You want to Alias your updates so that when you set they Id, they also set the DB Id.
This is more of an architecture, thing, if you really needed to you could write tool that helps you scaffold automatically, but that seems buggy and more complex.
Instead I'll try to fix this problem by modifying your architecture.
A simple way to fix this problem, is just to work with Id's
public partial class Orders : EntityBase<int>
{
protected Orders() { }
[Column("Orderid")]
public int Id { get; set; }
}
You can map a column directly to the Id property, and avoid the whole issue all together.
If you're set on having both OrderId and Id
public partial class Orders : EntityBase<int>
{
protected Orders() { }
public int Orderid { get; set; }
public override int Id { get => Orderid; set => Orderid = value; }
}
but using that architecure is generally a bad idea, It's messy since your EntityBase Is Generic an seems error prone.
If i understand right you need that your Orders.Orderid is an autoincremental-PrimaryKey for your DB table right?
Supposing you're using SQLite (if not probably is similar but not the same words) you can do something like that:
public partial class Orders : EntityBase<int> {
[PrimaryKey, AutoIncrement]
public int Orderid { get; set; }
protected Orders() { }
public override int Id { get => base.Id; set => base.Id = Orderid; }
}
When you create/update your table using the orders "model" SQLite should do the rest.
L-

How to create tables in codefirst approach

I am working on Login page creating in C# using Code First approach , in this I am getting lot of errors while trying to execute the code. I am a fresher and new to this .
Can you please review my code and help me what I missed in this ?
Rule are not creating and getting the multiple errors. Your help would help me to understand what went wrong in this.
My Class "Class1.cs"
public class Login
{
[Required]
public string username { get; set; }
[Required]
public string password{ get; set; }
}
}
public class LoginContext : DbContext
{
public LoginContext() : base("LoginDBConnectionString")
{
Database.SetInitializer<LoginContext>(new DropCreateDatabaseIfModelChanges<LoginContext>());
}
public DbSet<username> username { get; set; }
public DbSet<password> password { get; set; }
}
Context.cs
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.Data.Entity;
using Jquery1.Models;
namespace Jquery1.Models
{
public class Logincontext: DbContext
{
public Logincontext () : base ("LoginDBConnectionString")
{
}
public DbSet<Login> Logins{ get; set; }
}
}
class program
{
static void Main(string[] args)
{
using (var ctx = new Logincontext())
{
ctx.Database.Create();
}`enter code here`
}
}
Hi Let me explain this using fluent api bear with me a little please,
Create Your DbContext First:
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=MyConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, YourApplication.Migrations.Configuration>("MyConnection"));
}
public DbSet<Users> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//here you can MAP Your Models/Entities, but i am going to show you something more interesting. so keep up.
modelBuilder.Configurations.Add(new UsersMap());
}
}
Create a Migration Folder in your app root And make Configuration class there(Why?! so that everytime you made a change migration of EF will update the Tables for you):
internal sealed class Configuration : DbMigrationsConfiguration<YourApplication.Infrastructure.Data.MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
//this feature on true can result in unwanted/unexpected dataloss
AutomaticMigrationDataLossAllowed = true;
ContextKey = "YourApplication.Infrastructure.Data.MyDbContext";
}
protected override void Seed(YourApplication.Infrastructure.Data.MyDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
Now Go on And Create Your POCO Classes. I try to write my codes very clean. That's why when for example i made a Model like below, i create an EntityBase for every Id:
public class EntityBase
{
public int Id { get; set; }
}
And Implement it to my Model :
public class User: EntityBase
{
public string Example1{ get; set; }
public string Example2{ get; set; }
public string Example3{ get; set; }
}
And For Mapping I Create another Class like below and use Fluent Api:
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
//declaring the table name
ToTable("TblUser");
//declaring primary key of the table
HasKey(x => x.Id);
//declaring a property from poco class is required
Property(x => x.Example1)
.IsRequired();
//etc
}
}
be aware if you are using fluent api, you Shouldn't use Data Annotations. Happy Coding.
Entity frame-work uses a concept of standards-or-else. If you want your items fairly standard, you don' have to provide a lot of information. If you want your tables to have different names, or your columns to be different than standard you'll have to provide extra information using either Attributes (the method you use) or fluent API.
Every class that should become a table should have an Primary key. The default is to give your class a property Id, or to give the property the name of your class followed by Id:
public class Login
{
public int Id {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Login> Logins {get; set;}
}
This should be enough to give you a table with the default name, which is the plural of your class name Logins. Each record in the table has three columns:
Id: the primary key
UserName: a string, which in this case may be null
PassWord: a string which may be null.
Your Requires attribute will ensure that your UserName and Property are not null, but they won't prevent them being empty. I'm not sure that's enough for you.
Instead of Id your are free to use LoginId as foreign key:
public int LoginId {get; set;}
You'll see both methods used. Both have their advantages. The use of Id shows you immediately which property is the primary key. LoginId could also be used as foreign key. The name LoginId alone is not enough to see whether it is a primary key or a foreign key.
The defaults are usually plurals for collections of items where the item is the singular form. Here you'll see Login as class, and Logins as a set of objects of this class.
The article that helped me a lot to get on track using Entity Framework was this entity framework tutorial
The tutorial tells you about how to use defaults, how to create one-to-many relations with foreign keys, many-to-many, various inheritance strategies, and what to do if you are not satisfied with a default model.

Automatically create record in table when another table gets a new record

I have created the following two classes:
public class QuoteGeneral
{
public int QuoteGeneralID { get; set; }
public QuoteStatus Status { get; set; }
//Code and annotation removed for clarity
public int? QuoteCustomerID { get; set; }
public virtual QuoteCustomer QuoteCustomer { get; set; }
public virtual QuoteProduction QuoteProduction { get; set; }
}
public class QuoteProduction
{
public int QuoteGeneralID { get; set; }
public int testrun { get; set; }
public virtual QuoteGeneral QuoteGeneral { get; set; }
}
And then I added:
modelBuilder.Entity<QuoteProduction>()
.HasKey(e => e.QuoteGeneralID);
modelBuilder.Entity<QuoteGeneral>()
.HasOptional(s => s.QuoteProduction)
.WithRequired(ad => ad.QuoteGeneral);
To set the QuoteProduction.QuoteGeneralID field to be both the primary and foreign key. So, now when the database is generated it will look something like:
I would like a record to be automatically created in the QuoteProduction table whenever one is entered into the QuoteGeneral table. Is this possible? Or, do I have to add it myself? If I do have to add it myself, what is the best way of capturing the QuoteGeneralID value? I don't think SELECT MAX(QuoteGeneralID) FROM QuoteGeneral will always be reliable since I may have a lot of users working with the database, and it may pull the wrong value.
EDIT: What worked for me (for reference to possibly help other in the future)
public ActionResult Create([Bind(Include = "list of bind fields")] QuoteGeneral quoteGeneral)
{
if (ModelState.IsValid)
{
db.QuoteGenerals.Add(quoteGeneral);
var qp = new QuoteProduction
{
QuoteGeneralID = quoteGeneral.QuoteGeneralID
};
db.QuoteProductions.Add(qp);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(quoteGeneral);
}
As the QuoteGeneralID is a foreign key into the QuoteGeneral table you can simply create the QuoteProduction record:
var qp = new QuoteProduction
{
QuoteGeneral = quoteGeneral
};
The quoteGeneral object doesn't have to have been saved first.
Then when you save the quoteGeneral record the associated QuoteProduction record will get saved at the same time.
I would like a record to be automatically created in the QuoteProduction table whenever one is entered into the QuoteGeneral table.
Create trigger trg_test
on dbo.QuoteGeneral
after insert
as
begin
Set nocount on
insert into dbo.QuoteProduction
select requiredcolumns
from
inserted i
End
After you do .SaveChanges() you should be able to get the id of that object and then use that to add the child table.

Self referencing foreign key

I'm using Entity Framework Code-First to rebuild an application that used to run from an Access database. One of the requirements is that the new data schema should be auditable, that is it should show who created a record and who updated it and when etc.
I've created a base Entity class as follows:
public class Entity
{
public int Id { get; set; }
public int CreatedByUserId { get; set; }
public int? UpdatedByUserId { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User UpdatedBy { get; set; }
}
Then I created a class that inherits from EntityTypeConfiguration as follows
public class BaseEntityTypeConfiguration<T> : EntityTypeConfiguration<T> where T : Entity
{
Property(e => e.Id).HasColumnName(typeof(T).Name + "Id");
HasRequired(e => e.CreatedBy).WithMany().HasForeignKey(e => e.CreatedById);
HasOptional(e => e.UpdatedBy).WithMany().HasForeignKey(e => e.UpdatedById);
}
Now I create configurations that inherit from BaseEntityTypeConfiguration for the rest of my business classes that inherit from my Entity class.
The problem comes when I try to make my User class inherit from entity as follows:
public class User : Entity
{
public string Username { get; set; }
// etc
}
I'll be adding a "ghost" user for records where the evidence isn't there to determine who created the record, but this ghost user will essentially be created by itself.
I'm getting the following error from Entity Framework when I try to add this ghost user:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements or store-generated values.
There may be problems in my domain model that could be causing this error, but my theory is that it's down to this user that's trying to create itself in this instance.
Is having a self-referencing foreign key constraint problematic?
Your PK is an identity column and you're setting the ghost user's CreatedByUser property with itself. This causes a chicken/egg scenario - you need the User.Id value as the User.CreatedById value to insert the record into the DB table, but you don't know what User.Id is until after the record is inserted.
If you can be sure of the identity's seed value (EF seems to default to 1), you can set the CreatedByUserId property to that value instead of CreatedByUser.
Otherwise, create your ghost user by executing a SQL statement allowing you to manually set the Id and CreatedByUserId fields to the same value then reseed the identity to Id + 1.
Example of the former:
public class UserWithCreatedBy
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int Id { get; set; }
public int CreatedById { get; set; }
[ForeignKey( "CreatedById" )]
public UserWithCreatedBy CreatedBy { get; set; }
}
static void Main( string[] args )
{
using( var db = new TestContext() )
{
var u = new UserWithCreatedBy();
// doesn't work with identity
//u.CreatedBy = u;
// this will work as long as you know what the identity seed is
// (whatever the next identity value will be)
u.CreatedById = 1;
db.UsersWithCreatedBy.Add( u );
db.SaveChanges();
}
}

Categories