How should my Get method look like - MVC 4? - c#

Here is my API controller's Get method
[HttpGet]
public MyTable GetMyTable(byte id, string langId)
{
MyTable mytable;
if (id == 0)
{
mytable = db.MyTables.Find(langId);
}
else
{
mytable = db.MyTables.Find(id, langId);
}
if (mytable == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return mytable;
}
It has two keys, so a composite key. The DbSet.Find() method requires that both MyTable.ID and MyTable.LanguageId be specified.
My model for this table looks like:
[Table("MyTable", Schema = "MyScheme")]
public class MyTable
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Required]
[Column(Order = 0)]
public byte ID { get; set; }
[Required]
public string MyCode { get; set; }
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Required]
[Column(Order=1)]
public string LanguageId { get; set; }
[Required]
public byte LevelId { get; set; }
public string ConceptName { get; set; }
}
I want to be able to list all entries by given language id, but without specific id given.
I also want to be able to get single entry with id and langId.
How can I do that?

[HttpGet]
public JsonResult GetMyTable(string langId)
{
var mytable = db.MyTables.Find(langId);
return Json(mytable);
}
[HttpGet]
public JsonResult GetMyTable(byte id, string langId)
{
MyTable mytable = db.MyTables.Find(id, langId);
if (mytable == null)
{
return Json(new {Message = "NullreferenceExeption"})
}
return Json(mytable);
}

You have 2 queries which return different result sets. One returns a collection the other one returns a single result. I'm not going to get into describing how a proper RESTful response should look in api controller. That's a different topic. But if we look at what you have so far it should be like this:
[HttpGet]
public MyTable GetMyTable(byte id, string langId)
{
List<MyTable> results = new List<MyTable>();
if (id == 0)
{
results = db.MyTables.Where(x => x.LanguageId == langid).ToList();
}
else
{
var find = db.MyTables.Find(id, langId);
if (find != null) results.Add(find);
}
return results;
}
This way the results always come back as a list and if find is used for unique entry find then you get back a list with a single element.

