Need to assign object id to field when creating object - c#

I've got an object that has a property which, on creation, should always be populated with the object's id(primary key).
How do I do that?
I've tried assigning the id to the field just after the db savechanges(), before returning to the view, but that creates two records on my db.
Also tried loading a new instance of the same, assign the id to the field and save it, but also creates two records.
Thanks for any help.
//model
public partial class supplier
{
public int id { get; set; }
public int ref { get; set; }
}
//controller
public ActionResult Create(supplier sup)
{
if (ModelState.IsValid)
{
db.suppliers.add(sup)
db.SaveChanges();
sup.ref = sup.id;
db.SaveChanges();
}
...
}
Hi all, thanks for all your help... I've found a solution... don't really know if it's the correct one, but it does the job.
For whoever is interested here it is:
supplier needToUpdateSupplier = (from s in db.suppliers where s.id.Equals(supplier.id) select s).ToList().FirstOrDefault();
if (needToUpdateSupplier != null)
{
needToUpdateSupplier.ref = supplier.id;
db.suppliers.Attach(needToUpdateSupplier);
db.Entry(needToUpdateSupplier).Property(e => e.ref).IsModified = true;
db.SaveChanges();
}

Create the object
Save it (SaveChanges). This will give you the id.
Assign the id to your property, using the existing object (you don't need to load it again)
Save again
Notes: The property must be nullable, because up on creation it will be empty.
// A Test object
class MyObject
{
public int ID {set;get;}
public int? MyProperty {set;get;}
}
void Foo()
{
var test = new MyObject();
db.MyObjects.Add(test);
db.SaveChanges();
// at this moment test has the Id set. You can assign it.
test.MyProperty = test.Id;
db.SaveChanges();
}

With EF it should set the ID for you:
db.Posts.Add(post);
db.SaveChanges();
// post.ID should be now set

Related

exclude id field to copy an object into another object

I want to have a duplicate from an object that field Id must be specified with a database that means I shouldn't define it directly.
I find my object and copy all parameters into another object exclude the id field
but it doesn't look list the best way to copy all fields into another object to exclude just the id field.
I have done it this way but looking for a better way
/*duplicate product*/
products product = db.products.Find(id);
products duplicateProduct = new products {
title = product.title,
stock_count=product.stock_count,
price=product.price,
category_id=product.category_id,
context=product.context,
Featured=product.Featured,
ForSale=product.ForSale,
discount_prcent=product.discount_prcent
};
db.products.Add(duplicateProduct);
db.SaveChanges();
/*End duplicate product*/
Dreaming
If c# 9's record type could be used as Entity Framework object we could do:
var a2 = a1 with { Id = 2 };
Current solution
Copying properties may seem tedious but it's OK. It does the job.
Constructors are preferred to type initialisers { A = a ... because if Products gets a new field existing code will not copy all fields and there will be no compile errors.
AsNoTracking
Maybe following would work:
products product = db.products.AsNoTracking().Find(id);
product.Id = null;
db.products.Add(duplicateProduct);
db.SaveChanges();
Okay, there is more than one solution to this problem. As I said in the comment if you want ID values to always be different from each other when you create a copy of the product you can use Copy Constructor. Here is the code snippet for that:
public class Program
{
public static void Main()
{
Product product= new Product()
{
Property1=1,
Property2=2,
ID=3
};
Product copyProduct=new Product(product);
}
}
public class Product
{
public int Property1 {get;set;}
public int Property2 {get;set;}
public int ID {get;set;}
public Product()
{
}
public Product(Product product)
{
Property1=product.Property1;
Property2=product.Property2;
}
}
But be careful when you are copying reference values. You may want to have a look at this article Shallow copy vs. Deep Copy

Creating model with default fields

I Have this Vehicle Model and I want to have some default values for my field when I first create the object and add it to the database. The problem is that this constructor is being called several times, not just in the creation of the Vehicle object.
What am I doing wrong? Should I use another method to create models with default fields?
public class Vehicle
{
[Key]
public int VehicleId { get; set; }
public bool Validated { get; set; }
public DateTime CreationTime { get; set; }
public Vehicle()
{
this.CreationTime = DateTime.Now;
this.Validated = false;
}
}
Edit: Every time I call db.Vehicles.Find(id); it seems to me that Vehicle constructor is called, which is not what I want. I only want constructor to be called the first time object is created in the system, before being inserted to the database
// GET: Vehicles/Edit/5
public ActionResult Edit(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Vehicle vehicle = db.Vehicles.Find(id);
if (vehicle == null)
{
return HttpNotFound();
}
return View(vehicle);
}
If you want to control the initialisation then you could do something like this:
var myVehicle = new Vehicle
{
CreationTime = DateTime.Now,
Validated = false,
};
The default constructor (either the auto-generated one, or one you have written), will always be called, but if it does 'nothing', then when EF creates one, it will not impact anything.
Your update seems to imply you don't want the constructor to get invoked, which I don't think you'll be able to avoid.
When EF Core creates instances of these types, such as for the results
of a query, it will first call the default parameterless constructor
and then set each property to the value from the database. However, if
EF Core finds a parameterized constructor with parameter names and
types that match those of mapped properties, then it will instead call
the parameterized constructor with values for those properties and
will not set each property explicitly.

Child Model List is Empty after DB Add in Entity Framework Core

So I am trying out Entity Framework Core, and am having trouble with my first pair of relational Models. I'm trying to use conventions to form the relationship, as follows:
The principal entity, with conventional principal key (Id) and collection navigation property (the list of Locations):
public class Site
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public int LocationCount { get; set; } = 1;
public List<Location> Locations { get; set; } = new List<Location>();
}
The dependent entity, with conventional foreign key (SiteId) and reference navigation property (Site):
public class Location
{
public int Id { get; set; }
public int SiteId { get; set; }
public Site Site { get; set; }
public int AreaCount { get; set; } = 1;
public Location(Site site) { SiteId = site.Id; }
}
And since I want every Site to already have at least one location, I put this in the Create action of the SiteController, in an attempt to create the dependents along with the principal:
[HttpPost]
public IActionResult Create(Site site)
{
_context.Sites.Add(site);
for (int i = 0; i < site.LocationCount; i++)
_context.Locations.Add(new Location(site));
_context.SaveChanges();
return RedirectToAction("Details", new { Name = site.Name });
}
However, on the Details view, when I try to display the Locations that were created, the list is always empty:
<h2>#Model.Locations.Count Locations</h2>
#foreach (Location l in Model.Locations)
{
<div class="row">
<div class="col-sm-12">#l.Id</div>
</div>
}
Just results in:
0 Locations
Thank you in advance for any help explaining what I've missed!
Edit: By request, here is the body of the Details action in the Site Controller, which gets the Model data for the Details View:
public IActionResult Details(string name)
{
var model = _context.Sites.FirstOrDefault(a => a.Name == name);
if (model == null) model = new Site();
return View(model);
}
Also, per one suggestion, I have fixed the problem of setting the Location.SiteId before Site.Id has a value, since the Id isn't automatically generated on the Site until after SaveChanges() is called. Unfortunately, the result (empty Locations list) is still the same:
[HttpPost]
public IActionResult Create(Site site)
{
int nextsiteid = _context.Sites.Count() + 1;
_context.Sites.Add(site);
for (int i = 0; i < site.LocationCount; i++)
_context.Locations.Add(new Location(siteid));
_context.SaveChanges();
return RedirectToAction("Details", new { Name = site.Name });
}
And changed the Location constructor to use the Id instead of the instance:
public Location(int siteid) { SiteId = siteid; }
Thanks in advance for any further suggestions!
I'm just transcribing the answer I was given, which was provided in the comments of the original post, so that it can easily be found by anyone reading the post.
The issue was, quite simply, that EF Core does not yet support lazy loading. So when the Site entity is populated, it doesn't lazily load the list of dependent locations into the collection navigation property.
The fix is to use eager loading, by way of:
var model = _context.Sites.Include(s => s.Locations).FirstOrDefault(a => a.Name == name);
When loading the Model data of the Site to provide to the View.
Thanks for the solution, Ivan!
if the Lazy Loading is true. No need to loop over or navigate its child. its will save your parent and as well its child. make sure are you getting parents and its child
[HttpPost]
public IActionResult Create(Site site)
{ // debug here are you getting parent & its child ?
_context.Sites.Add(site);
// for (int i = 0; i < site.LocationCount; i++) // No need of it
// _context.Locations.Add(new Location(site)); // No need of it
_context.SaveChanges();
return RedirectToAction("Details", new { Name = site.Name });
}
Updated:
if these two tables are linked with each other & you are manually trying to add location table . Let me give you a simple , hope this will help you
[HttpPost]
public IActionResult Create(Site site)
{ // debug here are you getting parent & its child ?
_context.Sites.Add(site);
// get the last added site id
int? maxId=
_context.Sites.orderbyDecending(x=>x.SiteId).FirstorDefault(x=>x.SiteId);// may be in this line typing mistake , you change it according to linq
foreach(var i in site.Location)
{
// in this loop traverse the child array and override the foreign key id with last added id
i.SiteId =maxId;
_context.Locations.Add(i);
}
_context.SaveChanges();
return RedirectToAction("Details", new { Name = site.Name });
}

How to pass selected class properties to sql table using Dictionary

I have an empty sql table which contains around 100 column headers (fields), using database first approach I have created a single class with all the 100 fields as properties. Now I want to select only 10 fields out of all the properties and insert some data to those fields, such that the existing table should have the inserted data for only those 10 columns and the remaining columns should be updated with NULL. For instance,
public class Product
{
public int UniqueID {get; set;}
public string ProductName {get; set;}
public string type {get; set;}
etc......
}
Now I want the table should be updated with only UniqueID and ProductName and all the remaining fields should have NULL.
I am thinking of using Dictionary<string,object>, to add all the selected properties to dictionary and pass it to the database table.
Please share some ideas on how to implement this scenario using Entity Framework.
If it is MVC and you want to add/edit a subset use a view model:
// this is a viewmodel intended for a particular view, for example AddProductSubset.chtml
public class AddProductSubsetViewModel
{
public int UniqueID {get; set;}
public string ProductName {get; set;}
// add other properties you will be updating from the entity model
// you can also add properties from other entities or things only needed by the view
}
[HttpGet]
public ActionResult AddProductSubset(AddProductSubsetViewModel vm)
{
var vm = new AddProductSubsetViewModel();
// fill in any default values before add/edit
return View(vm);
}
[HttpPost]
public ActionResult AddProductSubset(AddProductSubsetViewModel vm)
{
if (ModelState.IsValid)
{
// map your viewmodel to the entity model. See automapper.
var product = new Product
{
UniqueID = vm.UniqueID, // unless this is identity, then omit
ProductName = vm.ProductName,
otherfield = vm.otherfield,
...
// unmapped entity fields will be set to their default (null, etc)
}
context.Products.Add(product);
context.SaveChanges();
return RedirectToAction("Index")
}
return View(vm);
}
You can reflect and hydrate properties to an instance, then add I think. This should do it
public void ReflectIntoModel<T>(Dictionary<string, object> dic)
{
T m = (T)Activator.CreateInstance(typeof(T));
var prop = typeof(T).GetProperties();
foreach (var a in prop)
{
dynamic val;
if (dic.TryGetValue(a.Name, out val))
a.SetValue(m, val);
}
}
Just make sure you're careful with typing the value, and the fact that the field names are case sensitive

C# Object Oriented Programming, Inheritance, Foreign Keys and DB

the title might be too generic, but I have a very specific question about how to design C# classes based on a relational database.
Let's say we have a table called TPerson and a table called TComment, both tables have one column in common (PERSON_ID, PK on TPerson and FK in TComment).
Let's say we have a web app where we are displaying a list showing all comments from everyone, in this list we are showing the comment and the first name and last name of the author (TPerson) and the date the comment was created as well. I think it is not appropriate to use inheritance (Base class TPerson, derived class TComment) because I don't need the alias for example, I don't want to drag the other columns with me if I only need first name and last name (column TPerson might have lots of columns).
I want a class design that is able to:
Add Person objects and save to DB
Add Comment objects and save to DB
Retrieve the list of comments with first name and last name of the person
Is there a way to create re-usable code by only retrieving or using parts of an object ?
The only way to do this would be to use inheritance and every time I retrieve a Comment, I would also create the Person object that goes with it, but in some parts it would be overkill to retrieve the entire thing when I only need certain parts of it...
If I were to create classes to represent the data, I would go with something like this, Any ideas ?, thanks for your help !:
class Person
{
int personId;
string firstName;
string lastName;
string alias;
DateTime creationDate;
public int PersonId
{
get { return personId; }
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
public string Alias
{
get { return alias; }
set { alias = value; }
}
public DateTime CreationDate
{
get { return creationDate; }
}
//for adding new person object
public Person(string first_name, string last_name, string alias)
{
}
//internal usage
public Person()
{
}
public void Save()
{
//save new person object in DB or update...
}
public static Person GetPerson(int personId)
{
Person p = null;
//call sproc and load from database...
p = new Person();
p.personId = 10;
p.firstName = "First Name";
p.lastName = "Last Name";
p.alias = "Alias";
return p;
}
}
class Comment
{
int commentId;
int personId;
string comment;
DateTime creationDate;
public int CommentId
{
get { return commentId; }
set { commentId = value; }
}
public int PersonId
{
get { return personId; }
set { personId = value; }
}
public string Comment1
{
get { return comment; }
set { comment = value; }
}
public DateTime CreationDate
{
get { return creationDate; }
set { creationDate = value; }
}
public Comment(int person_id, string comment)
{
}
public Comment()
{
}
public void Save()
{
//save or update to DB
}
public static List<Comment> GetComments()
{
List<Comment> comments = null;
//get data from db and load...
comments = new List<Comment>();
comments.Add(new Comment() {
commentId = 1,
personId = 10,
comment = "this is one comment",
CreationDate = DateTime.Now });
comments.Add(new Comment() {
commentId = 1,
personId = 11,
comment = "this is another comment",
CreationDate = DateTime.Now });
return comments;
}
}
Actually why not use entity framework? It treats your database as objects.
Edit:
Basically you can use entity framework to creates database objects for you.
It can maps your table into objects, TComments, TPerson objects etc.
And creates relationship between them with your foregin key.
i.e In your case, because your TPerson table contains Foregin Keys of TComments.
The TPerson object created by Entity Framework will contains a collection of TComment for each TPerson. You will only have to write LINQ to take them out.
And when you modify those object, you only need to execute the Save() method of the entity container and it will save to the database.
This is definitely not a classic case of inheritance - typically, you want to use inheritance to reflect an "is a" relationship - "car is a vehicle", for instance. There's no logical way in which you might say "comment is a person"; you could, of course, say that both comment and person are "things you can store into a database".
Whilst I agree with both King Chan and ken2k that you would probably want to use an existing ORM tool for this, it would help to read up on the underlying concepts of OO design first. I'd recommend Craig Larman's book "Applying UML and patterns" - it's technology-agnostic, but has a great description of how to map objects to a database.
This appears to be a one-to-many association. I wouldn't use inheritance here.
What I would use is an ORM framework. I recommend using Entity Framework.
Step 1: create a database with the schema you provided (actually you could also start from code or model but that's another topic)
Step 2: import your database in Visual Studio and create an entity model from it. This will create classes (called "entities") to represent your objects. This is a mapping between the tables of your database and the objects you manipulate in your application.
Step 3: that's all. Now you can retrieve all your comments from your DB using something like the following lines:
foreach (Comment comment in MyContext.Comments)
{
// Here you have access to the associated Person
Person = comment.Person;
}
Also it'll be easy to get all the comments associated with a person thanks to the one-to-many association:
var comments = Person.Comments;

Categories

Resources