I'm working with EF 6 and I need to create a model ignoring nested properties.
I have a model like this (not the same but it's similar)
public class Client
{
public int ID { get; set; }
public string Name { get; set; }
public int? IdEnt { get; set; }
public Enterprise Enterprise { get; set; }
public int? IdContact { get; set; }
public Contact DirectContact { get; set; }
public Client(string Name, Enterprise enterprise, Contact contact)
{
this.Name = Name;
Enterprise = enterprise;
DirectContact = contact;
}
}
public class Contact
{
public int IdContact { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
}
public class Enterprise
{
public int IdEnt { get; set; }
public string NameEnt { get; set; }
public string Dir { get; set; }
}
Contacts are created when I create a new Client, but Enterprise is an entity that exist in DB, so I only need to connect this properties but when I add a new Client and save changes appears an error indicate I try to insert an Enterprise (DB has automatic primary key in Enterprise table)
This is the transaction that I use to create a Client
bool transaction = false;
string msj = "";
var strategy = Context.Database.CreateExecutionStrategy();
strategy.Execute(() =>
{
using var dbContextTransaction = Context.Database.BeginTransaction();
try
{
Context.Set<Client>().Add(client); // <= client is created with constructor before this part of code
Context.SaveChanges();
dbContextTransaction.Commit();
Transaccion = true;
}
catch (Exception e)
{
dbContextTransaction.Rollback();
msj = e.InnerException.ToString();
/*
Exception:
Cannot insert explicit value for identity column in table 'X' when IDENTITY_INSERT is set to OFF.
I cannot set off this cause I only need connect the entities.
*/
msj = e.Message;
}
});
How I can this?
I'm sorry for my explication and my english if i said something wrong.
Extra:
I configured the entities with this code:
public class ClientConfig : IEntityTypeConfiguration<Client>
{
public void Configure(EntityTypeBuilder<Cliente> builder)
{
builder.ToTable("U_Clients");
builder.HasKey(ent => ent.ID);
builder.Property(i => i.ID)
.HasColumnName("IdClient");
builder.HasOne(ent => ent.Contact)
.WithMany()
.HasForeignKey(e=>e.IdContact);
builder.HasOne(ent => ent.Enterprise)
.WithMany()
.HasForeignKey(e=>e.IdEnterprise);
}
}
Related
I am implementing Entity Framework in ASP.NET Core-6 Web API and MSSQL DB. I have this code:
Models:
public class BankUser
{
public Guid { get; set; }
[ForeignKey("UserId")]
public string UserId { get; set; }
public string Description { get; set; }
[ForeignKey("BankBranchId")]
public Guid BankBranchId { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual BankBranch BankBranch { get; set; }
}
public class BankBranch
{
public Guid { get; set; }
public string BranchName { get; set; }
public virtual BankUser BankUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual BankUser BankUser { get; set; }
}
BankBranch has many BankUser, while BankUser and Application User is one-to-one.
Configuration:
public class ApplicationUserConfigurations : IEntityTypeConfiguration<ApplicationUser>
{
public void Configure(EntityTypeBuilder<ApplicationUser> builder)
{
builder.Property(u => u.IsDeleted).HasDefaultValue(false);
}
}
public void Configure(EntityTypeBuilder<BankBranch> builder)
{
builder.ToTable(name: "bank_branches");
builder.HasKey(b => b.Id);
builder.Property(b => b.Id).HasDefaultValueSql("NEWID()");
builder.HasIndex(b => b.BranchName).IsUnique();
builder.HasIndex(b => b.BranchNumber).IsUnique();
}
}
public class BankUserConfigurations : IEntityTypeConfiguration<BankUser>
{
public void Configure(EntityTypeBuilder<BankUser> builder)
{
builder.ToTable(name: "bank_users");
builder.HasKey(b => b.Id);
builder.Property(b => b.Id).HasDefaultValueSql("NEWID()");
}
}
Then this is the Dto:
public class BankUserCreateDto
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Role { get; set; }
public string MobileNumber { get; set; }
public Guid BankBranchId { get; set; }
}
Finally the implementation:
public async Task<Response<string>> CreateBankUserAsync(BankUserCreateDto model)
{
var response = new Response<string>();
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
var bankUser = _mapper.Map<ApplicationUser>(model);
var bankPassword = "#Password*123";
var result = await _userManager.CreateAsync(bankUser, bankPassword);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(bankUser, model.Role);
var user = new BankUser()
{
UserId = bankUser.Id,
BankBranchId = model.BankBranchId,
Description = model.Description,
CreatedBy = _currentUserService.UserName
};
await _unitOfWork.AdminUsers.InsertAsync(user);
await _unitOfWork.Save();
_logger.Information("Bank User created successfully");
response.StatusCode = (int)HttpStatusCode.Created;
response.Successful = true;
response.Data = _mapper.Map<BankUserDto>(bankUser);
response.Message = "Bank User created successfully!";
transaction.Complete();
return response;
}
return response;
};
}
When I submitted, I got this error:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): The INSERT statement conflicted with the FOREIGN KEY constraint "FK_bank_users_bank_branches_BankBranchId". The conflict occurred in database "ERPs", table "dbo.bank_branches", column 'Id'.
The statement has been terminated.
How do I get this sorted out?
Thanks
I think you're having an issue with BankBranch not being populated. If that entity set (table) is empty, then when you're trying to add a BankUser there is no branch to map them to.
Also, you mentioned that BankBranches have many BankUsers. Your code explicitly says in BankBranch:
public virtual BankUser BankUser { get; set; }
This communicates that there is 1 BankUser to the BankBranch. I think you'd rather want:
public virtual IColelction<BankUser> BankUsers { get; set; }
And it seems you're mixing Attributes with FluentAPI. While this can be done, I would suggest trying to stick to one way or the other unless there's a reason not to. Even MS-EF says that attributes can do much of hard work, but FluentAPI is better for your edge cases.
I am looking at Azure Mobile App and have setup a test service and client.
I've setup the following Entities service-side:
public class Hotel : EntityData
{
public string Title { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
public class Booking : EntityData
{
public BookingStatus BookingStatus { get; set; }
[ForeignKey("PersonId")]
public virtual Person Person { get; set; }
[ForeignKey("HotelId")]
public virtual Hotel Hotel { get; set; }
public string PersonId { get; set; }
public string HotelId { get; set; }
}
public class Person : EntityData
{
public string Name { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
And the controller:
public class BookingController : TableController<Booking>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileServiceContext context = new MobileServiceContext();
DomainManager = new EntityDomainManager<Booking>(context, Request);
}
// GET tables/Booking/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Booking> GetBooking(string id)
{
return Lookup(id);
}
// GET tables/Booking
public IQueryable<Booking> GetAllBookings()
{
return Query();
}
// PATCH tables/Booking/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Booking> PatchBooking(string id, Delta<Booking> patch)
{
return UpdateAsync(id, patch);
}
}
I have added some default data using CreateDatabaseIfNotExists<MobileServiceContext> and when I startup and test the Web API, the DB gets populated and I am happy that the Keys/Relationships are setup correctly. I am just using the Code First convention naming (as per this tutorial)
I have also created a test client with the following Entities:
public class Person
{
public string Id { get; set; }
public byte[] Version { get; set; }
public string Name { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
public class Booking
{
public string Id { get; set; }
public byte[] Version { get; set; }
public BookingStatus BookingStatus { get; set; }
public string PersonId { get; set; }
public string HotelId { get; set; }
public virtual Person Person { get; set; }
public virtual Hotel Hotel { get; set; }
}
public class Hotel
{
public string Id { get; set; }
public byte[] Version { get; set; }
public string Title { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
And with this test logic:
using (var client = new MobileServiceClient(m_Url, new ODataParameterHandler())
{
client.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
client.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var bookingTable = client.GetTable<Booking>();
var bookings = await placementTable
.Where(p => p.BookingStatus == BookingStatus.Confirmed && p.PersonId == 10)
.WithParameters(new Dictionary<string, string> { { "expand", "Hotel" } })
.ToListAsync();
var aBooking = bookings[0];
aBooking.BookingStatus = BookingStatus.Cancelled;
await bookingTable.UpdateAsync(aBooking);
}
// Class to allow $expand= querystring value to be passed in.
public class ODataParameterHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
UriBuilder builder = new UriBuilder(request.RequestUri);
builder.Query = builder.Query
.Replace("expand", "$expand")
.TrimStart('?');
request.RequestUri = builder.Uri;
return await base.SendAsync(request, cancellationToken);
}
}
The GET/ToListAsync works ok and I get the child Hotel object attached to my Booking. However, the Update fails with:
The operation failed due to a conflict: 'Violation of PRIMARY KEY constraint 'PK_dbo.Hotels'. Cannot insert duplicate key in object 'dbo.Hotels'. The duplicate key value is (0e6e1bae-bd59-46ac-9630-a2b53dd04a90).\r\nThe statement has been terminated.
But why on earth is it attemping to INSERT my child object again? Firstly, I haven't altered it, and secondly, it has an Id, CreatedAt etc.
I cannot find any similar issues regarding Azure Mobile Apps, but I did find this SO Post regarding Entity Framework but the OP talks about having manually created the children, so I am not sure it fully applies as I have fetched the child Entity from the DB through the TableController.
Azure Mobile Apps does not support relationships. You are bumping into one of the many issues that go along with that.
If you are using offline sync, then decompose the tables so that the linkage is less required, then sync each table individually.
If you are not using offline sync, use a custom API to commit the change to the database.
I am trying to perform CURD operation in MVC web application in a webgrid but the problem is I have multiple tables but don't know how to perform EDIT operation by using multiple tables.
Invoice table
public Invoice()
{
this.LineItems = new HashSet<LineItem>();
}
public int Customer_ID { get; set; }
public string Customer_name { get; set; }
public string Customer_Address { get; set; }
public virtual ICollection<LineItem> LineItems { get; set; }
Product Table
public Produc()
{
this.LineItems = new HashSet<LineItem>();
}
public int Product_ID { get; set; }
public string Product_name { get; set; }
public int Unit_Price { get; set; }
public virtual ICollection<LineItem> LineItems { get; set; }
LineItems Table
public partial class LineItem
{
public int Customer_ID { get; set; }
public int LineItems_ID { get; set; }
public int Product_ID { get; set; }
public int Quantity { get; set; }
public int Total { get; set; }
public virtual Invoice Invoice { get; set; }
public virtual Produc Produc { get; set; }
}
ViewModel
public class ViewModel
{
public string Customer_name { get; set; }
public string Customer_Address { get; set; }
public int Quantity { get; set; }
public int Total { get; set; }
public string Product_name { get; set; }
public int Unit_Price { get; set; }
}
here is a class which will perform CURD operation for me
public class Class1
{
SalesOrderEntities entities = new SalesOrderEntities();
public bool SaveStudent(ViewModel viewModel)
{
try
{
var Invoice = new Invoice()
{
Customer_name = viewModel.Customer_name,
Customer_Address = viewModel.Customer_Address
};
var LineItem = new LineItem()
{
Quantity = viewModel.Quantity,
Total = viewModel.Total
};
var Produc = new Produc()
{
Product_name=viewModel.Product_name,
Unit_Price=viewModel.Unit_Price
};
return true;
}
catch
{
return false;
}
}
public bool UpdateStudent()
{
try
{
}
catch (Exception)
{
throw;
}
}
Now, here i have problem i don't know how to perform edit functionality.
Updating using Entity Framework can be fairly straight-forward as it supports change-tracking by default. Change tracking will let EF automatically manage any changes that occur to your entities once they are pulled, so that when you call SaveChanges(), these same changes will be made at the database-level.
Example Adding New Entities
Since you already have your data context, when you are creating your new entities, you'll just need to ensure that you add them to the context properly and save the changes after that is done :
// Add each of your new entities to their appropriate table in the context and then save
// your changes
entities.Invoices.Add(new Invoice(){
Customer_name = viewModel.Customer_name,
Customer_Address = viewModel.Customer_Address
});
entities.LineItems.Add(new LineItem(){
Quantity = viewModel.Quantity,
Total = viewModel.Total
});
entities.Producs.Add(new Produc(){
Product_name = viewModel.Product_name,
Unit_Price = viewModel.Unit_Price
});
// Now save your changes
entities.SaveChanges();
Example Updating Existing Entities
Updating will essentially work the same way, however you will want to have access to the identifier so that you can query the existing entity, make your changes and save them :
public ActionResult UpdateStudent(int studentId)
{
using(var entities = new SalesOrderEntities())
{
// Get your student
var student = entities.Students.FirstOrDefault(s => s.StudentID == studentId);
if(student == null)
{
// Student wasn't found
return HttpNotFound();
}
// Create a view with the existing student data
return View(student);
}
}
[HttpPost]
public bool UpdateStudent(UpdateStudentViewModel viewModel)
{
try
{
using(var entities = new SalesOrderEntities())
{
// Retrieve your existing student (or other entities)
var existingStudent = entities.Students.FirstOrDefault(s => s.StudentID == viewModel.StudentID);
// Now that you have your entity, update the appropriate properties
existingStudent.Property = viewModel.Property;
// Then finally save your changes
entities.SaveChanges();
}
}
catch(Exception ex)
{
// Something went wrong updating the user
}
}
Hello I have following classes/Models mapped to database using Code-First
public class Speaker
{
public int Id { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public virtual ICollection<SpeakerCertification> Certifications { get; set; }
}
public class Certification
{
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<SpeakerCertification> Speakers { get; set; }
}
public class SpeakerCertification
{
public int Id { get; set; }
public int SpeakerId { get; set; }
public int CertificationId { get; set; }
public virtual Speaker Speaker { get; set; }
public virtual Certification Certifications { get; set; }
}
The certification table is already populated with some data. When speaker information is added, the speakers certification must be added as well (which can be chosen through checkboxes). Now I have to add this information to the database. I have written following code and stuck (_ctx is the dbContext). I require the speakerId for speakerCertification table but the speaker is not yet added in the database. The requirement is speakerId must be an identity field. How can I achieve this? The string array selectedCertifications contain the certifications selected when adding the speaker
public bool AddSpeaker(Entities.Speaker speaker, string[] selectedCertifications)
{
if (selectedCertifications != null)
{
SpeakerCertification speakerCertification = new SpeakerCertification();
speaker.Certifications = new List<SpeakerCertification>();
foreach (var certificate in selectedCertifications)
{
var certificationToAdd = _ctx.Certifications.Find(int.Parse(certificate));
speakerCertification.CertificationId = certificationToAdd.Id;
//speakerCertification.SpeakerId
speaker.Certifications.Add(speakerCertification);
}
}
_ctx.Speakers.Add(speaker);
_ctx.SaveChanges();
return true;
}
Sorry for troubling those who went through the question, there was almost no problem at all. I was just too silly to miss it. I forgot the magic of Entity Framework. I implemented the add function in following way
public bool AddSpeaker(Entities.Speaker speaker, string[] selectedCertifications)
{
if (selectedCertifications != null)
{
SpeakerCertification speakerCertification = new SpeakerCertification();
speaker.Certifications = new List<SpeakerCertification>();
foreach (var certificate in selectedCertifications)
{
if (certificate.CompareTo("false")!=0)
{
var certificationToAdd = _ctx.Certifications.Find(int.Parse(certificate));
speakerCertification = new SpeakerCertification();
speakerCertification.CertificationId = certificationToAdd.Id;
speaker.Certifications.Add(speakerCertification);
}
}
}
_ctx.Speakers.Add(speaker);
_ctx.SaveChanges();
return true;
}
I have a annoying problem with my code.
My model :
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Conference> Conference { set; get; }
}
public partial class Conference
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public virtual Option Option { set; get; }
public virtual User User { get; set; }
}
public partial class User
{
public int Id {get; set; }
public string Name { get; set; }
public virtual Conference Conference { get; set; }
}
And now i`m getting Option object from Db by dbSet.Find(id) (RepositoryFactory) and what i want to do is to save newly created User, but with selected Option.
If i do like that:
var option = dbSet.Find(id);
var user = new User()
{
Name = "Name",
Conference = new Conference
{
Option = option
}
};
//...
context.SaveChanges();
I`m getting an exception:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
What I`m doing wrong?
Edit: I Tried to create Mapping, but this doesn`t seems to work too.
modelBuilder.Entity<Conference>()
.HasKey(x => x.UserId)
.HasRequired(x => x.User)
.WithOptional(user => user.Conference);
modelBuilder.Entity<Option>()
.HasMany(option => option.Conferences)
.WithRequired(conference => conference.Option)
.HasForeignKey(conference => conference.UserId);
Are you trying to achieve a 1:1 relationship between User and Conference? If so, you need to add an Id (key) property to User. Please see the comments I added to the code sample below regarding the 1:1 relationship. I would advise further evaluation of your domain layer to ensure this is what you are trying to achieve...
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace Stackoverflow
{
public class EntityContext : DbContext
{
public IDbSet<Conference> Conferences { get; set; }
public IDbSet<Option> Options { get; set; }
public IDbSet<User> Users { get; set; }
}
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Conference> Conference { set; get; }
}
public class Conference
{
// In one-to-one relation one end must be principal and second end must be dependent.
// User is the one which will be inserted first and which can exist without the dependent one.
// Conference end is the one which must be inserted after the principal because it has foreign key to the principal.
[Key, ForeignKey("User")]
public int UserId { get; set; }
public int OptionId { get; set; }
public virtual Option Option { set; get; }
public virtual User User { get; set; }
}
public class User
{
// user requires a key
public int Id { get; set; }
public string Name { get; set; }
public virtual Conference Conference { get; set; }
}
class Program
{
static void Main(string[] args)
{
using (var entityContext = new EntityContext())
{
// added to facilitate your example
var newOption = new Option {Name = "SomeOptionName"};
entityContext.Options.Add(newOption);
entityContext.SaveChanges();
var option = entityContext.Options.Find(newOption.Id);
var user = new User
{
Name = "Name",
Conference = new Conference
{
Option = option
}
};
// need to add the user
entityContext.Users.Add(user);
//...
entityContext.SaveChanges();
}
}
}
}