Best practice: multiple entities to single entity - c#

This is a very hard to explain question and I hope my code extract explains most of it.
Let's say you have the following database design:
musicstyle relations http://img190.yfrog.com/img190/2080/musicstylerelations.jpg
And you want to build one generic interface to modify the musicstyle relations between all three entities. Currently I have created a MusicStyleController which requires the type of Entity it is related to (Member, Event or Band).
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult DeleteMusicStyle(int id, string type, int typeid)
{
if (!(Session["MemberLoggedIn"] is Member)) return Json(string.Empty);
Member member = (Member)Session["MemberLoggedIn"];
switch (type) {
case "member":
_memberService.DeleteMusicStyle(member, id);
break;
case "band":
Band band = _bandService.GetBand(typeid);
_bandService.DeleteMusicStyle(band, id);
break;
case "event":
Event #event = _eventService.GetEvent(typeid);
_bandService.DeleteMusicStyle(#event, id);
break;
}
return SelectedMusicStyles();
}
I make myself sick writing such code, but can't find another, more elegant way.
Note that this function is called using jquery.post().
The question
How would you refactor this code, and would you normalize the database even more? Keep in mind that I'm using the Entity Framework as a data model.

Assuming that id represents the member's id, you could create 3 separate functions to handle each type, thus separating your concerns more than they are now.
Example:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult DeleteMusicStyleByMember(int id)
{
if (!(Session["MemberLoggedIn"] is Member)) return Json(string.Empty);
Member member = (Member)Session["MemberLoggedIn"];
_memberService.DeleteMusicStyle(member, id);
return SelectedMusicStyles();
}
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult DeleteMusicStyleByBand(int id, int typeid)
{
Band band = _bandService.GetBand(typeid);
_bandService.DeleteMusicStyle(band, id);
return SelectedMusicStyles();
}
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult DeleteMusicStyleByEvent
(int id, int typeid)
{
Event event = _eventService.GetEvent(typeid);
_bandService.DeleteMusicStyle(event, id);
return SelectedMusicStyles();
}
Then you would just modify your jquery post to go to the respective methods depending on what you're trying to do.

How would you refactor this code?
1) The code which checks the user is logged in should be moved:
if (!(Session["MemberLoggedIn"] is Member)) return Json(string.Empty);
Member member = (Member)Session["MemberLoggedIn"];
This is a cross cutting concern, which should be applied using a security framework, Spring pops to mind as an example.
2) I would avoid using a singleton pattern to represent this use-cases, they can quickly turn into a collection of scripts which when grow large can be difficult to know where to place code. Consider using the Command Pattern instead.
This pattern will allow you to return the results as JSON, XML or any other format based on the interfaces you which your command to conform too.
class DeleteMusicStyleByBandCommand : JsonResultModelCommand, XmlResultModelCommand {
public DeleteMusicStyleByBand(int id, int typeid) {
//set private members
}
public void execute() {
..
}
public JsonResult getJsonResult() { .. }
public XmlResult getXmlResult() { .. }
}
The Command pattern IMHO is much better at representing use-cases than many methods in a Service..

Related

Getting error in MVC Model regarding use of List(T)

