Can we add LINQ where condition of method parameters - c#

Below is my existing method using inside some condition
public List<MenuItem> getTopMenu(List<Role> Id)
{
// here many lines code for checking id value
}
I am changing above method to check where condition inside method parameter like below but not working code
public List<MenuItem> getTopMenu(List<Role> Id.Where(r=>r.status=="Active"))
{
}
Error is coming i need to pass only active id inside method i can not write individually to every method.

Move the filter inside the method:
public List<MenuItem> getTopMenu(List<PersonRoleDTO> Id)
{
Id = Id.Where(r=>r.status=="Active").ToList();
// Whatever else
}
You cannot put statements as parameters to a method.

I think it would better if you add the default parameter (status="Active") on your method and use it like below.
Also this makes your method more usable if you want to query with another status later.
public List<MenuItem> getTopMenu(List<PersonRoleDTO> Id, string status="Active")
{
var query = Id.Where(r=>r.status==status);
//your logic
}

This is syntax and a design issue.
Syntax:
You can't pass a condition as a parameter it doesn't make sense.
Design:
You have multiple ways of doing this. My two suggestions are, either you can have a method to return the predicate to obtain only the active "Roles":
private Func<Role, bool> OnlyActiveRoles(IList roles) => r => r.status == "Active";
Or you can just filter the Roles list before calling those methods.

Related

Accessing a property of a non-static class without assigning it to a variable

I am writing a custom automation framework using Selenium, C# and Page Object Pattern.
I am trying to extract some values from the pages without stopping the statement.
Basically I want to turn this:
var page = Login()
.OpenPage();
page.Remember(page.Title);
page.MoreActions()
.MoreNavigation();
into this:
var page = Login()
.OpenPage()
.Remember(???.Title)
.MoreActions()
.MoreNavigation();
Not sure if it is possible and how I could access the class at that point.
Notes:
All the methods return pages
There is a base Page class and a lot of PageName : Page<PageName> classes representing the actual classes that I use in the tests
I want to implement the Remember method in the Page class so that I can use it everywhere.
Classes have Properties that I want to use to remember the values
LoginPage : NavPage<LoginPage>
{
public override string Title => "Hardcoded Title";
public string OtherProperty => pageContent.GetSomeValue();
...
}
I have a dictionary of parameters in the tests. I want to remember the value in one of the parameters.
Solutions that I cannot use:
Making the classes static and using the class name is not an option as we have our page structure based on inheritance and overloading methods.
Creating RememberTitle methods on all the pages is not an option because I want to remember the value into a Dictionary of parameters and such a parameter cannot be passed by reference.
Update:
As per comments I tried implementing a lamda expression:
public static NavPage<T> Remember(this NavPage<T> page, Func<NavPage<T>, string> getTitle)
{
var title = getTitle(page);
//use Title
return page;
}
.Remember(p => p.Title);
The new error is There is no argument given that corresponds to the required formal parameter getTitle
What you can do is to write an extension method to achieve your desired result.
Let's say the page that you mentioned is of type Page & the Title that you've mentioned is of type string. Then you can have the following extension method:
public static Page Remember(this Page page, Func<Page, string> getTitle)
{
var title = getTitle(page);
// Use the title
page.Remember(page.title);
// Return page object for further chaining
return page;
}
Then you can use this extension method the following way:
var page = Login()
.OpenPage()
.Remember(p => p.Title)
.MoreActions()
.MoreNavigation();

Service method with changing parameter

I have a service method:
public long InsertMessage(OutgoingInEntity input)
{
var request = new InsertOutgoingMessageRequest
{
Id = input.Id
... // fields
};
return Util.UsingWcfSync<IOutgoing, long>(client => client.InsertOutgoing(request));
}
I want to reuse this method in other context because I want 1 method which call this specific service, but the parameter OutgoingInEntity can change. When I call this method with other entities, the fields used in InsertOutgoingMessageRequest will be available and I will map like I did with the var request I cannot initiate InsertOutgoingMessageRequest in other context.
How can I say this input parameter is like generic and can be used for all kind of entities?
If you want to manage the object you receive you can just do this:
public long InsertMessage(Object input)
{
OutgoingInEntity yourObj = (OutgoingInEntity)input;
///.. your code ..///
}
Then you can do just the same for whatever you need.

