I'm looking for a way to write a custom .net class that would allow for nested methods.
For example... say I have a class X with a function Y that returns a list. Then I have another function that returns a sorted list...
I would like to be able to do something like x.y().z() where z would accept the output of y() as its input.
Basically how .toLower() or .toUpper() can be tacked on to any string.
I'm trying to google it but I'm not even sure if I'm asking the right question.
Thanks
Extension methods might be what you are looking for (can accept the output of y()), but that depends on the version of .NET you are using.
so if you wanted to create an extension method called x that takes y as a parameter, you would create a method:
public static object z(input as y)
{
//do your manipulations here
}
so if you wanted your function to do sorting, you would call your sort method, pass the object, y, and return the object sorted.
There's nothing magic which needs to happen. If class A has a method which returns an object of class B, then you can call methods on the function in A directly.
Given:
public static class MyClass
{
public MyList getList()
{
MyList retVal = new MyList();
...
return retVal;
}
}
public class MyList
{
public MyList sort()
{
// Sort list
...
return sortedList;
}
}
then this is legal:
MyList list = MyClass.getList().sort();
In asp.net vb you can use a Module instead of a Class like this:
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension()> _
Public Function extendedMethod(ByRef input As String) As String
Return input & "extended method"
End Function
End Module
Then in your code behind you import it the same as you would any class:
Imports Extensions
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim st As String = "a string "
Response.Write(st.ToUpper.extendedMethod)
End Sub
End Class
In this case you can use the "extendedMethod" method from the module on any string value in the same way you would use .toUpper() or .toLower()
Steve is right (no pun intended): Extension methods are what you're asking for. In C# you'd do something like this:
namespace ExtensionsNamespace; // Name this whatever you want.
public static class ListExtensions // must be public static!
{
// must be public static and the first parameter needs a "this"
public static IList<T> ToOrderedList<T>(this IList<T> originalList, IComparer<T> comparer)
{
// Code to take the original list and return an ordered version
}
}
And then in your code:
using ExtensionsNamespace;
...
IComparer<Book> comparer = GetBookComparer();
IList<BooK> books = GetBookList().ToOrderedList(comparer);
There are some additional things you can do using lambda expressions to avoid the need to write your own comparer class in certain cases, and so forth. However, before you go reinventing the wheel I'd suggest you look at LINQ to Objects, which already has a lot of these functionalities built in. For example:
using System.Linq;
...
IEnumerable<Book> booksInOrder1 = GetBookList().OrderBy(b => b.Title);
Does that answer your question?
Related
I have a VB class. It's big, so I'm posting a simplified example here:
Public Class ExampleClass
Public Property Foo As String
Public Property Bar As String
End Class
Suppose I have a text file like this:
foo,123
bar,456
And I want to read the file and populate the properties of my object accordingly.
Simplest way I can think of is to add a method with a case statement, like this:
Public Class ExampleClass
Public Property Foo As String
Public Property Bar As String
Public Sub SetProperty(prop As String, val As String)
Select Case prop
Case "Foo"
Foo = val
Case "Bar"
Bar = val
End Select
End Sub
End Class
But as my class will have around a hundred properties, so my SetProperty procedure is going to get big and boring to put together, and probably not be the most maintainable. Is there a better way to do this?
Before you tell me, I shouldn't structure my data this way, I know. However, this is a legacy database table that I have to live with. I'm updating the application with some new Entity Framework and LINQ code that will allow me to drop some very ugly stored procedures.
Thanks for your help.
James
You can find each object property via reflection
MyClass myClass = new MyClass();
System.Reflection.PropertyInfo pi = myClass.GetType().GetProperty("Foo");
if (pi != null && pi.CanWrite)
{
//Check the type before assignment...
if (pi.PropertyType.IsAssignableFrom(typeof(string)))
pi.SetValue(myClass, value, null);
}
I created a simple solution with an EDMX file that possess one table Sport with 2 field IdSport and Label. I would like to insert a record in DB with an object inherited of the Sport object created by EF.
Public Class Index
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim aSport As New TestSport()
Using ctx As New FormationEntities
ctx.AddObject("Sport", aSport)
ctx.SaveChanges()
End Using
End Sub
End Class
Public Class TestSport
Inherits Sport
End Class
With an Sport object it work but not with TestSport. I need the inherited class for adding some properties and others functionnalities, but when I save it, I would like to save only the property possessed by the parent object Sport.
Error message:
Mapping and metadata information could not be found for EntityType
I know that the usual way is to use partial class but on my project, the EDMX file is in another project, so the only solution I see is to use an inherited class.
What am I doing wrong? How to fix my problem? Is it exist a better way to do it?
Thanks.
On searching through gooogle I found the following link, where a very similar scenario is discussed:
Deriving from classes generated by Entity Framework in C#
Although there is one post marked as answer, but the second answer is equally relevant.
Hope this helps.
Entity Framework appears to use a kind of reflection during the saving of your entities, and is probably why your inheritances do not work. One way you could still add functionality to your enties(albeit only functions) is using Extension methods: http://msdn.microsoft.com/en-us//library/bb383977.aspx
But if it is more than just some functions you need to add, consider a more structural solution. Having part of your object in a data layer and part of that same object in an upper layer is not a good separation of responsibilities.
Instead of having part of the class in your data project(I assume), and part of it in another project, consider creating one 'Logics class' in your project which wraps around your entity and adds functionality that way. You could for example do this by exposing the entity directly:
public class SportLogic
{
private Sport _sport;
public Sport Sport { get { return _sport; } }
public string SomeNewProperty { get; set; }
public void DoStuff() {};
}
Or the way I use where the logics object is acting as an actual logical wrapper around the entity. It is cleaner because it obfuscates the entity entirely: any calling code wil not need knowledge of your entity(Sport) object.
public class SportLogic
{
private Sport _sport;
public string SportProperty { get { return _sport.SportProperty; } set { _sport.SportProperty = value; } }
public string SomeNewProperty { get; set; }
public void DoStuff() {};
}
I asked this question several weeks ago and received some good answers: ASP.NET Class Library best practice. I now have another question.
The problem I have is that I have inherited an ASP.NET application, which contains lots of classes with tight coupling and low cohesion, which is not ideal. I want to share some of the code with other apps. Most of the code exists in one class, however the class references other classes and those classes reference other classes etc. Is there any way of sharing the code in one class (which references other classes)? The only way I can think of doing this is using web services, but there is sensitive information.
The only good option, in cases like this, is refactoring the code. You don't have to change the existing class interface, however. You can create multiple new classes that are designed properly and replace the logic in the original poorly designed class. Then you can refactor the original class to use the new classes internally to perform the functionality. You don't have to do this all at once. As you find that you need a particular bit of logic in a shared library, just refactor that logic and leave the rest untouched. Over time you can, in this way, refactor the whole thing. Unless, of course, it's not that big or you have all the time in the world to refactor the beast. However, usually that's not the case.
For instance, let's say you have the following overly simplified classes:
Public Class OriginalBeast
Private _dependency As New Dependency()
Public Function Method1() As Integer
Return _dependency.Calculate(2)
End Sub
Public Function Method2() As Integer
Return _dependency.Calculate(2)
End Sub
' ...
Public Function Method1027() As Integer
Return _dependency.Calculate(1027)
End Sub
End Class
Public Class Dependency
Public Function Calculate(value As Integer) As Integer
Return value * 2
End Function
End Class
And you want to share the logic in OriginalBeast.Method2 in a class library, you would need to move the Dependency class to the class library (and likely need to partially refactor it as well). Then you would need to create a new class that contains just the desired methods from the original beast:
Public Interface INice
Function Method2() As Integer
End Interface
Public Class Nice
Implements INice
Public Sub New(dependency As IDependency)
_dependency = dependency
End Sub
Private _dependency As IDependency
Public Function Method2() As Integer Implements INice.Method2
Return _dependency.Calculate(2)
End Function
End Class
Public Interface IDependency
Function Calculate(value As Integer) As Integer
End Interface
Public Class Dependency
Implements IDependency
Public Function Calculate(value As Integer) As Integer Implements IDependency.Calculate
Return value * 2
End Function
End Class
Then, you would need to refactor the original beast to use the class library instead of doing the logic itself:
Public Class OriginalBeast
Public Sub New()
_dependency = New Dependency()
_nice = New Nice(_dependency)
End Sub
Private _dependency As IDependency
Private _nice As INice
Public Function Method1() As Integer
Return _dependency.Calculate(2)
End Sub
Public Function Method2() As Integer
Return _nice.Method2()
End Sub
' ...
Public Function Method1027() As Integer
Return _dependency.Calculate(1027)
End Sub
End Class
Obviously real-world beasts are never that simple and it will likely require a lot of work to refactor even a small part of it, but hopefully that gives you an idea of what I'm talking about.
In the following I am trying to define a Private variable on Class level called _p. The HTTP.POST for Index will bring a User provided value which I'll set this private variable with. In the second Method called ListOfVehicles, I'll be accessing this variable.
Now everything is alright theoretically, however when I try to access this private variable I don't get anything, this is found Nothing.
Public Class QuotationController
Inherits System.Web.Mvc.Controller
'Private Variables
Dim _p As String
'Get Basic pickup and dropoff details
Function Index() As ActionResult
Return View()
End Function
'Function to get basic details out of the view
'and to redirect to ListOfVehicles
<HttpPost()>
Function Index(ByVal P As String, ByVal D As String) As ActionResult
_p = P
Return RedirectToAction("ListOfVehicles")
End Function
'Show list of vehicels
Function ListofVehicles() As ActionResult
ViewData("UserChoice") = "Pickup: " & _p
vehicleList = QB.GetQuotation(_p, _d)
Return View(vehicleList)
End Function
End Class
That is fundamentally impossible.
Each HTTP request gets a separate controller instance; they don't share anything.
You should use cookies, session, application state, or cache, as appropriate.
In your case, you should probably include that variable in a POST to the other action from a <form>.
If you don't want to add a formal post parameter you can use
TempData.Add("P", P);
just before the return statement, in your ListOfVeicles you can accesso via
string p = TempData["P"];
Temp data is valid just within the request scope
EDIT: sorry for C# syntax, I'm not using VB since the good old days ov VB 6
I would like to extend the System.Web.HttpContext.User object (ASP.NET/VB.NET) so that it contains other fields besides just Name. I understand I can create an object that inherits the System.Security.Principal.GenericPrincipal class, but how do I store that in the Current.User object in a usable fashion. ie, I can do something like Current.User.UserID.
So far to achieve this I've created a kludgy workaround by using | delimited strings in the User.Name property and then splitting them, but it's getting kind of ridiculous.
Any suggestions?
Thanks!
EDIT: I have tried the following to no avail:
Imports System.Security.Principal
Public Class CurrentUser : Inherits GenericPrincipal
Private _totalpoints As Integer
Private _sentencecount As Integer
Private _probationuntil As DateTime
Public ReadOnly Property TotalPoints() As Integer
Get
Return _totalpoints
End Get
End Property
Public ReadOnly Property SentenceCount() As Integer
Get
Return _sentencecount
End Get
End Property
Public ReadOnly Property ProbationUntil() As DateTime
Get
Return _probationuntil
End Get
End Property
Public Sub New(ByVal principle As IIdentity, ByVal roles() As String, _
ByVal points As Integer, ByVal sentences As Integer, ByVal probationTil As DateTime)
MyBase.New(principle, roles)
_totalpoints = points
_sentencecount = sentences
_probationuntil = FixDBNull(probationTil)
End Sub
End Class
setting the object in my Global.asax Application_AuthenticateRequest function like so:
HttpContext.Current.User = New CurrentUser(User, userRoles, _
points, sentenceCount, probationUntil)
with a direct cast wherever the object is needed like so:
Dim thisUser As CurrentUser = DirectCast(Current.User, CurrentUser)
i also tried CType and it didn't work... my error is
[InvalidCastException: Unable to cast object of type 'System.Security.Principal.GenericPrincipal' to type 'myProject.CurrentUser'.]
i'm losing my mind here ... :( thanks guys...
anyone?
You can create your own Principal class with the required properties, that inherits from a Generic Principal, and then set the User property of your Current Context to be the a user of that type.
The example below is for ASP.Net MVC but a similar approach could be used with webforms.
You can do this in the PostAuthenticateRequest after a user is authenticated (in the Global.asax)
private void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
SomePrincipal newUser = new SomePrincipal(User.Identity, tmpRoles);
senderRef.Context.User = newUser;
System.Threading.Thread.CurrentPrincipal = newUser;
}
You could then add a property or method in a base class of your page (or controller) for example that to wrap and type the Context.User principal to your Principal type and make sure you call it rather than calling the one on the HttpContext.
There are probably other solutions too!
Would this approach work for you? It looks a little involved but it really doesn't take too long to setup:
Create a 'base' class of your own, and have your pages inherit from that. For example, create a base class called 'BasePage' which inherits from System.Web.UI.Page.
Have your ASP.net pages inherit from your new BasePage class.
In the BasePage class, you can have a public property which contains the extra fields you want to store for your user (eg. BasePage.FirstName, BasePage.LastName). Better still, create a User object containing the extra fields, and expose that via BasePage, eg. "BasePage.Customer". This keeps things tidy if you plan to extend BasePage later.
You can then override the OnInit() of the base class to check for HTTPContext.Current.User.Name property, and fetch the necessary info from your DB to initialise your custom properties.
You can modify the code so that it won't need to hit the database each time the page is refreshed by using ControlState to check whether the custom fields have values before populating them again from the database.
Hope this helps...
Richard.