My working code:
// GET api/MyTable?langid=mk-MK
[HttpGet]
public IQueryable<MyTable> GetMyTable(string langid)
{
IQueryable<MyTable> mytable = db.MyTables.Where(x => x.LanguageId == langid);
if (mytable == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return mytable;
}
// GET api/MyTable?id=5&langid=mk-MK
[HttpGet]
public MyTable GetMyTable(byte id, string langid)
{
MyTable mytable = db.MyTables.Find(id, langid);
if (mytable == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return mytable;
}

Related

How to automap the property with same class?

I want to map all properties from product to result.
I use the route to pass the model, then follow the tutorial to map all properties.
But I want to make my code more flexible.
So I try both to direct pass product to result and use AutoMapper.
But either doesn’t work.
I have print changeTracker. When I use like result.Name = product.Name; ETC, it can track the change. But The method I try to use doesn’t work;
Original
public IActionResult Edit(TempProducts product)
{
if (this.ModelState.IsValid)
{
var result = (from s in _db.Product where s.ID == product.ID select s).FirstOrDefault();
result.Name = product.Name;
result.Description = product.Description;
result.PublishDate = product.PublishDate;
result.CategoryId = product.CategoryId;
result.Price = product.Price;
result.DefaultImageId = product.DefaultImageId;
result.Quantity = product.Quantity;
result.Status = product.Status;
_db.ChangeTracker.DetectChanges();
Console.WriteLine(_db.ChangeTracker.DebugView.LongView);
_db.SaveChanges();
return RedirectToAction(nameof(Pass2.index));
}
else
{
return View(product);
}
}
Direct pass
public IActionResult Edit(TempProducts product)
{
if (this.ModelState.IsValid)
{
var result = (from s in _db.Product where s.ID == product.ID select s).FirstOrDefault();
result = product;
_db.ChangeTracker.DetectChanges();
Console.WriteLine(_db.ChangeTracker.DebugView.LongView);
_db.SaveChanges();
return RedirectToAction(nameof(Pass2.index));
}
else
{
return View(product);
}
}
Use AutoMapper
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(TempProducts product)
{
if (this.ModelState.IsValid)
{
var config = new MapperConfiguration(c =>
{
c.CreateMap<TempProducts, TempProducts>();
});
IMapper mapper = config.CreateMapper();
#nullable disable
var result = (from s in _db.Product where s.ID == product.ID select s).FirstOrDefault();
result = mapper.Map<TempProducts,TempProducts>(product);
_db.ChangeTracker.DetectChanges();
Console.WriteLine(_db.ChangeTracker.DebugView.LongView);
_db.SaveChanges();
return RedirectToAction(nameof(Pass2.index));
}
else
{
return View(product);
}
}
Edit: TempProducts
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace forLearn.Models.RouteTest
{
public partial class TempProducts
{
public uint ID { get; set; }
public string Name { get; set; }=null!;
public string Description { get; set; }=null!;
[Range(0, 999)]
public int CategoryId { get; set; }
[Range(0, 999.99)]
public int Price { get; set; }
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; }
public bool Status { get; set; }
public int DefaultImageId { get; set; }
[Range(0, 999)]
public int Quantity { get; set; }
}
}
I have found the answer.
Just add this below the result.
_db.Entry(result).CurrentValues.SetValues(product);

XmlIgnore not ignoring

I'm trying to return an object to an ajax request with an api 2 controller in an MVC 5 app. my controller looks like this:
public class RFQsController : ApiController
{
private ApplicationDBContext db = new ApplicationDBContext();
[HttpGet]
public Models.RFQChecklist.RFQ GetRFQByQuote(string number)
{
var quote = db.QuoteSet.FirstOrDefault(q => q.Number == number);
if(quote != null)
{
return quote.RFQ?.FirstOrDefault();
}
else
{
return null;
}
}
}
RFQ looks like this:
public class RFQ
{
public int ID { get; set; }
public int QuoteID { get; set; }
//other values
[XmlIgnore, JsonIgnore]
public virtual Quote.Quote Quote { get; set; }
public virtual ICollection<RFQ_Signoff> Signoffs { get; set; }
}
It's important to ignore Quote because Quote has a reference to its contained RFQs, so that would be a circular reference. So when serializing an RFQ for consumption, I'm trying to ignore Quote, because Quote has to reference RFQ. The problem is that XmlIgnore isn't... ignoring. I've also tried ScriptIgnore, and the combination of DataContract/DataMember. No matter what I do, the MVC 5 serializer seems to try (and fail) to serialize the Quote object again.
I'm quite certain it is because of this value, because if I remove JsonIgnore then it doesn't work when I try to retrieve as Json, but if I include it it works.
If you want to limit your return data you need to use linq select instead of [XmlIgnore, JsonIgnore] attribute.
you can try to use an anonymous object to contain your expect return fields.
return quote.RFQ?.Select(x => new {
ID = x.ID,
QuoteID = x.QuoteID,
Signoffs = x.Signoffs }).FirstOrDefault();
instead of
return quote.RFQ?.FirstOrDefault();
Try to return IHttpActionResult
[HttpGet]
public IHttpActionResult GetRFQByQuote(string number)
{
var quote = db.QuoteSet.FirstOrDefault(q => q.Number == number);
if(quote != null)
{
var result = quote.RFQ?.Select(x => new {
ID = x.ID,
QuoteID = x.QuoteID,
Signoffs = x.Signoffs }).FirstOrDefault()
return Ok(result);
}
else
{
return null;
}
}

How can i create a filter by with Linq?

My MVC view currently displays the following
I would like to perform filtering with a linq based on user selection so i can display the filtered results only.
My controller is like
var Products = db.Products.AsQueryable();
public class FilterPageViewModel
{
public int?[] man { get; set; }
public int?[] size { get; set; }
public int?[] color { get; set; }
public decimal? minPrice { get; set; }
public decimal? maxPrice { get; set; }
public Sorting Sorting {get ; set; }
public Order Order {get; set; }
}
public ActionResult Index(int? CategoryID = 0,
int? page = 0,
FilterPageViewModel model = null)
{
//Manufacturer #############################################
if (model.man != null)
{
foreach (int? i in model.man)
{
if (i.HasValue)
{
//Do something here
}
}
}
//Size ######################################################
if (model.size != null)
{
foreach (int? i in model.size)
{
if (i.HasValue)
{
//Do something here
}
}
}
//Color ######################################################
if (model.color != null)
{
foreach (int? i in model.color)
{
if (i.HasValue)
{
//Do something here
}
}
}
}
I guess i should perform something like...
Where Brand is 'Nike' OR 'Salomon'
AND Size is 'L' OR 'XL' OR 'XXL'
AND Color is 'Red' OR 'Green' OR 'Blue'
Of course the text within '' above are id's and replaced for clarification purposes.
I am sorry if i have not made my self clear but my tongue language is not English.
You can do something along these lines since you have an IQueryable provider for your Linq to SQL.
var filteredProducts = Products;
if(model.man != null)
filteredProducts = filteredProducts.Where(x => (from man in model.man where man.HasValue() select man.Value).Contains(x.manId));
if(model.size != null)
filteredProducts = filteredProducts.Where(x => (from size in model.size where size.HasValue() select size.Value).Contains(x.sizeId));
//etc
var enumerate = filteredProducts.ToList();
Because you're working with an IQueryable, the filters won't be evaluated until you actually enumerate the object with a call like ToList() or plug it into a foreach.

Repository/UnitOfWork Add throws Cannot insert the value NULL into column error

I am working on an application that GETs data from a REST API. I am parsing the json against a concrete class and then stepping through that piece to either Update or Insert (depending of if the row exists) into a SQL database. Part of this is creating a Unique Identifier column with an SkillId and an AccountId, aptly named AccountSkillId. I believe I have traced the issue to my Add portion of my Repository because the Commit works, but the Add does not.
The Add acts like the entity that I am passing is empty, but when I look at it in the locals it shows that it is not empty:
Here is the Code in my Business Layer that attempts
public void GetSkills()
{
var dataContext = new LivePersonContext(ConfigurationWrapper.DataConnectionString);
var apiRequest = new OAuthRequest();
var builder = new StringBuilder("");
using (dataContext)
{
var uow = new UnitOfWork(dataContext);
var accountCredentialIds = uow.Accounts.GetAll().Select(c => c.AccountID).ToList();
foreach (var a in accountCredentialIds)
{
var account = a;
try
{
builder.Clear();
var consumerKey = uow.Accounts.GetById(account).ConsumerKey;
var consumerSecret = uow.Accounts.GetById(account).ConsumerSecret;
var token = uow.Accounts.GetById(account).Token;
var tokenSecret = uow.Accounts.GetById(account).TokenSecret;
builder.AppendFormat(ConfigurationWrapper.SkillsUri, account);
var uri = builder.ToString();
var response = apiRequest.OauthRequestResponse(uri, consumerKey, consumerSecret, token,
tokenSecret);
if (response != null)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
var json = reader.ReadToEnd();
var jsonObj = JsonConvert.DeserializeObject<List<Entity.Skills>>(json);
var currentSkillData = uow.SkillMeta.GetAll().Select(c => c.AccountSkillId).ToList();
foreach (var item in jsonObj)
{
if (item.id != null)
{
var itemId = int.Parse(item.id);
var string2 = item.id + account;
var accountSkillId = int.Parse(string2);
if (currentSkillData.Contains(accountSkillId))
{
var skillUpdate = uow.SkillMeta.GetById(accountSkillId);
// skillUpdate.AccountSkillID = int.Parse(accountSkillId);
//skillUpdate.AccountID = account;
//skillUpdate.SkillID = int.Parse(item.id);
skillUpdate.Name = item.name;
skillUpdate.IsDefault = item.isDefault;
skillUpdate.Description = item.description;
uow.Commit();
}
else
{
var addSkill = new lp_Skill_Meta
{
AccountSkillId = accountSkillId,
AccountId = account,
SkillId = itemId,
Name = item.name,
IsDefault = item.isDefault,
Description = item.description,
//TODO: Verify that they are only ever passing single values
Rel = item.links[0].rel,
Href = item.links[0].href
};
uow.SkillMeta.Add(addSkill);
uow.Commit();
}
}
}
}
}
}
catch (Exception exception)
{
var lEngine = new LoggerEngine{Logger = _logger};
lEngine.Log("ERROR: " + exception);
}
}
}
}
Here is look at my Repository Abstract Class
public abstract class Repository<T> : IRepository<T>
where T : class
{
protected DbSet<T> _objectSet;
public Repository(BaseContext context)
{
_objectSet = context.Set<T>();
}
#region IRepository<T> Members
public abstract T GetById(object id);
public IEnumerable<T> GetAll()
{
return _objectSet;
}
public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
{
return _objectSet.Where(filter);
}
public void Add(T entity)
{
_objectSet.Add(entity);
}
public void Remove(T entity)
{
_objectSet.Remove(entity);
}
#endregion
}
BaseContext is a DbContext Abstraction in my DAL
Here is my Entity that I am trying to Add into my DB
public class lp_Skill_Meta
{
[Key]
public long AccountSkillId { get; set; }
public int AccountId { get; set; }
public int SkillId { get; set; }
public string Name { get; set; }
public bool IsDefault { get; set; }
public string Description { get; set; }
public string Rel { get; set; }
public string Href { get; set; }
}
Here is the schema of the DB:
CREATE TABLE [dbo].[lp_Skill_Meta](
[AccountSkillId] [bigint] NOT NULL,
[AccountId] [int] NOT NULL,
[SkillId] [int] NOT NULL,
[Name] [varchar](250) NULL,
[IsDefault] [bit] NULL,
[Description] [varchar](max) NULL,
[Rel] [varchar](50) NULL,
[Href] [varchar](250) NULL
) ON [PRIMARY]
As you can see in the picture the entity is not empty when I pass it into the add and try to commit the change. But I still get:
Cannot insert the value NULL into column AccountSkillId, table
`DatabaseName.dbo.lp_Skill_Meta'; column does not allow nulls. INSERT
fails.\r\nThe statement has been terminated.
I have been staring at this for a few hours now and have searched and searched and did not see this situation (not saying it isn't there), but any help would be amazing.
UPDATE: Based off of a SQL Trace hat tip to Jeff below in the comments here is the query that EF is trying to run
exec sp_executesql N'INSERT [dbo].[lp_Skill]([AccountId], [SkillId],
[Name], [IsDefault], [Description], [Rel], [Href]) VALUES (#0, #1, #2,
#3, #4, #5, #6) SELECT [AccountSkillId] FROM [dbo].[lp_Skill] WHERE
##ROWCOUNT > 0 AND [AccountSkillId] = scope_identity()' ,N'#0 int,#1
int,#2 nvarchar(max) ,#3 bit,#4 nvarchar(max) ,#5 nvarchar(max) ,#6
nvarchar(max) '
,#0=10740014,#1=6,#2=N'Abusers',#3=0,#4=N'Abusers',#5=N'self',#6=N'https://sales.liveperson.net/api/account/10740014/skills/6'
AccountSkillId is not an identity column in the DB so this why its throwing the NULL. Now on to the search on how to get it to stop marking that column as an Identity...
Per this question: Cannot insert the value NULL into column in ASP.NET MVC Entity Framework
The answer came down to adding [DatabaseGenerated(DatabaseGeneratedOption.None)] to my Entity runs like a charm. Huge shout out to #JeffTreuting for pointing me in the right direction with the SQL Trace. I honestly hadn't even thought about that. Thanks again everyone!
public class lp_Skill
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long AccountSkillId { get; set; }
public int AccountId { get; set; }
public int SkillId { get; set; }
public string Name { get; set; }
public bool IsDefault { get; set; }
public string Description { get; set; }
public string Rel { get; set; }
public string Href { get; set; }
}

Entity Framework 6 has multiplicity 1 or 0..1

I am receiving the following error when trying to insert an object into a child collection after the model binder has created the model, children and grandchildren and then using context.SaveChanges();
Multiplicity constraint violated. The role 'OrderDetail_OrderDetailPricelistProductOptions_Source' of the relationship 'PPLib.Models.OrderDetail_OrderDetailPricelistProductOptions' has multiplicity 1 or 0..1.
My models are as follows (removed properties for brevity);
public class Order
{
public int OrderId { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
public class OrderDetail
{
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; } //FK NAV
public int? PricelistProductId { get; set; } // if a subscriber order ...has the ProductId from a PriceList.
private decimal _Price = 0;
public decimal Price { get { return _Price; } set { _Price = value; } }
private int _Quantity = 1;
public int Quantity { get { return _Quantity; } set { _Quantity = value; } }
public virtual List<OrderDetailPricelistProductOption> OrderDetailPricelistProductOptions { get; set; }
}
public class OrderDetailPricelistProductOption
{
public int OrderDetailPricelistProductOptionId { get; set; }
public int OrderDetailId { get; set; }
public virtual List<OrderDetailPricelistProductOptionsDetail> OrderDetailPricelistProductOptionsDetails { get; set; }
}
public class OrderDetailPricelistProductOptionsDetail
{
public int OrderDetailPricelistProductOptionsDetailId { get; set; }
public int OrderDetailPricelistProductOptionId { get; set; }
public string Name { get; set; }
}
To be clearer:
If I submit a complete new Order, with a list of OrderDetails, its list of OrderDetailPricelistProductOptions and its list of OrderDetailPricelistProductOptionsDetails, the model binder does its job and I receive no error doing:
db.Orders.Add(order);
db.SaveChanges();
If I submit an Edit with and Existing Order and a NEW a list of OrderDetails, its list of OrderDetailPricelistProductOptions and its list of OrderDetailPricelistProductOptionsDetails, I get the Order from the DB context and then merge the OrderDetails from the view model, using:
order.OrderDetails.AddRange(pricelistProductVM.Order.OrderDetails);
and I receive no error doing:
db.Entry(order).State = EntityState.Modified;
db.SaveChanges();
I have a particular situation, where I have to instantiate a new OrderDetail called autoFillOd, and inject its values from one of the existing OrderDetails assembled by the Model Binder. I change its Quantity value and then add it to the collection of OrderDetails in the ViewModel, like so:
pricelistProductVM.Order.OrderDetails.Add(autoFillOd);
When I do db.SaveChanges(), I receive the error.
You'll notice that the error is on the child of the OrderDetails: OrderDetail_OrderDetailPricelistProductOptions_Source
Why can I not add an OrderDetail dynamically into the collection of OrderDetails? All the OrderDetails are new (to be inserted) so the values are the same between the copies, except for the Quantity property which should not be an issue.
The controller action is as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Add(pricelistProductVM pricelistProductVM)
{
OrderLogic ol = new OrderLogic();
//Auth is running on execute
int userId = WebSecurity.CurrentUserId;
int websiteId = (int)Session["websiteId"];
int? id = null; // mediaId
int productId = pricelistProductVM.Product.ProductId;
int? eventId = pricelistProductVM.eventId;
string err = "";
if (productId > 0)
{
//Set Pricelist
Pricelist pricelist = ol.setPricelist(websiteId, id, eventId);
if (pricelist.PricelistId != 0)
{
//get the pricelistproduct from the pricelist
PricelistProduct pp = await (from ppx in db.PricelistProducts
where ppx.ProductId == productId
&& ppx.PricelistId == pricelist.PricelistId
&& ppx.isAvailable == true
&& ppx.DiscontinuedDate == null
&& ppx.Product.isAvailable == true
&& ppx.Product.DiscontinuedDate == null
select ppx).SingleOrDefaultAsync();
if (pp != null)
{
Order order = new Order();
//set some default values for the Order entity
if (pricelistProductVM.Order.OrderId == 0)
{
pricelistProductVM.Order.WebsiteId = websiteId;
pricelistProductVM.Order.UserId = userId;
pricelistProductVM.Order.EventId = eventId;
pricelistProductVM.Order.StartedDate = DateTime.UtcNow;
order = pricelistProductVM.Order;
}
else
{
order = await db.Orders.FindAsync(pricelistProductVM.Order.OrderId);
}
//set some default values for the OrderDetails entity
pricelistProductVM.Order.OrderDetails.First().InjectFrom(pp);
pricelistProductVM.Order.OrderDetails.First().IsPackage = false;
//determine if this product should be automatically added to any packages in the order
OrderDetail autoFillOd = ol.packageCheck(ref pp, ref pricelistProductVM, ref order, websiteId, db);
if (autoFillOd != null)
{
if (autoFillOd.Quantity > 0)
{
//This is where the OrderDetail that causes a problem is added
pricelistProductVM.Order.OrderDetails.Add(autoFillOd);
}
}
if (pricelistProductVM.Order.OrderId == 0)
{
db.Orders.Add(order);
}
else
{
order.OrderDetails.AddRange(pricelistProductVM.Order.OrderDetails);
db.Entry(order).State = EntityState.Modified;
}
db.SaveChanges();
}
else
{
//return error
err = "The product was not found in the available pricelist. Please reload your browser and make sure you are signed-in.";
}
}
}
else
{
//return error
err = "A productId was not passed so no product could not be found. Please reload your browser and make sure you are signed-in.";
}
if (err == "")
{
ViewBag.data = JsonConvert.SerializeObject(new { Success = 1, Msg = "The product was successfully added to your cart." });
}
else
{
ViewBag.data = JsonConvert.SerializeObject(new { Success = 0, Msg = err });
}
return View();
}
I appreciate the help!
I think OrderDetailPricelistProductOption.OrderDetailId can't be single -> it should be a list because it can appear in many OrderDetails...

Categories

Resources