I am learning to build the application using one of the online tutorials regarding MVC. It requires to create a user db.
I am getting the following error while building the application. I have just copy-pasted the code from the tutorial. I googled few things, but I am not getting it. Please help to resolve and explain.
using System;
using System.Collections.Generic;
using System.Collections;
using System.EnterpriseServices;
namespace AdvancedMVCApplication.Models
{
public class Users
{
public List UserList = new List();
//action to get user details
public UserModels GetUser(int id)
{
UserModels usrMdl = null;
foreach (UserModels um in UserList)
if (um.Id == id)
usrMdl = um;
return usrMdl;
}
//action to create new user
public void CreateUser(UserModels userModel)
{
UserList.Add(userModel);
}
//action to udpate existing user
public void UpdateUser(UserModels userModel)
{
foreach (UserModels usrlst in UserList)
{
if (usrlst.Id == userModel.Id)
{
usrlst.Address = userModel.Address;
usrlst.DOB = userModel.DOB;
usrlst.Email = userModel.Email;
usrlst.FirstName = userModel.FirstName;
usrlst.LastName = userModel.LastName;
usrlst.Salary = userModel.Salary;
break;
}
}
}
//action to delete exising user
public void DeleteUser(UserModels userModel)
{
foreach (UserModels usrlst in UserList)
{
if (usrlst.Id == userModel.Id)
{
UserList.Remove(usrlst);
break;
}
}
}
}
}
Error: CS0305: Using the generic type 'List' requires 1 type arguments\Models\Users.cs Line:11
You can view the example here: https://www.tutorialspoint.com/mvc_framework/mvc_framework_advanced_example.htm
I was going to say "maybe the code blocks on tutorialspoint hide the necessary <xxx> after the List because it gets interpreted as an HTML tag".. but then I saw the next code block had actual html tags in just fine
To expand on the point Klaus made, it is possible to write classes in C# that are completed by the compiler rather than you. You specify some placeholder for the type of object the class deals with and then the compiler can use it to create an actual class in the background for you
class TenThings<T>{
private T[] _things = new T[10];
private T GetFirst(){
return _things[0];
}
}
T isn't any type in your program, or in the framework, for the purposes of this class/as written here but if you then say somewhere else:
var tt = new TenThings<string>();
Then the compiler can know "anywhere T is mentioned, in this case it needs to be a string" so it can knock together a class for you that is an array of ten strings and has a GetFirst method that returns a string. On the very next line you can have a TenThings<int> and you'll get another different type of class out that deals with ints. You created a template for the compiler to use to write code for you, and the benefit you get is that your GetFirst really does return a string in one case and an int in another. You could have just made a class like this:
class TenThings{
private object[] _things = new object[10];
private object GetFirst(){
return _things[0];
}
}
But then you have to cast everything that comes out - old classes like ArrayList worked this way, and it wasn't a great experience
List is a generic class like this new "templates" way; you really need to have another type of class in angle brackets after its name, such as List<UserModel> and it becomes a part of the type at the same time as dictating to the compiler how to create the template. Per the comment it seems that tutorials point forgot to put the relevant <UserModels> after the List
There are a few other things I take exception to in that tutorial, but talking specifically about this property; creating the List as a public field for one, calling the class UserModels when it seems to represent a single item (unwarranted plural / collections of items are typically recommended to have a name that ends with "Collection" - plurals are used for properties that are collections), I.e. it should be public List<UserModel> UserModels { get; set; } = new List<UserModel>();. I'll leave picking on it for not being a read only collection typed as something generic like IEnumerable<T> for another time :)

How do I design multi-role authorization on post requests?

