I have an WebAPI Controller that uses complex types from Entity Framework. When I receive the object I check to see if it exists. If it doesn't I'd like to create a new. Before I create a new I'd like to add a couple additional values to the object. If I add a break point and a watch I can see the value and it appears like it has changed. But the value doesn't make it to the database.
[Authorize(Roles ="customerprofileuser")]
[Route("api/CustomerProfile/Save")]
[HttpPost]
public IHttpActionResult SaveCustomerProfile([FromBody] MERP.Customer _input)
{
Models.Message.Response _return = new Models.Message.Response();
_return.Message = "Profile Saved!";
_return.Now = DateTime.Now;
try {
ERPEntities ent = new ERPEntities();
var cust = ent.Customers.AsNoTracking().Where(w => w.ID == _input.ID).FirstOrDefault();
if (cust == null)
{
_input.ID = Guid.NewGuid();
_input.Alias = getCustomerNumberNext(_input.Type);
_input.CreatedOn = DateTime.Now;
ent.Customers.Add(_input);
}
else
{
ent.Customers.Attach(_input);
ent.Entry(_input).State = System.Data.Entity.EntityState.Modified;
}
_return.ResponseObject = _input.ID.ToString();
ent.SaveChanges();
}
catch (Exception ex)
{
_return.Message = ex.Message;
_return.Severity = 3;
}
return Ok(_return);
}
If I map the values to a new object like this, everything works as expected.
var val = new Customer();
val.ID = Guid.NewGuid();
val.Active = _input.Active;
val.Alias = getCustomerNumberNext(_input.Type);
val.CreatedOn = DateTime.Now;
ent.Customers.Add(val);
I'd rather not map every single property to the new object property. Is there a way around this behavior?
Here's a sample of the auto-generated Customer class from my Entity Model.
public partial class Customer
{
public System.Guid ID { get; set; }
public string Name { get; set; }
public Nullable<System.Guid> Type { get; set; }
public string Alias { get; set; }
public string Website { get; set; }
public string Note { get; set; }
public string Email { get; set; }
public Nullable<System.Guid> Salesman { get; set; }
public Nullable<System.Guid> SalesRegion { get; set; }
public Nullable<bool> Active { get; set; }
public string LinkedIn { get; set; }
public string Facebook { get; set; }
public string Twitter { get; set; }
public string GoldmineFK { get; set; }
public string SalesFK { get; set; }
public string InventoryFK { get; set; }
public Nullable<System.Guid> Industry { get; set; }
public Nullable<System.Guid> Lead { get; set; }
public Nullable<System.Guid> Show { get; set; }
public Nullable<System.Guid> Territory { get; set; }
public Nullable<System.DateTime> CreatedOn { get; set; }
}
Here's the getCustomerNumberNext function
private string getCustomerNumberNext(Guid? companyid)
{
ERPEntities ent = new ERPEntities();
var _prefix = (from p in ent.CompanyLookups
where p.Type == "CustomerNumberPrefix"
select p.Value.ToString()).FirstOrDefault();
var _number = (from p in ent.CompanyLookups
where p.Type == "CustomerNumberSequence"
select p.Value.ToString()).FirstOrDefault();
var _newNumber = Convert.ToInt32(_number) + 1;
try
{
var _update = (from p in ent.CompanyLookups
where p.Type == "CustomerNumberSequence"
select p).FirstOrDefault();
_update.Value = _newNumber.ToString();
ent.SaveChanges();
}
catch (Exception ex)
{ return ex.Message; }
return _prefix + _number;
}
EDIT: The C# code works as expected. The issue was with the data round tripping from the client and incompleteness.
I believe there is a typo in your question, which says "does make it to the database" but I believe you meant "does not make it to the database"
With that assumption I tried running similar code locally and was able to save the values as expected. The primary difference is that Alias is an integer in my code and I am assuming it is a complex class in your code. Here is the code that successfully saved the values to the database,
public class HomeController : ApiController
{
[HttpPost]
[Route("api/CustomerProfile/Save")]
public IHttpActionResult SaveCustomerProfile([FromBody] Customer _input)
{
masterEntities masterEntities = new masterEntities();
var cust = masterEntities.Customers.AsNoTracking().Where(w => w.ID == _input.ID).FirstOrDefault();
if (cust == null)
{
_input.ID = Guid.NewGuid();
_input.Alias = 0;
_input.CreatedOn = DateTime.Now;
masterEntities.Customers.Add(_input);
}
else
{
masterEntities.Customers.Attach(_input);
masterEntities.Entry(_input).State = System.Data.Entity.EntityState.Modified;
}
masterEntities.SaveChanges();
return Ok();
}
}
Here is what the generated Customer class like,
public partial class Customer
{
public System.Guid ID { get; set; }
public bool Active { get; set; }
public Nullable<int> Alias { get; set; }
public Nullable<System.DateTime> CreatedOn { get; set; }
}
Can you update your question with the Customer and Alias classes from your code and I can try reproducing that?
On a side note, I would suggest changing
var cust = ent.Customers.AsNoTracking().Where(w => w.ID == _input.ID).FirstOrDefault();
to
var cust = ent.Customers.AsNoTracking().FirstOrDefault(w => w.ID == _input.ID);
Related
I am working on an API and I have an error that says "cannot convert from "HolidayChallenge.Models.Ornament.Ornament2Edit to HolidayChallenge.Data.Ornament2". The code follows:
[Route("api/Ornament2/Update")]
public IHttpActionResult Put(Ornament2Edit ornament2)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var service = CreateOrnament2Service();
if (!service.UpdateOrnament(ornament2)) //This is the line the error is on
{
return InternalServerError();
}
return Ok("Your Ornament was updated!");
I am not sure how to fix this one. I have figured a lot of similar ones out but this one is fighting me. Any help would be much appreciated. Thank you in advance.
Update to question
OrnamentEdit:
namespace HolidayChallenge.Models.Ornament
{
public class Ornament2Edit
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
public int TreeId { get; set; }
}
}
Ornament2:
namespace HolidayChallenge.Data
{
public class Ornament2
{
[Key]
public int Id { get; set; }
public Guid UserId { get; set; }
public string Description { get; set; }
public virtual List<Ornament2> Ornaments { get; set; }
[ForeignKey("ChristmasTree")]
public int TreeId { get; set; }
[Required]
public virtual ChristmasTree ChristmasTree { get; set; }
}
}
Ornament2Service:
public bool UpdateOrnament(Ornament2 model)
{
using (var ctx = new ApplicationDbContext())
{
var entity =
ctx
.Ornaments
.Single(e => e.Id == model.Id && e.UserId == _userId);
entity.Id = model.Id;
entity.Description = model.Description;
entity.TreeId = model.TreeId;
return ctx.SaveChanges() == 1;
}
}
your Service input is Ornament2 object but you pass an Ornament2Edit object and this causes the error. you must pass an object with the correct type.
I don't want to use .include to get the entire child tables. I just need select columns.
public class ProjectTypeDTO {
public string Type { get; set; }
}
public class CourseDTO {
public string CourseCode { get; set; }
public string CourseTitle { get; set; }
}
public class ProjectDTO {
public int Id { get; set; }
public ProjectTypeDTO ProjectType { get; set; }
public CourseDTO Course { get; set; }
public string StartTerm { get; set; }
public DateTime SignOff { get; set; }
public DateTime StartDateTime { get; set; }
}
[HttpGet("getallprojects")]
public IActionResult GetAllProjects()
{
var projects = _context.Projects
.Select(p => new ProjectDTO
{
Id = p.Id,
ProjectType = { Type = p.ProjectType.Type },
Course = { CourseCode = p.Course.CourseCode, CourseTitle = p.Course.CourseTitle },
StartTerm = p.StartTerm,
SignOff = p.SignOff,
StartDateTime = p.StartDateTime,
}).ToList();
return Ok(projects);
}
This is throwing a "NotImplementedException: The method or operation is not implemented." error.
I've tested it as an anonymous function and it works.
var projects = _context.Projects
.Select(p => new
{
p.Id,
p.ProjectType.Type,
p.SignOff,
p.StartDateTime,
p.Course.CourseCode,
p.Course.CourseTitle,
p.StartTerm
}).ToList();
An anonymous type won't work for my app, since I need to be able to make changes to this data before it gets returned.
Based on other examples I've seen here and other sites, this looks correct. Could it be a bug?
I haven't seen that syntax for sub-objects before. eg:
ProjectType = { Type = p.ProjectType.Type }
I believe that should be:
ProjectType = new ProjectTypeDTO{ Type = p.ProjectType.Type }
Hi i have a collection In mongoDB that i want to get only part of the fields from it, i created a class that i'm inserting data with to Mongo
ClassCode:
public class FrameDocument
{
public ObjectId _id { get; set; }
public Nullable<System.DateTime> FrameTimeStamp { get; set; }
public Nullable<int> ActivePick { get; set; }
public Nullable<int> TraderId { get; set; }
public Nullable<int> EventCodeId { get; set; }
public byte[] Frame { get; set; }
public int ServerUserId { get; set; }
public int SesionId { get; set; }
public string TraderName { get; set; }
public string ServerUserName { get; set; }
}
This is the insert code:
FrameDocument frameDoc = new FrameDocument();
frameDoc.Frame = imageBA;
frameDoc.EventCodeId = 1;
frameDoc.SesionId = 1;
frameDoc.FrameTimeStamp = DateTime.Now;
frameDoc.ServerUserId = (int)toMongoDt.Rows[0]["ServerUserId"];
frameDoc.TraderId = (int)toMongoDt.Rows[0]["TraderId"];
frameDoc.ActivePick = (int)toMongoDt.Rows[0]["ActivePick"];
frameDoc.TraderName = (string)toMongoDt.Rows[0]["TraderName"];
frameDoc.ServerUserName = (string)toMongoDt.Rows[0] ["ServerUserName"];
var mongoCon = "mongodb://127.0.0.1";
MongoClient client = new MongoClient(mongoCon);
var db = client.GetDatabase("Video");
var frameCollection = db.GetCollection<FrameDocument>("Frame");
frameCollection.InsertOne(frameDoc);
**For now i get all The fields from the collection with this code, But i want to leave the Frame field out of the class, i tried to build different class without this field but i don't know how to not receive the Frame field **
var collection = db.GetCollection<BsonDocument>("Frame");
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Eq("SesionId", 1)
& builder.Eq("TraderId", 125)
& builder.Eq("ServerUserId", 1)
& builder.Lt("FrameTimeStamp", sing.eDate)
& builder.Gt("FrameTimeStamp", sing.sDate);
var result = collection.Find(filter).ToList();
Can anyone help?
please see this:
_result = _collection.Find(o => o._id == _id)
.Project<FrameDocumentNoFrameField>
(Builders<FrameDocument>.Projection.Exclude(f => f.Frame)).ToList();
where FrameDocumentNoFrameField is a class without Frame field
source here
#Example: Model class
public class Company
{
public string CompanyId { get; set; }
public string CompanyName { get; set; }
public List<CompanySettings>{ get; set; }
}
[BsonIgnoreExtraElements]
public class CompanySettings
{
public CompanySetupType CompanySetupTypeId { get; set; }
public List<string> CompanyEmployee{ get; set; }
}
#Now create a Projection class for which you want to read values
[BsonIgnoreExtraElements]
public class CompanySettingsProjectionModel
{
public List<CompanySettings> CompanySettings { get; set; }
}
#After Creating projection,fetch data from mongo using Builders
public async Task<CompanySettings> GetCompanySettings(string companyId, short CompanySetupTypeId)
{
var filter = BaseFilter(accountId);
var projection = Builders<Company>.Projection
.Include(x => x.Id)
.Include(x => x.CompanySettings);
FindOptions<Company, CompanySettingsProjectionModel> findOptions = new FindOptions<Company, CompanySettingsProjectionModel>()
{
Projection = projection
};
var companySettings = await (await Collection.FindAsync(filter, findOptions)).FirstOrDefaultAsync();
if (companySettings != null && companySettings.CompanySettings != null && companySettings.CompanySettings.Any())
{
return companySettings.CompanySettings .FirstOrDefault(x => (int)x.CompanySetupTypeId == CompanySetupTypeId);
}
return default;
}
This a simple project where users can search for job postings by area of expertise. The relationship between Areas and Postings are Many-to-many. I seem to be able to get to the very last part of retrieving the correctly filtered list, but getting back into the view model keeps giving me different errors:
ViewModel:
public class AreaOfertasViewModel
{
public Oferta UnaOferta { get; set; }
public SelectList AreasTrabajo { get; set; }
public IEnumerable<Oferta> Ofertas { get; set; }
public int idArea { get; set; }
public AreaOfertasViewModel()
{
this.UnaOferta = UnaOferta;
this.Ofertas = new List<Oferta>();
cargarAreas();
}
private void cargarAreas()
{
PostulaOfertaContext db = new PostulaOfertaContext();
this.AreasTrabajo = new SelectList(db.Areas, "areaId", "Area");
}
}
}
Controller:
public ActionResult SearchXArea()
{
return View(new AreaOfertasViewModel());
}
[HttpPost]
public ActionResult SearchXArea(AreaOfertasViewModel aovm)
{
int id = aovm.idArea;
PostulaOfertaContext db = new PostulaOfertaContext();
var area = db.Areas.Where(c => c.areaId == id);
var ofertas = from c in db.Ofertas.Where(r => r.AreaTrabajo == area)
select c;
aovm.Ofertas = (IEnumerable<Oferta>)ofertas.ToList();
return View(aovm);
}
The line giving me issues is
aovm.Ofertas = (IEnumerable)ofertas.ToList();
I've tried List<> for Ofertas, and I've tried leaving it as .ToList() without casting, and casting it as different things, but it gives me errors about not being able to cast it, and "Cannot compare elements of type 'System.Collections.Generic.List`1'. Only primitive types, enumeration types and entity types are supported."
What's the solution here?
Model for AreaTrabajo:
public class AreaTrabajo
{
[Key]
public int areaId { get; set; }
public string Area { get; set; }
public virtual List<Oferta> oferta { get; set; }
}
Model for Oferta:
public class Oferta
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Titulo { get; set; }
[Required]
public DateTime Vencimiento { get; set; }
[Required]
public string Cargo { get; set; }
[Required]
public int HorarioComienzo { get; set; }
[Required]
public int HorarioFin { get; set; }
[Required]
public string DescripcionTareas { get; set; }
public int Remuneracion { get; set; }
[Required]
public int RangoEdadMin { get; set; }
[Required]
public int RangoEdadMax { get; set; }
public string TipoFormacion { get; set; }
public string Idiomas { get; set; }
public string Competencias { get; set; }
public string OtrosEstudios { get; set; }
public string Estado { get; set; }
public virtual List<AreaTrabajo> AreaTrabajo { get; set; }
public virtual TipoContrato TipoContrato { get; set; }
public virtual Empresa Empresa { get; set; }
public virtual List<Postulante> Postulantes { get; set; }
}
Answer
[HttpPost]
public ActionResult SearchXArea(AreaOfertasViewModel aovm)
{
int id = aovm.idArea;
PostulaOfertaContext db = new PostulaOfertaContext();
var area = db.Areas.Where(c => c.areaId == id).FirstOrDefault();
var ofertas = db.Ofertas.Where(s => s.AreaTrabajo.All(e => e.areaId == area.areaId)).ToList();
aovm.Ofertas = ofertas;
return View(aovm);
}
Sorry if my question wasn't clear enough. I needed to filter out from the many-to-many relationship, and this solved it.
You are getting an error because the actual sql is executed when you call tolist(). The error is in your sql because you are comparing AreaTrabago to a list.
[HttpPost]
public ActionResult SearchXArea(AreaOfertasViewModel aovm)
{
int id = aovm.idArea;
PostulaOfertaContext db = new PostulaOfertaContext();
var area = db.Areas.Where(c => c.areaId == id).FirstOrDefault();
var ofertas = db.Ofertas.Where(s => s.AreaTrabajo.All(e => e.areaId == area.areaId)).ToList();
aovm.Ofertas = ofertas;
return View(aovm);
}
Sorry if my question wasn't clear enough. I couldn't get the many-to-many relationship, and this solved the filtering problem perfectly.
I have the follow function that creates a new row or updates an existing row in model MACReg based on whether a given mac address exists.
public Boolean RegisterMAC(string pwd, string mac, string location)
{
School school = getSchoolByCode(pwd);
if (school == null)
{
return false;
}
//initial register or update
using (CloudPrintDbContext db = new CloudPrintDbContext())
{
MACReg r = db.MACRegs.Find(mac);
if (r == null) //create new row
{
MACReg m = new MACReg { MAC = mac, Location = location,
School = school, RegTime = DateTime.Now, UpdateTime = DateTime.Now };
db.MACRegs.Add(m);
}
else //update location
{
r.School = school;
r.Location = location;
r.UpdateTime = DateTime.Now;
}
db.SaveChanges();
}
return true;
}
However, the problem is that it always creates a new row in model School (not MACReg). Any idea why? Thanks!
Models for MACReg and School are below:
public class MACReg
{
[Key]
public string MAC { set; get; }
[Required]
public School School { set; get; }
[Required]
public string Location { set; get; }
[Required]
public DateTime UpdateTime { set; get; }
[Required]
public DateTime RegTime { set; get; }
}
public class School
{
[Key]
public int SchoolID { set; get; }
[Required]
public string SchoolName { set; get; }
[Required]
public DateTime CreateTime { set; get; }
[Required]
public DateTime PwdExprTime { set; get; }
[Required]
public byte[] PwdHash { set; get; }
[Required]
public byte[] Salt { set; get; }
}
UPDATE: getSchoolByCode is below
private School getSchoolByCode(string pwd)
{
using (CloudPrintDbContext db = new CloudPrintDbContext())
{
foreach(School s in db.Schools.Where(s => s.PwdExprTime > DateTime.Now)){
byte[] userH = HashUtils.GenerateHash_Salt(pwd, s.Salt);
if (HashUtils.CompareByteArrays(userH, s.PwdHash))
{
return s;
}
}
}
return null;
}
Your school is from a different CloudPrintDbContext so it's not tracked by the db instance in the using statement. If it isn't attached to any other DbContext then you could attach it to that one before you set the School and then it should work.
db.Schools.Attach(school);
As an aside I'd recommend you use the DbSet.Create() method instead of new so that you can use the dynamic proxies, as per EF documentation.