public ActionResult AddComplianceForm(string TemplateName)
{
}
In this ASP.net MVC5 application there is a folder Templates that contains a bunch of different classes that all have different TemplateName attributes. The first part of this method needs to find the class that has a TemplateName matching the string passed in. I then need to create an instance of whatever template matched. I am very new to working with attributes in C# so help would be very appreciated. I mostly need to know how to access that folder of classes in the program to look into it.
What you are trying to do is called "Reflection" in C#.
Below is the link to another answer that shows how to get all the classes in a namespace (I'm assuming that the physical folder implies the use of a unique namespace for the classes contained in the folder.)
Link to StackOverflow answer
**Btw you should look up reflection performance and see if it makes sense in your case. You may want to use a factory pattern instead.
This will work:
public class HomeController : Controller
{
public ActionResult AddComplianceForm(string TemplateName)
{
Assembly assembly = Assembly.Load("Testy20161006"); //assembly name
Type t = assembly.GetType("Testy20161006.Templates." + TemplateName); //namespace + class name
Object obj = (Object)Activator.CreateInstance(t);
return View();
}
Don't just use reflection
The typical way someone would deal with this is with Reflection and run-time type discovery/binding. However, this is probably a poor starting point in this exact situation. The template name is passed in as an action argument, presumably through binding to a value in the request string, and you don't want c# code that will instantiate whatever class is passed in from the web!!! That would be a serious security issue known as an insecure direct object reference.
Create a list
To mitigate the risk, the proper approach is to check the argument against a whitelist. Well, if we have a white list already, we may as well associate each item in the list with a lambda expression that returns the object you want.
class MyController
{
static private readonly Dictionary<string,Func<BaseTemplate>> _templateList = new Dictionary<string,Func<BaseTemplate>>();
static MyController()
{
_templateList.Add("ATemplate", () => return new ATemplate());
_templateList.Add("SomeOtherTemplate", () => return new SomeOtherTemplate());
_templateList.Add("JustOneMore", () => return new JustOneMore());
}
public ActionResult AddComplianceForm(string TemplateName)
{
BaseTemplate template;
try
{
template = _templateList[TemplateName]();
}
catch (KeyNotFoundException exception)
{
RedirectToAction("MyController", "InvalidTemplateError");
}
DoSomethingWithTemplate(template);
}
}
Create the list using Reflection
But what if you have a crap ton of templates? You don't want to hard code all those dictionary entries, right?
Well, you could tag each of the templates with a custom attribute, e.g. [TemplateAttribute], and then populate the list this way:
foreach (Assembly b in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in b.GetTypes())
{
var a = Attribute.GetCustomAttribute(t, typeof(TemplateAttribute));
if (a != null)
{
var localType = t; //Avoid closure on loop variable
_templateList.Add(t.Name, () => Activator.CreateInstance(localType) as BaseTemplate);
}
}
}
This will automatically iterate through all the types that are loaded for your application and find the ones with the TemplateAttribute. Only those types will be allowed in the action argument.
Notes:
In these examples I assume all of your templates inherit from a BaseTemplate, but if they have no ancestor in common (not recommended) I guess you could just use object.
In these examples, I store the list and implement the code in the controller, but if you are going for well-structured code you should consider moving all that stuff into some sort of factory class and just pass the string in from the controller.
Related
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 :)
I have a quick question. Is it possible to use MongoDB with the OnDeserializing attribute or something like that?
MongoClient client { get; } = new MongoClient("mongodb://localhost:27017");
var Userscollection = db.GetCollection<UserModel>("Users");
var userfind = (await Userscollection.FindAsync(x => x.UserId == "UserId"));
var user = userfind.FirstOrDefault();
My UserModel class has a function with the OnDeserializing attribute but it doesn't fire on Find and fetching the item of the user.
[OnDeserializing]
void TestFunc(StreamingContext context)
{
}
Is there any way to fire it automatically or any similar method to detect in the constructor of the class if the class is creating by my codes or using the MongoDB serializer/deserializer?
OK, After poking a lot with attributes and lots of tries finally I found a solution for my case.
I commented above that creating 2 constructors doesn't work because settings values are going to be done after running constructors.
But, There's a workaround! BsonConstructor Attribute
create a simple constructor or with arguments for your own (if you need otherwise you can skip it)
Then create another constructor but using the BsonConstructor attribute like below.
[BsonConstructor()]
public Postmodel(ObjectId postid, ObjectId userid)
{
//Called by the BSon Serialize/Deserialize
}
But surely this constructor will not be what you need because all of the properties are null or having default values so you need to pass some argument too.
The sample below will give you the values of properties you need so you can do whatever you want in the constructor.
[BsonConstructor(nameof(Id), nameof(UserId))]
public Postmodel(ObjectId postid, ObjectId userid)
{
}
Id and UserId are two properties in my class.
You can also simply use
[BsonConstructor("Id", "UserId")]
instead, But be careful, Changing properties name or removing them in the development won't notify you to fix your values, so using nameof(PropertyName) is much safer.
I typed this out in Notepad++ real quick so please forgive any typos/mistakes. If it's possible, I'd be getting rid of some repetitive work (i.e. a long case statement). Not a huge deal but I'm curious if it's possible and if so, how bad would it be to actually implement the code.
jsonFromWebpage = {
StatusUpdate: {
QueryType: "SomeClassName",
LocalCount: 5,
RemoteCount: 5
},
StatusUpdate: {
QueryType: "AnotherClass",
LocalCount: 29,
RemoteCount: 30
}
}
// Model
public class StatusUpdate
{
public string QueryType { get; set; }
public int LocalCount { get; set; }
public int RemoteCount { get; set; }
}
// Controller
public IActionResult GetStatusUpdate([FromBody] List<StatusUpdate> status)
{
_service.GetStatusUpdate(status);
return status
}
// Service
public List<Status> GetStatusUpdate(List<StatusUpdate> status)
{
foreach(var s in status)
{
var typeArgument = s.QueryType; // <--- Is there a way for this...
status.CurrentCount = GetTotalCount<typeArgument>(); // <--- to work here?
status.RemoteCount = thisworksfineforotherreasons(s.QueryType);
}
}
// Repo
public int GetTotalCount<T>() where T: class
{
var result = _db.GetCount<T>();
return result;
}
EDIT
First, thank you to everyone that has responded. Having read everything so far, I wanted to give a little more context. Here's a different take on the example:
// View
<div class="col-12">
<div class="api-types">History</div>
<div class="progress-bar">50 out of 50 copied</div>
</div>
<div class="col-12">
<div class="api-types">Users</div>
<div class="progress-bar">25 out of 32 copied</div>
</div>
// -- View javascript
var types = [];
$(".api-types").each(function (c, i) {
types.push({ ApiAndClassName: $(i).text() });
});
pushToController(JSON.stringify(types));
// Controller
public IActionResult GetSyncStatus(List<SyncStatusVM> status)
{
_service.GetSyncStatus(status);
return Json(status);
}
// Service
public List<SyncStatusVM> GetSyncStatus(List<SyncStatusVM> status)
{
foreach(var s in status)
{
// LocalCount
var magicTypeFigurator = s.ApiAndClassName
s.LocalCount = _repo.GetCount<magicTypeFigurator>(); <-- "this is a variable but should be a type..."
// Remote
var url = $"https://api.domain.com/{s.ApiAndClassName.ToLower()}"
s.RemoteCount = FetchCountFromApi(url);
}
return status;
}
// Repository
public long GetCount<T>()
{
var result = _orm.Count<T>();
return result;
}
// Models
public class SyncStatusVM
{
public string ApiAndClassName { get; set; }
public int LocalCount { get; set; }
public int RemoteCount { get; set; }
}
public class History
{
public long Id {get;set;}
public DateTime CreatedDate {get;set;}
public string Message {get;set;}
}
public class Users
{
public long Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
}
Using this code, I can just create a section in the view and a class for each type. The class is reused by the ORM and desearializing from the API. The most cumbersome point is having a case statement in the controller that calls the generic method with the correct type, based on the "ApiAndClassName". I could edit the ORM so it's string based instead of generic but I don't like that method for various reasons. I could turn the case statement into a collection in the controller or just move it to the service layer but what I have in place already works. I could also just refactor so the view builds from a collection but there are other data points where that wouldn't be the best option. Unless there's something I'm missing, the generic argument from string thing kinda makes sense. It's a fringe case... and kinda just curious if it can be done well enough.
Generally strong typsisation is your friend. Compile time type checks are a feature, not a enemy to be fought. Without them or with too agressive casting, we get the JavaScript and PHP examples from this comic.
For work with weakly typed langauges or WebServices, .NET has the ExpandoObject. The data can be stored in it, then later transfered into the proper type of instance. Also it looks like your case would fall into JSON deserialisation, wich is a well established code.
Generic is the wrong term. Generics are usually about the type still being known at compile time, so the compile time type checks still work. You are explicitly about the type not being known at compile time, only at runtime. This is very distinct from a generic. Dynamic Types are the proper term afaik. But to not mix it up with the type Dynamic (yes, naming here becomes really confusing).
Reflection is the droid you are looking for. For most purposes, the name of a class or field does not exist at runtime. It is primarily there for you and the compiler to communicate. Now Reflection is the exception. It is all about getting stuff (like instances or property/fields) based on a string representation of their name. The nessesary metadata is baked into the .NET Assemblies, as much as the COM support. But as I support strong typisation, I am not a friend of it.
switch/case statements can usually be replaced with a collection of some sort. Cases are really just a hardcoded way to check a collection of constants. You use the case identifier as the key and whatever else you need for the Value. You can totally use Functions as the value (thanks to delegates). Or the Type type, you then use for the instance creation.
But for your case it sounds like all of this is wrong. Bog standart Inheritance - Inheritance might be the real droid you are looking for. A JSON service would not usually give you different instance in a single collection, unless those instances are related in some way. "SomeClassName" and "AnotherClass" should have another ancestor. Or in fact, they should even be just one class - QueryType is simply a string field of said class.
Assuming that you have a way to map strings to Type objects, yes: you can use MethodInfo.MakeGenericMethod():
var totalCount = (int) (
GetType()
.GetMethod("GetTotalCount")
.MakeGenericMethod(MapStringToType(s.QueryType))
.Invoke(this, null)
);
This assumes the presence of a method Type MapStringToType(string) in the local scope.
One way to map types would be to use a Dictionary<string, Type> and fill it with the allowed types and their respective names that will be used in the JSON data to refer to them.
In Sitecore, how can I define index field names on derived SearchResultItem class' properties other than [IndexField] attribute?
I'm trying to use the same interfaces I use for Glass.Mapper model definitions, and it already contains [SitecoreField] attribute on properties which define the Sitecore field name (and therefore the index field name).
Thank you!
I would check out this project:
https://github.com/cardinal252/Lucinq.Sitecore
It links Glass.Mapper and the Lucene index.
Mike
I don't think thats possible, since 'IndexField' and 'SitecoreField' represent different things, 'IndexField' attribute sets the name of the index field, for example, 'IndexField("_id")', 'IndexField("_language")' represent the id and language fields names inside lucene document.
Now, Sitecore by default, store the index names of all fields by it's name in Sitecore, for example, a field called 'Content' will be stored inside lucene document as 'content' by default. which you can change to something else if you want.
SitecoreField attribute represent the actual name of a field for an item, so that glass can map that field value into the property.
Bottom line, you just need to specify IndexField and SitecoreField on each property in your class, since each attribute works differently
I believe you could change Sitecore Glass Mapper SitecoreFieldAttribute to accomplish that.
You could implement the interface IIndexFieldNameFormatterAttribute on the Glass MapperSitecoreFieldAttribute.
This interface you will find in the Sitecore.ContentSearch.Linq.dll and it looks like that:
namespace Sitecore.ContentSearch
{
public interface IIndexFieldNameFormatterAttribute : _Attribute
{
string GetIndexFieldName(string fieldName);
string GetTypeFieldName(string fieldName);
}
}
Your implementation would be that, I pasted only the interface methods here.
namespace Glass.Sitecore.Mapper.Configuration.Attributes
{
/// <summary>
/// Used to populate the property with data from a Sitecore field
/// </summary>
public class SitecoreFieldAttribute: AbstractSitecorePropertyAttribute, IIndexFieldNameFormatterAttribute
{
public string GetIndexFieldName(string fieldName)
{
return this.FieldName;
}
public string GetTypeFieldName(string fieldName)
{
return fieldName;
}
I haven`t test it, but as I could see Sitecore Linq rely on this interface to find the fields names. You can investigate it yourself, but here is the piece of code which made me deduce that:
var variable = (from p in (IEnumerable<PropertyInfo>)typeof(TItem).GetProperties()
select new { Property = p, Attribute = (IIndexFieldNameFormatterAttribute)p.GetCustomAttributes().FirstOrDefault<Attribute>((Attribute a) => a is IIndexFieldNameFormatterAttribute) }).FirstOrDefault((p) => p.Attribute != null);
if (variable != null && variable.Attribute.GetIndexFieldName(variable.Property.Name) == this.FieldName)
{
property = variable.Property;
}
Hope it helps..
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; }
}