I have a viewmodel to update data (from API, not view) with params like below:
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
i just want to update "name", so my param like below:
{
"name": "my name"
}
its name changed become "my name" but its email and phone become null. how to avoid params changed to be null if they don't exist in input form?
Thanks...
You'll have to adjust whatever does the update so that it understands "null means do not set a value, rather than set the value to null"
For example if you're running a db update query you could:
UPDATE person
SET
name = COALESCE(#name, name),
email = COALESCE(#email, email),
phone = COALESCE(#phone, phone)
WHERE
id = #id
Now if any value is supplied as null the update will set the column to the same value it is currently (ie no-op)
If you're adjusting a c# object you can take a similar approach:
var p = db.FindPerson(viewmodel.PersonId);
p.Name = viewmodel.Name ?? p.Name;
...
As I see, you have a problem or misunderstanding in your software design.
If this API endpoint is meant to just update the name field, then you should not have the other fields in your ViewModel. However, if you update the other fields in some cases, then you should pass their values as well.
So maybe you need to call a Get endpoint first to get the all data you need in your client "web page for example" and then allow this client to resend the full JSON, not just the name.
Or you may just need an endpoint that just takes the name.
Another solution, in case you're using EF, is to ignore the null fields when updating the EF entity.
e.g.
...
var entity = dbContext.Employees.FirstOrDefault(e => e.Id == 3);
entity.Name = request.Name;
await dbContext.SaveChangesAsync();
...
Related
Using EF Core
We are trying to obtain all information of an assessment, which includes its groups and all assigned users. See the
Database Diagram
What is working in following order;
HttpPost (api/Assessment/aID/groups) of an empty group to an assessment
HttpPost (api/Group/gID/users) of users to an existing group
What we are trying to accomplish (code referenced is a different example, yet same principle);
HttpPost (api/Assessment/aID/groups) where a group already contains a list of users. When trying to accomplish this, a possible object cycle was detected which is not supported.
This piece of code is currently throwing a NullReference on Address
-------------------------------------------------------------------
Group groupToCreate = new Group { Name = dto.Name, Description = dto.Description };
foreach (var u in dto.Users)
{
groupToCreate.AddUser(new User
{
Name = u.Name,
Email = u.Email,
Address = new Address
{
Country = u.Address.Country,
City = u.Address.City,
PostalCode = u.Address.PostalCode,
Street = u.Address.Street,
HouseNr = u.Address.HouseNr,
BusNr = u.Address.BusNr
}
});
}
_groupRepository.Add(groupToCreate);
_groupRepository.SaveChanges();
return groupToCreate;
HttpGet (api/Assessment) which displays its assigned groups and linked users.
This seems to be working
------------------------
groupList = _groups.Select(g => new GroupDTO
{
Name = g.Name,
Description = g.Description,
Users = g.GroupUsers.Select(u => new UserDTO
{
Name = u.User.Name,
Email = u.User.Email,
Address = new AddressDTO
{
Country = u.User.Address.Country,
City = u.User.Address.City,
PostalCode = u.User.Address.PostalCode,
Street = u.User.Address.Street,
HouseNr = u.User.Address.HouseNr,
BusNr = u.User.Address.BusNr
}
}).ToList()
}).ToList();
References:
User
Group
Assessment
AssessmentRepo
Hard to tell with the details you're providing, but I'm guessing this is due to Having two-way navigation properties? Are you using EF here?
For example, if your User has a Navigation property allowing access to the user's Group, but a Group has a collection of User objects, then each of those users would themselves have the Group expressed within them... then when trying to express this it could easily get stuck in a cycle, e.g. a user would look like:
{
"Name":"user name",
"Group":{
"Name":"group1",
"Users":[
{
"Name":"user name",
"Group":{
"Name":"group1",
"Users":{
....
}
}
}
]
}
}
.. because a User has a Group, and the Group has a list of User objects, and each one of those has a Group... etc.
This is the sort of issue that comes from mixing your Data layer and DTO objects. Change your system so the objects returned by your REST methods are new objects designed for the requirements of the API/front-end. These objects may look very similar to your DB models (at least initially) but they should not be the same objects.
Create entirely new objects which don't have any logic or navigation properties, and exist only to pass information back to API consumers. For example, a simple class to give a list of user groups and the users in those groups may be defined as:
public class UserDto
{
public string UserName { get; set; }
public IEnumerable<string> Groups { get; set; }
}
public class UserListDto
{
public IEnumerable<UserDto> Users { get; set; }
}
And then your controller action could do something like:
var users = userService.GetAllUsers();
var result = new UserListDto {
Users = users.Select(u => new UserDto{
UserName = u.Name,
Groups = u.Groups.Select(g => g.Name)
}
};
return Ok(result);
..So the thing being serialised for the response doesn't have any complicated relationships to negotiate, and more importantly a change to how you are internally storing and working with the data won't affect the external contract of your API - API consumers can continue to see exactly the same information but how you store and compile this can change drastically.
It is tempting to think "The Data I need to return is basically the same as how I store it internally, so just re-use these classes" but that's not a great idea & will only ever give problems in the long run.
To avoid having to (re-)write a lot of code to 'convert' one object into another, I'd recommend looking into something like AutoMapper as this can make that fairly easily re-usable & allow you to put all this 'Translation' stuff into one place.
I have tried to create a opportunity with suite talk API. while updating the entity field value it returns error because it needs internal id value of the field but it is not feasible to address the internal id.
ReflectionExtensions.SetPropertyValue(NS_OPPURTUNITY, map.Dst_Fld_Name, new RecordRef()
{
internalId = "2551",
type = RecordType.customer,
typeSpecified = true
});
i want to get rid of that static id to reference the entity.
As far as I know, you need the internal ID to reference any object through web services. You can however find the internal ID by first searching for the item you need to reference.
You can use a CustomerSearch to find the internal ID of your customer:
CustomerSearch custSearch = new CustomerSearch();
SearchStringField name = new SearchStringField();
name.SearchValue = "firstName";
name.operatorSpecified = true;
name.#operator = SearchStringFieldOperator.#is;
CustomerSearchBasic custBasic = new CustomerSearchBasic();
custBasic.firstName= customerEntityID;
custSearch.basic = custBasic;
// Search for the customer entity
SearchResult res = _service.search(custSearch);
//Get the internal ID of the customer
string internalID = ((Customer) (res.recordList[0])).internalId;
You can search for the customer using other fields besides 'firstName' as well. Check which other fields are available on the CustomerSearchBasic object here: CustomerSearchBasic
You can update the entity field on basis of external ID also but for this you need to remember certain things
1. You need to set externalID during creation of any record.
2. External ID is unique around the system.
3. Some records don't support external ID such as Custom List.
InventoryItem inventory = new InventoryItem();
inventory.externalId = "abc";
inventory.displayname = "Hello";
setPreferences();
WriteResponse writeRes = _service.update(inventory );
I'm doing a wpf App and i've got a bit of an issue i would like to ask you about.
I'm querying a database on the window level and i pass the result of the query to a method in my object like this :
Window level code :
payrollEmailManager.SetListOfSalariesToEmailTo(
from Record in SqlInfo.SqlTable.T_SALs
where Record.EtatPaie == 3
select new {
Matricule = Record.MatriculeSalarie,
Nom = Record.Nom,
Prenom = Record.Prenom,
Email = Record.EMail });
This is my Method Definition :
public void SetListOfSalariesToEmailTo(object _ListOfSalaryToRecieveMail)
{
ListOfSalary = _ListOfSalaryToRecieveMail;
}
Where ListOfSalary is also of type object.
Now here is the issue for me, I have another method where I want to go trough each record of listofsalary and get the information I selected in query like Matricule or Email, something like this :
public void SendEmail()
{
foreach(var Salary in (dynamic)ListOfSalary)
{
Mail.To.Add(Salary.????
}
}
I can't reference the Nom column or the Email column any advice ??
If you consider your following query:
var query = from Record in SqlInfo.SqlTable.T_SALs
where Record.EtatPaie == 3
select new {
Matricule = Record.MatriculeSalarie,
Nom = Record.Nom,
Prenom = Record.Prenom,
Email = Record.EMail
};
After running this line the query is not yet executed to database. Only when you materialize it (using functions like ToList()/ToArray()/FirstOrDefault etc.) it is actually being executed in the database and information is returned.
Therefore if you just do SomeFunction(query); it does not execute the query and you can store it for later execution.
However you do need to change your code a bit:
The function should not get object but IQueryable<T>
public void SetListOfSalariesToEmailTo(IQueryable<T> query)
As you want to store the query you need to later on know the type of each item. To do so do not use an anonymous object (new { }) in the select. Use instead a custom object or use c# 7.0 named tuples and then the function will look like:
var query = from Record in SqlInfo.SqlTable.T_SALs
where Record.EtatPaie == 3
select new SomeType {
Matricule = Record.MatriculeSalarie,
Nom = Record.Nom,
Prenom = Record.Prenom,
Email = Record.EMail
};
public void SetListOfSalariesToEmailTo(IQueryable<SomeType> query)
{
ListOfSalary = query;
}
You can still use object and dynamic as you did, and just access the properties, but you will not have the intellisense showing you the properties and options, as it does not know the concrete type.
So, I am trying to programmatically add in a ModelFilter to my ObjectListView that will look at two (or more) columns and filters on each separately. Currently, I think that ObjectListView only supports one filter, but I may be missing something in the code/documentation.
As an example, one of my intended filters is to look at column "Active" and that has values of "A" or "T". Another column is a Supervisor Name. So, I want to find all entries where Supervisor name = "Smith" and Active = "A".
I can get the filter to work for either of these options separately using TextMatchFilter, but cannot figure out how to get both to work at the same time.
The minor problem I see is that if the Supervisor Name contains an "A", then using the standard Filter will return the whole row. I have been able to get around that by programmatically setting the Searchable property for columns to false if I don't want to look at them, and then turn them back on once the list is filtered. However, I have a feeling that if I turn Searchable on for the Supervisor column, I will get the unwanted results.
Does anyone know of a way to get the filter to work on multiple columns, using only the indicated columns for each filter?
(I have no sample code to show that helps in solving this. However, if you really want to see what I have for my filtering code, I will be happy to add that; it is in VB however).
Current Code - This looks at a value chosen by the user (searchMeth) and enables searching on that column. It then does the search for what was entered in the txtSearch box. However, in addition to this, I want to add in an additional filter for Supervisor. (See the AndAlso comment
olvEmps.UseFiltering = True
OlvColumn1.Searchable = False
OlvColumn2.Searchable = False
OlvColumn4.Searchable = False
OlvColumn3.Searchable = False
OlvColumn5.Searchable = False
Select Case searchMeth
Case "Name"
OlvColumn1.Searchable = True
Case "Employee Number"
OlvColumn2.Searchable = True
Case "Department"
OlvColumn3.Searchable = True
End Select
olvEmps.OwnerDraw = True
Dim tFilter As BrightIdeasSoftware.TextMatchFilter = BrightIdeasSoftware.TextMatchFilter.Contains(olvEmps, txtSearch.Text)
'andalso olvColumn5 = supeName?
olvEmps.ModelFilter = tFilter
olvEmps.DefaultRenderer = New BrightIdeasSoftware.HighlightTextRenderer(tFilter)
OlvColumn1.Searchable = True
OlvColumn2.Searchable = True
OlvColumn3.Searchable = True
OlvColumn4.Searchable = True
OlvColumn5.Searchable = True
I'm sure the PredicateBuilder solution will work, but ObjectListView comes with a simpler solution already.
TextMatchFilter can be limited to which columns it searches via the Columns property. Set this to an array of columns that you want to consider.
TextMatchFilter filter1 = TextMatchFilter.Contains(olvEmps, txtSearch.Text)
filter1.Columns = new [] { this.olvColumn1, this.olvColumn2 };
You can combine two filters using the CompositeAllFilter to match two or more other filters.
this.olvEmps.ModelFilter = new CompositeAllFilter(new List<IModelFilter> { filter1, filter2 });
Though I don't yet fully understand your deal, I'll give it a shot with the PredicateBuilder that is part of the LINQKit assembly which you can download here.
As such, filtering on multiple columns shall get easy. Perhaps shall you consider to reset the binding of your ObjectListView control once your source collection has been filtered.
Grossly, I would do about the following:
Load your datum;
Display them through data binding;
Once a column is clicked for filter, make a call to your "Filter" method which will apply your predicates;
Rebind your control with the new filtered collection.
Please refer to the PredicateBuilder documentation at the link provided previously. Another example to building dynamic filters is illustrated here: "How would this query translate into a dynamic Linq expression?" for a search engine I implemented.
In my case, the filters were applied directly against the database results. Aside, it can even be used in your situation with in-memory datum since it is Linq based.
I'm sure I'll be able to provide further assitance when you post your code sample for filtering the information.
EDIT #1
After I have read the code sample provided, here's what I believe would do the trick. As for the Searchable property, I am no familiar of this approach, so maybe I can miss something important out of your code and if it is so, feel free to point me what I could have missed. =)
Note that I assume that all of your datum are string, since I am verifying whether your datum is null or white space. Furthermore, the way I see it, to filter a result set is to bring visible only records which meet certain criterion. You don't want to see what doesn't meet the criterion. It's the same as a WHERE clause in SQL.
public class FilterCriterion {
public bool HasEmployeeName { get { return !string.IsNullOrWhiteSpace(EmployeeName); } }
public bool HasEmployeeNumber { get { return !string.IsNullOrWhiteSpace(EmployeeNumber); } }
public bool HasDepartment { get { return !string.IsNullOrWhiteSpace(Department); } }
public string EmployeeName { get; set; }
public string EmployeeNumber { get; set; }
public string Department { get; set; }
}
The FilterCriterion class shall be used to apply any filter that you want against your data source, collection or whatsoever.
var employees = LoadEmployeesFromUnderlyingDataStore();
var criterion = new FilterCriterion();
switch(searchMeth) {
case "Name": filter.EmployeeName = "the name to filter by"; break;
case "EmployeeNumber": filter.EmployeeNumber = "the number to filter by"; break;
case "Department": filter.Department = "the department to filter by"; break;
}
var filter = PredicateBuilder.True<Employee>(); // assuming you have an employee class.
if (criterion.HasEmployeeName)
filter.And(e => e.Name.ContainsLike(criterion.EmployeeName));
if (criterion.HasEmployeeNumber)
filter.And(e => e.EmployeeNumber.ContainsLike(criterion.EmployeeNumber));
if (criterion.HasDepartment)
filter.And(e => e.Department.ContainsLike(criterion.Department));
var filteredEmployees = employees.Where(filter);
// Supply your ObjectListView the way you're used to and this shall function.
Aside, you could also, if you have to deal with such string variables write a ContainsLike extension method to the string class.
namespace System {
public static class StringExtensions {
public static bool ContainsLike(this string input, string value) {
if (string.IsNullOrWhiteSpace(input) || string.IsNullOrWhiteSpace(value)) return false;
input = input.ToLower().RemoveDiacritics();
value = value.ToLower().RemoveDiacritics();
if (string.IsNullOrWhiteSpace(input) || string.IsNullOrWhiteSpace(value)) return false;
return input.Contains(value);
}
public static string RemoveDiacritics(this string input) {
return input == null ? null :
Encoding.ASCII.GetString(Encoding.GetEncoding(1251).GetBytes(input));
}
}
}
I do hope this helps, otherwise inform me of what I misunderstood from your question and we'll try to figure this out together.
Should you need the VB version of this code, I'll try to translate to the best of my VB knowledge.
This code is provided as is and has not been tested, except for both the string extension methods.
I am struggling with how I do the following in LightSwitch. I am 'intercepting' the entity during the partial void tblStaffExtendeds_Updating(tblStaffExtended entity) and then pulling some data from our local ldap and trying to set one of the properties of the entity to a specific value, like so:
partial void tblStaffExtendeds_Updating(tblStaffExtended entity)
{
string ldapPath = #"LDAP://DC=myLDAP,DC=myLDAP";
string user = entity.GPEmployeeID.ToString();
string[] props = {
ActiveDirectoryInfo.strings.DISPLAYNAME, ActiveDirectoryInfo.strings.EMAIL,
ActiveDirectoryInfo.strings.LOGONALIAS, ActiveDirectoryInfo.strings.PHONE,
ActiveDirectoryInfo.strings.OFFICE, ActiveDirectoryInfo.strings.TITLE,
ActiveDirectoryInfo.strings.GPEMPLOYEEID
};
var propResults = ActiveDirectoryInfo.UserPropertySearchByGPEmpID(user, ldapPath, props);
entity.tblAdminStaffType.StaffType = propResults[ActiveDirectoryInfo.strings.TITLE];
entity.WorkEmail = propResults[ActiveDirectoryInfo.strings.EMAIL];
entity.UserID = propResults[ActiveDirectoryInfo.strings.LOGONALIAS];
entity.WorkPhone = propResults[ActiveDirectoryInfo.strings.PHONE];
}
Now for fields like WorkEmail and WorkPhone this works fine as those properties are just strings and that is what I am getting from LDAP.
However I do I go about setting the StaffType which is a reference to an Admin Table entry? LDAP returns a string which matches the description on the Admin Table but on the Entity I would need to set it to the correct ID, I am assuming.
Is there someway to do this, short of creating "Look Up" methods to find the ID from the Admin Table by matching the Description to my String from LDAP?
The solution should look something like this:
string title = propResults[ActiveDirectoryInfo.strings.TITLE];
var qryAdminStaffType = from st in DataWorkspace.ApplicationData.StaffTypes
where st.Title == title
select st;
entity.tblAdminStaffType.StaffType = qryAdminStaffType.FirstOrDefault();
In this example, I'm assuming that your data source is called ApplicationData (the LS default name), that the StaffTypes table holds your staff types, and that you are matching the Title attribute. Note that if there is no match to the title, FirstOrDefalt() will return null.