i am trying to get this to work for hours now. I tried the examples and read documentation but i just cant figure it out.
I want to query distinct values from the DB with a name field.
I think this should work but it does not. The method Distinct is not found
[HttpGet]
public IQueryable<MvlOP> MvlOpsPerson(long mvlId)
{
System.Data.Entity.Infrastructure.DbQuery<MvlOP> query = ContextProvider.Context.MvlOps;
query = query.Include("StatusOP");
return query.Where(op => op.MvlId == mvlId).Distinct(new PropertyComparer<MvlOP>("Id_P"));
}
I get the following error:
ExceptionMessage=LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[MAHN.Model.MvlOP] Distinct[MvlOP](System.Linq.IQueryable`1[MAHN.Model.MvlOP], System.Collections.Generic.IEqualityComparer`1[MAHN.Model.MvlOP])' method, and this method cannot be translated into a store expression.
ExceptionType=System.NotSupportedException
So this is wrong. As far as i understand it breeze does not offer querying distinct values. Querying all and filtering is not an option. Any help on how this can be done much appreciated.
I post this so someone who might need it can use it. Maybe this can be improved in some way. (Inspired by Breeze DocCode (thanks for the Partial Class Api Method in the Northwind Controller :) )
Querying distinct values can be done this way:
To be able to use the Distinct(IEqualityComparer) method the query has to be in Memory as an IEnumerable. An EqualityComparer cannot be translated into an SQL-Statement.
So the where clause applies and the resulting records are then filtered by the Comparer.
return query.AsQueryable(): To be able to use skip/take and inlineCount the query has to be an IQueryable<T>. Therefor the method AsQueryable().
//The API Method ----------
[HttpGet]
public IQueryable<MvlOPPersonPartial> MvlOpsPerson(long mvlId)
{
var query = (from op in ContextProvider.Context.MvlOps
where op.MvlId == mvlId
select new MvlOPPersonPartial()
{
MvlId = op.MvlId,
Kundenname = op.Kundenname,
Kundennummer = op.Kundennummer,
Id_P = op.Id_P
})
.AsEnumerable()
.Distinct(new PropertyComparer<MvlOPPersonPartial>("Id_P"));
return query.AsQueryable();
}
public class MvlOp
{
...
public string Kostenstelle { get; set; }
public string Betreuer_Id { get; set; }
public decimal? Id_P { get; set; }
public string Kundenname { get; set; }
public string Kundennummer { get; set; }
public string Person_Typ1 { get; set; }
...
}
//The partial class needed for distinct values ------------
//MvlOP cannot be constructed in an LINQ to Entities query
public class MvlOPPersonPartial
{
public long MvlId { get; set; }
public string Kundenname { get; set; }
public string Kundennummer { get; set; }
public decimal? Id_P { get; set; }
}
//A generic comparer ---------------
public class PropertyComparer<T> : IEqualityComparer<T>
{
private PropertyInfo _PropertyInfo;
/// <summary>
/// Creates a new instance of PropertyComparer.
/// </summary>
/// <param name="propertyName">The name of the property on type T
/// to perform the comparison on.</param>
public PropertyComparer(string propertyName)
{
//store a reference to the property info object for use during the comparison
_PropertyInfo = typeof(T).GetProperty(propertyName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
if (_PropertyInfo == null)
{
throw new ArgumentException(string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
}
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
//get the current value of the comparison property of x and of y
object xValue = _PropertyInfo.GetValue(x, null);
object yValue = _PropertyInfo.GetValue(y, null);
//if the xValue is null then we consider them equal if and only if yValue is null
if (xValue == null)
return yValue == null;
//use the default comparer for whatever type the comparison property is.
return xValue.Equals(yValue);
}
public int GetHashCode(T obj)
{
//get the value of the comparison property out of obj
object propertyValue = _PropertyInfo.GetValue(obj, null);
if (propertyValue == null)
return 0;
else
return propertyValue.GetHashCode();
}
#endregion
}
I think that the problem is that the Entity Framework (EF) cannot use your PropertyComparer. EF only supports Distinct without a comparer.
Related
I am using a unit of work to retrieve records / record form database, i am trying to implement some kind of a null object design pattern so that i dont have to check every-time if the returned object is null or not. I have tried searching online however i have not land on any good explanation on how to best achieve this in this current situation, I am familiar with the traditional approach for Null Object Design Pattern where you create a copy null class with hard coded properties and methods and return either the class or null-class based on outcome of the search in Db. however I feel that with the unit of work and repository patterns this approach is not valid. here is the class.
public class HR_Setup_Location
{
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string FullAddress{
get { return $"{Street1} {City} {Country}"; }
}
[ForeignKey("Setup")]
public int SetupId { get; set; }
public virtual Setup Setup { get; set; }
public virtual ICollection<HR_Setup_OfficeEvent> HR_Setup_OfficeEvents { get; set; }
}
I tried the following , which is doing the job for now, however i appreciate your feedback on the approach if you have tried something similar in a similar situation. and what is the best way to address null objects in this pattern.
public interface ISetupLocationRepository : IRepository<HR_Setup_Location>
{
HR_Setup_Location GetById(int LocationId);
}
public class SetupLocationRepository : Repository<HR_Setup_Location>, ISetupLocationRepository
{
private readonly DataBaseContext context;
public SetupLocationRepository(DataBaseContext context)
: base(context)
{
this.context = context;
}
public HR_Setup_Location GetById(int LocationId)
{
HR_Setup_Location Obj = context.HR_Setup_Locations.Where(p => p.HR_Setup_LocationId == LocationId).FirstOrDefault();
if (Obj != null)
{
return Obj;
}
else
{
HR_Setup_Location Obj2 = new HR_Setup_Location()
{
HR_Setup_LocationId = -1,
Street1 = string.Empty,
Street2 = string.Empty,
City = string.Empty,
State = string.Empty,
Country = string.Empty,
SetupId = -1,
};
Obj2.HR_Setup_OfficeEvents = null;
return Obj2;
}
}
}
Then with the unit of work I am trying to access the location address by calling:
string LocationName = Vacancy.HR_Setup_LocationId.HasValue ? unitOfWork.SetupLocations.GetById(Vacancy.HR_Setup_LocationId.Value).FullAddress : "";
so basically if no id is based it will return an empty string, and if an id is passed but the record is no longer available in DataBase then the null object return empty for Fulladdress
I have a quality tracking webapp where I need each resolution steps to be signed by a user. To ease the process, I've built my model around following who filled the date at the end of each step.
My simplified Failure model looks like this:
public class failure {
[key]
public string FailureId { get; set; }
...
public int? OpenUserId { get; set; }
public int? DiagUserId { get; set; }
public int? CloseUserId { get; set; }
...
[ForeignKey("OpenUserId")]
public virtual signature OpenUser { get; set; }
[ForeignKey("DiagUserId")]
public virtual signature DiagUser { get; set; }
[ForeignKey("CloseUserId")]
public virtual signature CloseUser { get; set; }
}
and my Signature model:
public class signature {
[key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int SignatureId { get; set; }
public string UserName { get; set; }
[Column(TypeName:="Date")]
[DataType(DataType.Date)]
public date DateSign { get; set; }
}
The goal of this model is to minimize the number of signature in the table. So, if a user signs on multiple failure in one day, the code should only need to create one signature and reuse the same Id.
The problem arises when a user fills multiple steps in one save. Two or more signatures are created (which can be a problem in itself but it's not the focus right now) and an error is raised
Multiple added entities may have the same primary key.
because, before the SaveChanges, all Ids are at 0 and the code can't differentiate them.
Here's the POST:
async Task<ActionResult> Edit( FailureVM failure ) {
if (ModelState.IsValid) {
...
failure.OpenUserId = Await TryUpdateSignature(...);
failure.DiagUserId = Await TryUpdateSignature(...);
failure.CloseUserId = Await TryUpdateSignature(...);
...
await db.SaveChangesAsync;
}
}
and my function:
public static async Task<int?> TryUpdateSignature(MyDbContext db, Signature oldSignUser, Date? newDate, string userName)
{
int? SignatureID = null; //Returns null if no date
//Validate if there is a new date
if ((IsNothing(oldSignUser) && newDate != null) || (oldSignUser != null && oldSignUser.DateSign != newDate))
{
Signature recSignature = Await db.Signature.FirstOrDefaultAsync(s => s.UserID == userName && s.DateSign == newDate);
if (IsNothing(recSignature))
{
recSignature = new Signature;
recSignature.UserID = userName;
recSignature.DateSign = newDate;
db.Signature.Add(recSignature);
}
SignatureID = recSignature.SignatureID;
}
else if (oldSignUser != null && newDate != null)
{ //If no change, keep old signature
SignatureID = oldSignUser.SignatureID;
}
return SignatureID;
}
I tried using the object instead of the Ids but it didn't work. I could insert the Signature beforehand but I would prefer having everything saved at once.
At this point, I assume there's a problem with my model or my approach.
All your objects have Id's of 0 after going through the constructor, because 0 is the default int value. Because the PK has to identify the object in the database, EF won't even let you insert multiple objects with the same id values, let alone trying to save those back to the database, because it does not know how to identify each tuple (this even is of relevance to EF, not only DBMS for consistency reasons, because EF uses the PK values as default concurrency tokens).
The way to solve this is to give all your objects distinct PK values. How you do it is up to you - in my project, we've let all objects that are meant to be saved to the database from one common base class, let's call it BOBase:
public abstract class BOBase
{
public long Id { get; set; }
public BOBase()
{
Id = DbTempId.Next();
}
}
internal static class DbTempId
{
private static long _next = -1;
internal static long Next()
{
return _next--;
}
}
Once you let all your BO's inherit from this base class (and don't hide the inherited property), the Id's of newly created objects will be -1, -2 and so on. We even use this to differentiate between new and to be updated objects - new objects will have a negative Id, while already existing objects will have a positive one.
My Model
public class FlightBooking
{
public int Id { get; set; }
public ICollection<FlightPassenger> Passengers { get; set; }
public DateTime DateJourney { get; set; }
public virtual City FromCity { get; set; }
public virtual City ToCity { get; set; }
}
public class FlightPassenger
{
public int FlightBookingId { get; set; }
public FlightBooking FlightBooking { get; set; }
public int CustomerId { get; set; }
public Customer Passenger { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public DateTime BirthDate { get; set; }
public ICollection<FlightPassenger> FlightPassengers { get; set; }
}
And in the OnModelCreating I have added
modelBuilder.Entity<FlightPassenger>().HasKey(x => new { x.FlightBookingId, x.CustomerId });
This creates the 3 tables in the database. Customer, FlightBooking and FlightPassenger. All this is fine to represent the many to many relationship in EF7. Now I am trying to take this input from the user.
My view
<select asp-for="Passengers" asp-items="Enumerable.Empty<SelectListItem>()" class="form-control customer"></select>
I am getting the data properly using Ajax and able to select the multiple values in the dropdown. But in the controller no value is passed in Passengers and its count is 0. I checked for the value in the dropdown before posting and it shows ids of the selected customers with comma. I know Passengers is not an integer array but adding an integer array to the model gives another error, so I was thinking there has to be another way. I did a small hack to by adding a string to my view model and before posting adding this integer array to the string. This string has all the values (comma sep) in the controller. But I am sure there should be a better way. Any guidance on getting this value from the view and eventually storing in the database would be great.
In my current project I have a lot of many-to-many relationships. As far as I know EF Core does not yet support many-to-many so I assume it has to be done manually. I generalized the solution.
As I'm new to EF/MVC feedback is welcome:
First I created a JoinContainer to hold the necessary data for the many-to-many entity.
public class SimpleJoinContainerViewModel
{
public int[] SelectedIds { get; set; }
public IEnumerable<SelectListItem> SelectListItems { get; set; }
// keeping track of the previously selected items
public string PreviousSelectedHidden { get; set; }
public int[] PreviousSelectedIds
{
get
{
// if somebody plays around with the hidden field containing the ints the standard exception/error page is ok:
return PreviousSelectedHidden?.Split(' ').Where(s => !string.IsNullOrEmpty(s)).Select(int.Parse).ToArray();
}
private set { PreviousSelectedHidden = value == null ? "" : string.Join(" ", value); }
}
/// <summary>
/// Call when form is loaded - not on post back
/// </summary>
/// <param name="selectListItems"></param>
/// <param name="selectedIds">Currently selected referenced ids. Get via m:n/join-table</param>
public void Load(IEnumerable<SelectListItem> selectListItems, IEnumerable<int> selectedIds)
{
SelectListItems = selectListItems;
SelectedIds = selectedIds?.ToArray();
PreviousSelectedIds = SelectedIds;
}
}
In the view model (of FlightBooking):
[Display(Name = "Passengers")]
public SimpleJoinContainerViewModel PassengersJoinContainer { get; set; } = new SimpleJoinContainerViewModel();
In the GET action I use the Load() method to fill the Container with the data:
viewModel.PassengerJoinContainer.Load(
DbContext.Customers
.Select(s => new SelectListItem
{
Text = s.LastName,
Value = s.Id.ToString()
}),
flightBookingEntity?.Passengers?.Select(p => p.CustomerId));
In the view I use the properties of the JoinContainer:
<div class="form-group">
<label asp-for="PassengersJoinContainer" class="col-sm-3 control-label"></label>
<div class="col-sm-9">
<div class="nx-selectize">
#Html.ListBoxFor(m => m.PassengersJoinContainer.SelectedIds, Model.PassengersJoinContainer.SelectListItems)
</div>
#Html.HiddenFor(m => m.PassengersJoinContainer.PreviousSelectedHidden)
<span asp-validation-for="PassengersJoinContainer" class="text-danger"></span>
</div>
</div>
Then I have a generalized Update class/method.
public class SimpleJoinUpdater<T> where T : class, new()
{
private DbContext DbContext { get; set; }
private DbSet<T> JoinDbSet { get; set; }
private Expression<Func<T, int>> ThisJoinIdColumn { get; set; }
private Expression<Func<T, int>> OtherJoinIdColumn { get; set; }
private int ThisEntityId { get; set; }
private SimpleJoinContainerViewModel SimpleJoinContainer { get; set; }
/// <summary>
/// Used to update many-to-many join tables.
/// It uses a hidden field which holds the space separated ids
/// which existed when the form was loaded. They are compared
/// to the current join-entries in the database. If there are
/// differences, the method returns false.
/// Then it deletes or adds join-entries as needed.
/// Warning: this is not completely safe. A race condition
/// may occur when the update method is called concurrently
/// for the same entities. (e.g. 2 persons press the submit button at the same time.)
/// </summary>
/// <typeparam name="T">Type of the many-to-many/join entity</typeparam>
/// <param name="dbContext">DbContext</param>
/// <param name="joinDbSet">EF-context dbset for the join entity</param>
/// <param name="thisJoinIdColumn">Expression to the foreign key (Id/int) which points to the current entity</param>
/// <param name="otherJoinIdColumn">Expression to the foreign key (Id/int) which points to the joined entity</param>
/// <param name="thisEntityId">Id of the current entity</param>
/// <param name="simpleJoinContainer">Holds selected ids after form post and the previous selected ids</param>
/// <returns>True if updated. False if data has been changed in the database since the form was loaded.</returns>
public SimpleJoinUpdater(
DbContext dbContext,
DbSet<T> joinDbSet,
Expression<Func<T, int>> thisJoinIdColumn,
Expression<Func<T, int>> otherJoinIdColumn,
int thisEntityId,
SimpleJoinContainerViewModel simpleJoinContainer
)
{
DbContext = dbContext;
JoinDbSet = joinDbSet;
ThisJoinIdColumn = thisJoinIdColumn;
OtherJoinIdColumn = otherJoinIdColumn;
ThisEntityId = thisEntityId;
SimpleJoinContainer = simpleJoinContainer;
}
public bool Update()
{
var previousSelectedIds = SimpleJoinContainer.PreviousSelectedIds;
// load current ids of m:n joined entities from db:
// create new boolean expression out of member-expression for Where()
// see: http://stackoverflow.com/questions/5094489/how-do-i-dynamically-create-an-expressionfuncmyclass-bool-predicate-from-ex
ParameterExpression parameterExpression = Expression.Parameter(typeof (T), "j");
var propertyName = ((MemberExpression) ThisJoinIdColumn.Body).Member.Name;
Expression propertyExpression = Expression.Property(parameterExpression, propertyName);
var value = Expression.Constant(ThisEntityId);
Expression equalExpression = Expression.Equal(propertyExpression, value);
Expression<Func<T, bool>> thisJoinIdBooleanExpression =
Expression.Lambda<Func<T, bool>>(equalExpression, parameterExpression);
var joinedDbIds = JoinDbSet
.Where(thisJoinIdBooleanExpression)
.Select(OtherJoinIdColumn).ToArray();
// check if ids previously (GET) and currently (POST) loaded from the db are still the same
if (previousSelectedIds == null)
{
if (joinedDbIds.Length > 0) return false;
}
else
{
if (joinedDbIds.Length != previousSelectedIds.Length) return false;
if (joinedDbIds.Except(previousSelectedIds).Any()) return false;
if (previousSelectedIds.Except(joinedDbIds).Any()) return false;
}
// create properties to use as setters:
var thisJoinIdProperty = (PropertyInfo) ((MemberExpression) ThisJoinIdColumn.Body).Member;
var otherJoinIdProperty = (PropertyInfo) ((MemberExpression) OtherJoinIdColumn.Body).Member;
// remove:
if (joinedDbIds.Length > 0)
{
DbContext.RemoveRange(joinedDbIds.Except(SimpleJoinContainer.SelectedIds).Select(id =>
{
var e = new T();
thisJoinIdProperty.SetValue(e, ThisEntityId);
otherJoinIdProperty.SetValue(e, id);
return e;
}));
}
// add:
if (SimpleJoinContainer.SelectedIds?.Length > 0)
{
var toAddIds = SimpleJoinContainer.SelectedIds.Except(joinedDbIds).ToList();
if (toAddIds.Count > 0)
{
DbContext.AddRange(SimpleJoinContainer.SelectedIds.Except(joinedDbIds).Select(id =>
{
var e = new T();
thisJoinIdProperty.SetValue(e, ThisEntityId);
otherJoinIdProperty.SetValue(e, id);
return e;
}));
}
}
return true;
}
}
In the Post action I call this class/method:
var flightPassengersUpdater = new SimpleJoinUpdater<FlightPassenger>(
DbContext,
DbContext.FlightPassengers,
mm => mm.FlightBookingId,
mm => mm.CustomerId,
model.Id, // model = current flightBooking object
viewModel.PassengersJoinContainer);
if (!flightPassengersUpdater .Update())
{
ModelState.AddModelError("PassengersJoinContainer", "Since you opened this form the data has already been altered by someone else. ...");
}
I am working on MVC and Entity Framework. I have written a function for multiple record delete.
The model is:
public partial class Category
{
public Category()
{
//this.Products = new HashSet<Product>();
}
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public byte[] Picture { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Controller is:
public ActionResult DeleteMultipleCategories(int[] id)
{
try
{
foreach (Category item in _db.Categories.Where(x => id.Equals(x.CategoryID)).ToList())
_db.Categories.Remove(item);
_db.SaveChanges();
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
It is giving an error like
"Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.".
The statement "id.Equals(x.CategoryID)).ToList()" is responsible the error.
If I change it to id.Contains(x.CategoryID)).ToList(), it is working fine.
I don't want to use "Contains" as it will not give actual result e.g. if I want to delete the record no. 1 it will also delete record no. 11, 111 etc.
Any help will be thankfully accepted.
Partha
For the int datatype you have to use == as the comparator. So your linq statement should be something like .where(m =>m.id == x.CategoryID).ToList()
I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.