In the system I am building there is a complex and continuously changing resource-based authorization. There is a total of six roles at the moment.
The system is handling members where all members can edit basic info on their own profile, another person in another role can edit even more info on their profile and so on.
I cannot figure out which is the best way to design this with endpoints / actions for posts like the edit member action. What I ended up doing, but dislike, is that each role has one controller action, view and view model. The main reason for doing this instead of having one view model is that I felt it did not make sense to have all the properties that someone cannot even edit, that's over-posting right?
I am not quite happy with the result. 6 view models, 6 views, 6 madly similar controller actions, 6 validators etc.
My idea now is that I will just have one edit action and then have a bunch of if statements when mapping back to the domain object, in the view and on the validator classes. The overposting is still there but managed with if statements. I'm also thinking like this - what if the system would become an API? api/members/1/edit/ makes more sense than api/members/1/editAsTreasurer?
What do you think? Anyone has another solution I have not thought of?
Some code parts, example of duplicated code, of course there's more in validator classes, views, and mapping, not sure how much to include:
[HttpPost]
public IActionResult EditAsSecretary(EditMemberAsSecretaryViewModel viewModel)
{
if (!ModelState.IsValid)
{
viewModel.Init(_basicDataProvider, _authorizationProvider.GetAuthorizedLogesForManageMember());
return View("EditAsSecretary", viewModel);
}
var member = _unitOfWork.Members.GetByMemberNumber(viewModel.MemberNumber, true);
if (member == null) return NotFound();
// Authorize
if (!_authorizationProvider.Authorize(viewModel.MemberInfo.LogeId, AdminType.Sekreterare))
return Forbid();
var user = _unitOfWork.Members.GetByUserName(User.Identity.Name);
var finallyEmail = viewModel.MemberContactInfo.Email != null && member.Email == null &&
!member.HasBeenSentResetPasswordMail && member.MemberNumber != user.MemberNumber;
_domainLogger.UpdateLog(viewModel, member, user);
UpdateMember(viewModel, member, user.Id);
_unitOfWork.Complete();
if (finallyEmail) SendUserResetPasswordMail(member).Wait();
TempData["Message"] = "Member has been updated.";
return RedirectToAction("Details", "Members", new { memberNumber = member.MemberNumber });
}
[HttpPost]
public IActionResult EditAsManager(EditMemberAsManagerViewModel viewModel)
{
if (!ModelState.IsValid)
{
viewModel.Init(_basicDataProvider, _authorizationProvider.GetAuthorizedLogesForManageMember());
return View("EditAsManager", viewModel);
}
var member = _unitOfWork.Members.GetByMemberNumber(viewModel.MemberNumber, true);
if (member == null) return NotFound();
// Authorize
if (!_authorizationProvider.Authorize(member.LogeId, AdminType.Manager))
return Forbid();
var user = _unitOfWork.Members.GetByUserName(User.Identity.Name);
var finallyEmail = viewModel.MemberContactInfo.Email != null && member.Email == null &&
!member.HasBeenSentResetPasswordMail && member.MemberNumber != user.MemberNumber;
_domainLogger.UpdateLog(viewModel, member, user);
UpdateMember(viewModel, member, user.Id);
_unitOfWork.Complete();
if (finallyEmail) SendUserResetPasswordMail(member).Wait();
TempData["Message"] = "Member has been updated.";
return RedirectToAction("Details", "Members", new { memberNumber = member.MemberNumber });
}
private void UpdateMember(EditMemberAsSecretaryViewModel viewModel, Member member, string userId)
{
_mapper.Map(viewModel, member);
MapGodfathers(viewModel.MemberInfo, member);
AfterUpdateMember(member, userId);
_userManager.UpdateNormalizedEmailAsync(member).Wait();
}
private void UpdateMember(EditMemberAsManagerViewModel viewModel, Member member, string userId)
{
_mapper.Map(viewModel, member);
MapGodfathers(viewModel.MemberInfo, member);
AfterUpdateMember(member, userId);
_userManager.UpdateNormalizedEmailAsync(member).Wait();
}
My idea now is that I will just have one edit action and then have a bunch of if statements when mapping back to the domain object, in the view and on the validator classes. The overposting is still there but managed with if statements
Don't.
Besides making the code much less readable, it also poses a security risk. Every Action should take as little parameters as it needs. It does not cost you anything to have more Actions so there is no reason for doing that.
There are some issues though with your code, that help in that duplication:
You seem to be making security validations against what you receive from the user, instead of using the currently authenticated user. This is a big issue, as you are trusting the data that comes from a user.
Instead of that, create a custom(s) Authorization Policy that checks for the type of user using your business logic. Those can be then added to the built-in container and you can use:
[Authorize(Policy = "EnsureManager")]
public IActionResult EditAsManager(...)
This would allow you to remove all that duplicated code and be closer to the SRP.
Your duplicated UpdateMember looks like your models are unrelated. In a case like this, it would be far better to have a base model and then children with the required properties:
public abstract class EditMemberBaseViewModel
{
[Required]
public Something Something { get; set; }
}
public class EditMemberAsSecretaryViewModel : EditMemberBaseViewModel
{
[Required]
public AnotherThing AnotherThing { get; set; }
}
That would allow you to have a single UpdateMember since the logic is based on EditMemberBaseViewModel and not their children, as far as you have shown that is:
private void UpdateMember(EditMemberAsManagerViewModel viewModel, Member member, string userId)
{
_mapper.Map(viewModel, member);
MapGodfathers(viewModel.MemberInfo, member);
AfterUpdateMember(member, userId);
_userManager.UpdateNormalizedEmailAsync(member).Wait();
}
As a last and probably most important point, there is a problem with this code:
_userManager.UpdateNormalizedEmailAsync(member).Wait();
That's really bad. You are making ASP.NET Core hang an entire thread waiting for that action to complete. That's synchronous, 2000s code.
You need to learn to use asynchronous code for every IO-related operation (like database calls) in your application, otherwise performance will suffer lots. As an example:
public async Task<IActionResult> EditAsManager(...)
{
.....
await UpdateMemberAsync(...);
}
public async Task UpdateMemberAsync(...)
{
await _userManager.UpdateNormalizedEmailAsync(member);
}