Is it possible to set the where clause through a data annotation?

May be this is a stupid question.
I have some model class in a Asp.Net web api 2.2 application, which implements an interface ICountryOfOrigin.
I need to filter records by applying where clause as shown below. I have to repeat this logic in many controllers with different models which implement ICountryOfOrigin.
Is it possible to move the filtering logic into a separate method and apply it to the controller action through data annotation?
My intention is to eliminate the repeating code.
Is it possible?
//Interface
public Interface ICountryOfOrigin
{
string Country {get;set;}
}
//Model
public class Product : ICountryOfOrigin
{
..
string Country {get;set;}
}
//Action
public IHttpActionResult Get()
{
List<string> euCountries = GetEuCountries();
Product product = _repository.Products.GetAll().Where(p=> euCountries.Contains(p.countries); // The filter is applied here
return Ok(products);
}
//Need to achieve something like this
[EuCountriesOnly]
public IHttpActionResult Get()
{
List<string> euCountries = GetEuCountries();
Product product = _repository.Products.GetAll();
return Ok(products);
}
Any experts help me on this?
I guess I would shoot anybody who would implemented it the way you want. Believe me, it's not cool at all to alter behavior of some method you're calling with an attribute you put on a calling method.
Just put the logic into an extension method on your repository type and call it a day:
public static class RepoExtensions
{
private static readonly euCountries = new Country[]{};
public static IEnumerable<ICountryOfOrigin> GetEU(this Repository repo)
{
return repo.Products.GetAll().Where(p=> euCountries.Contains(p.countries);
}
}
I'm assuming that your repository is of type Repository, you'll need to put the real type instead of it.

How to query a SingleResult object in a ASP.NET based server?

How do I query a SingleResult object in a ASP.NET based server? Please help with the below code.
public class UserController : TableController<User>
{
...
public Task DeleteUser(string id)
{
SingleResult<User> user = Lookup(id);
// I can drill down into user via the debugger and see the data
// I'm looking for. But I don't know how to translate what I see
// in the debugger to a .Net call.
// This is what I see in the debugger:
// new System.Linq.SystemCore_EnumerableDebugView
// <AlertMeService.DataObjects.User>(result.Queryable).Items[0].GroupNm
// which equals "fooGrp"
// The below call doesn't work
// string GroupNm = user.GroupNm;
// I need GroupNm from the object to send a group notification
PostNotification(user.GroupNm, ...);
return DeleteAsync(id);
}
...
Any help is greatly appreciated.
SingleResult returns an IQueryable, so use the Linq Single or SingleOrDefault methods to execute the query and get the result.
Single will throw an exception if 0, 2 or more values are returned, and SingleOrDefault will allow either 0 or 1 value and will throw if 2 or more than values are returned. If you want multiple values then use First/FirstOrDefault or Last/LastOrDefault, or some other terminal Linq method, as appropriate
SingleResult<User> userQuery = Lookup(id);
User user = userQuery.SingleOrDefault();
if( user == null ) {
}
else {
}
According to your description, I have checked the SingleResult class:
public sealed class SingleResult<T> : SingleResult
{
//
// Summary:
// Initializes a new instance of the System.Web.Http.SingleResult`1 class.
//
// Parameters:
// queryable:
// The System.Linq.IQueryable`1 containing zero or one entities.
public SingleResult(IQueryable<T> queryable);
//
// Summary:
// The System.Linq.IQueryable`1 containing zero or one entities.
public IQueryable<T> Queryable { get; }
}
Per my understanding, you could modify your DeleteUser method as follows:
public Task DeleteUser(string id)
{
SingleResult<User> result = Lookup(id);
var user=result.Queryable.SingleOrDefault();
if(user!=null)
{
//I need GroupNm from the object to send a group notification
PostNotification(user.GroupNm, ...);
return DeleteAsync(id);
}
return Task.FromException($"the specific user with userid:{id} is not found.");
}

User is null when Function not called from UI

In my database table I have a column in which the values are manipulated before saving into the database. The logic for the manipulation was added at a later stage of development, after a lot of values were inserted to the table. Now I want to edit the contents of the table to manipulate the values of the existing contents.
My approach
To call the edit function for all the items in the table as the manipulation logic is added in the EDIT action method as well.
When I call the edit function while looping through the contents in the database, I get a null reference exception which is not there when I use the edit function from the UI.
EDIT Action method
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
// Valuestring from another function
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
The call to this function was made from the HomeController. I commented all the return statements in the EDIT method while calling it from the HomeController.
Exception
Object reference not set to an instance of an object.
Question
Why does the edit work while called from the UI without any Exception , but not from HomeController?
Why is the user null even when I call the function from a different controller? Using windows authentication. How do I get the user name if the user has already been authenticated?
EDIT - Added how the Edit function is called
//Home controller (But I have also tried calling the function from a different controller where the call to the method is from the UI
public ActionResult Index()
{
test();
return View();
}
public void test()
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
}
}
Update - General Overview of the problem
The question can be generalized so as to find an answer how the User property is defined when using Windows Authentication.
Is it that the controller gets access to the Windows user property only when it is called from the UI?
Is it not possible to access the User Property in a function that is not called via UI?
And the best thing as far as I know about the issue is that, the User is defined in the Controller where the testmethod is called but not in the Edit in another controller.
You are partially correct when you are saying you can only access the User property only accessing from the UI. The correct answer is -
When accessing the Edit method through the UI, it is actually going through the ASP.Net controller initializer pipeline and that environment takes care of the current browser session and assigns the User variable. HttpContext is available at this time.
But when you are creating the controller variable like this -
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
You are bypassing all those initialization codes. No HttpContext and this is just another normal object and thus it does not have the User property populated.
ANSWER TO QUESTIONS:
Is it that the controller gets access to the Windows user property only when it is called from the UI?
=> Yes, absolutely right, because you are going through the ASP.Net pipeline. But it is not only for Windows user, it is all those things that re in a HttpContext.
Is it not possible to access the User Property in a function that is not called via UI?
=> Only if, you manually assign it otherwise NO.
MORE INSIGHT:
Basically, what you are trying to achieve is a very poor design. Nowhere, remember nowhere, you are supposed to call a controller from inside a controller unless it is a subclass to base method call. The only way you can call another controller from inside another controller is redirecting the execution by using "Redirect" methods. No one will stop you from calling controller like this, but this shows poor design principle..
The best way to solve your situation is like this -
public class ModelService {
public void Edit(IPrincipal user, SetValue setValue){
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
Then in both the controllers constructors -
public class ControllerOne : Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
_modelService.Edit(User, Setvalue);
}
}
//controller 2
public class HomeController: Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Index()
{
foreach(var item in db.SetValue.ToList())
{
_modelService.Edit(User, item);
}
}
}
You can take help from IoC Container for dependency injection, that is even better approach.
Invoking a Controller from within another Controller is probably getting some data (as the User) to be missing. I wouldn't be making much effort understanding why (unless curious), since doing it this way might be considered as bad design. (I bet some other info would be missing as well, maybe cookies and such). the better thing you can do is to separate the logic from your Controllers into some service layer and call the methods with the IPrincipal User as parameter. If you are forced to do it the way you described, then send the user as a parameter to the other controller.
public ActionResult Index()
{
test(User);
return View();
}
public void test(IPrincipal user)
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item, user);
}
}
And the oter controller
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue, IPrincipal user = null)
{
var currentUser = User == null? user : User;//or somthing like that
}
Didn't check this code. but it should give you something to work with.

Categories