DESIGN -- Remove by name or object?

Does it make more sense to remove an object using its name/id or passing the actual object? For example:
void MyList::remove(MyObject &myObject) { /* blah */ }
// or
void MyList::remove(std::string id) { /* blah */ }
I've used both but I can't really see what the advantages vs disadvantages. Is there a preferred standard?
EDIT: this would probably be a better example providing what I'm trying to do:
Let's say I have an Account class with a collection of Transactions. Am I better to pass Transaction object or the id of the Tranaction?
class Account
{
private List<Transaction> transaction = new List<Transaction>();
public void Remove(Transaction transaction) { }
// OR
public void Remove(string name) { }
// OR
public void Remove(Guid id) { }
}
NOTE: this question has both C++ and C# code...
You may not have the item reference always. So it is better to have remove methods by name or id.
You can decide which one is preferable (name or id) according to your business requirement. name or id has to be unique otherwise it will remove the wrong item. So the business requirement has to decide it.

How do I use WebAPI/Rest correctly when other params are needed

I am new to WebAPI and rest and am trying to do things correctly. By default if I were to access something such as User I would call api/user/5 if I wanted user 5. This would go to my User controller to Get(int num) I think. But I know I will often need other params passed as well. Currently I have Get(JObject data), but that data param is for other parameters. I will need other optional params whether I am sending an ID or wanting a list of everything. How do I go about organizing methods properly with WebAPI? Am I misunderstanding something?
To clarify:
This question is more about REST than dynamic objects, though they play a part:
How do I get a single resource vs a list of resources when I need additional params. I see those concepts as two separate methods, but the additional params complicate it in my mind when routing is involved.
Use attribute routing
For example -
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
or
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
if you need to return a list, create a method that returns a list, otherwise return the specific item requested
Look into using JToken or the even more dynamic 'dynamic' (Taken from here)
"
JSON and JavaScript is really dynamic, though, and often it's a hassle to try to "deserialize" really dynamic JSON objects into strongly-typed .NET structures. JSON.NET and ASP.NET Web API's model binding offer a happy medium - a middle ground - called JToken.
public class ContactController : ApiController
{
public JToken Post(JToken contact)
{
return contact;
}
}
Using JToken gives a dynamic container but also a DOM-like navigation model. But if that's not dynamic enough for me, why can't my method's parameter just take a "dynamic."
C# is statically typed, sure, but that doesn't mean I can't statically type something dynamic. ;)
Again, note the watch window.
Using dynamic to catch JSON post payloads
public class ContactController : ApiController
{
public dynamic Post(dynamic contact)
{
return contact;
}
}
"
I think you should make a new object for each WebAPI function that will handle the request. You can make the parameters optional with nullable properties.
[HttpPost]
public void SampleFunction(SampleFunctionModel model)
{
}
where SampleFunctionModel is:
public class SampleFunctionModel
{
public int? Id { get; set; }
public string Name { get; set; }
}

C# OOP - Return a string from a Business Object that performs Validation and Inserts a record - Good Practice or Not?

Just curious if someone can shed some light on if this is a good practice or not?
Currently I am working on a C# project that performs and Inserts a record and runs through 4 or 5 methods to validate that the record can be added and returns a string that tells the presentation layer if the record has been submitted or not.
Is this a good practice? Pros/Cons?
The call from the presentation is:
protected void btnProduct_Click(object sender, EventArgs e)
{
lblProduct.Text = ProductBLL.CreateProduct(txtProductType.Text, txtProduct.Text, Convert.ToInt32(txtID.Text);
}
The BLL method is:
public class AccountBLL
{
// Create The Product w/ all rules validated
public static string CreateProduct(string productType, string product, int id)
{
// CHECK IF PRODUCT NAME IN DB
else if (ValidateIfProductNameExists(product) == true)
{
return "Invalid Product Name";
}
// CHECK IF 50 PRODUCTS CREATED
else if (ValidateProductCount(id) == true)
{
return "Max # of Products created Can't add Product";
}
// CHECK IF PRODUCT TYPE CREATED
else if (ValidateProductType(productType) == false)
{
return "No Product Type Created";
}
// NOW ADD PRODUCT
InsertProduct(productType, product,id);
return "Product Created Successfully";
}
As mentioned in the previous post, use Enum types.
Below is a sample code that could be used in your application.
public struct Result
{
public Result(ActionType action, Boolean success, ErrorType error) :
this()
{
this.Action = action;
this.HasSuceeded = success;
this.Error = error;
}
public ActionType Action { get; private set; }
public Boolean HasSuceeded { get; private set; }
public ErrorType Error { get; private set; }
}
public enum ErrorType
{
InvalidProductName, InvalidProductType, MaxProductLimitExceeded, None,
InvalidCategoryName // and so on
}
public enum ActionType
{
CreateProduct, UpdateProduct, DeleteProduct, AddCustomer // and so on
}
public class ProductBLL
{
public Result CreateProduct(String type, String name, Int32 id)
{
Boolean success = false;
// try to create the product
// and set the result appropriately
// could create the product without errors?
success = true;
return new Result(ActionType.CreateProduct, success, ErrorType.None);
}
}
Don't use hardcoded strings.
Use an Enum for the return value, you can do much more and more efficiently with enums.
Validations must be done, only thing you can improve is to put the whole validation process in a single method.
After you call the method, you can have a single if sentence in the main method to check the enum returned.
if (IsValidated(productType, product,id) == MyEnumType.Success) { }
I'd use exceptions rather than a string or a enum...
I would recommend looking at the Validation framework used by Imar Spaanjaar in his N-Layer architecture series. The framework he uses if very versatile and it even supports Localization through using Resource files for the validation strings.
It is not a best practice to return a string with the status of the method.
The main reason is that it violates the separation of concerns between the UI layer and the business layer. You've taken the time to separate out the business logic into its own business layer; that's a good thing. But now the business layer is basically returning the error message directly to the UI. The error message to display to the user should be determined by the UI layer.
With the current implementation the business layer also becomes hard to use (for anyone without explicit knowledge of the implementation) because there is no contract. The current contract is that the method will return a string that you should display to the user. This approach makes reuse difficult. Two common scenarios that could cause headaches are if you want to support a new language (localization) or if you want to expose some of these business methods as a service.
I've been bitten when trying to use some old code like this before. The scenario is that I want to reuse the method because it does exactly what I want but that I want to take some action if a specific error occurs. In this case you end up either rewriting the business logic (which is sometimes not possible) or you end up having to hard code a horrible if statement into your application. e.g.
if (ProductBLL.CreateProduct(productType, product, ID) ==
"Max # of Products created Can't add Product")
{
...
}
Then a requirement comes down that the message should be changed to something different ("The maximum number of products has been exceeded. Please add less products and try again."). This will break the above code. In production. On a Saturday night.
So in summary: don't do it.

Categories

Resources