Extend System.Web.HttpContext.User - asp.net

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.

Related

Webforms Autofac parameter to constructor using VB.NET

So, i want to do what I feel should be such a simple task... pass in a parameter to a constructor using Autofac!
However, I have managed to get a work around working, but just dont think this is correct, I feel i am chaning too much of the recommended code from the Autofac guides
I am more than happy for answers in C# or VB.net it doesnt matter, the location of code is all the same
So, here is my setup (im not fussed about neatness, just trying to get this to work for now)
In my global.asax I have:
'***Note, the autofac guide had this a a private shared, see below for why i changed it***
' Provider that holds the application container.
Public Shared _containerProvider As IContainerProvider
' Instance property that will be used by Autofac HttpModules
' to resolve And inject dependencies.
Public ReadOnly Property ContainerProvider As IContainerProvider Implements IContainerProviderAccessor.ContainerProvider
Get
Return _containerProvider
End Get
End Property
then within my global.asax within application_start I have:
***again, note, originally I was using IMyClass when registering type... not sure this or that is correct***
Dim builder As New ContainerBuilder()
builder.RegisterType(Of MyClass)().As(Of MyClass)().InstancePerLifetimeScope()
'... continue registering dependencies...
' Once you're done registering things, set the container
' provider up with your registrations.
_containerProvider = New ContainerProvider(builder.Build())
As you can see, origianly the _containerProvider was just public, but I have had to make it "Shared" for this to work, this feels wrong right away!
so, now, in my webForm1.aspx.vb I have this:
Public Property MyClass2 As IMyClass
Private _myClass As IMyClass
Now, because I have adjusted the global to "registerType" to use the actual object, not the interface (which, again seems wrong having to change that too), means now my webform public property is not being set (but, because of my work around, i dont need that anyway)
Also, note the private _myClass... this is for my workaround
so, now in my Webform init method, i have the following:
WebForm1.aspx.vb*
_myClass = [Global]._containerProvider.RequestLifetime.Resolve(Of MyClass)(New TypedParameter(GetType(HttpRequest), Request))
which now instantiates my _myClass with the parameter correctly injected in... this is great, whoopadeedoo
...but... I dont think this is correct.
Now, when I didnt need to pass in a parameter to the construtor, it all worked nice, didnt need to change any of the code from the autofac guide, it just worked, set the public property on my webform.aspx page fine, was really nice.
But, as soon as I start to work with a paramter being passed into the construtor, it seems everything needs to be tweaked so it will work? is this correct?
I have even tried the deligate guide from autofac, but that also doesnt work for me at all by doing this within my webForm.aspx page:
Dim container As ILifetimeScope = [Global]._containerProvider.RequestLifetime
Dim myClassFactory As MyClass = container.Resolve(Of MyClass.Factory)
Dim myClassholding As MyClass = myClassFactory.Invoke("ABC")
even tried without the "Invoke", but "cannot be indexed because it has no default property"
Just incase it helps, here is "myClass"
Private _myID as integer
Public Delegate Sub Factory(myID As integer)
Sub New()
End Sub
Sub New(myID As integer)
_myID = myID
End Sub
Public Sub DoSomething() Implements IDfCookieManager.DoSomething
'do something with myID
End Sub
I know I can pass the id in as a parameter to DoSomething, but i want to understand how to pass this into the constructor
so, my questions:
If this is not how to do this (which I am hoping its not correct), how would I do this without needing to change all the global setup??
Is it best to use a deligate factory or just resolve?
do I really need to change the global container to be shared/static, so that i can access the container from within my code?
So, there are two ways, but firstly, shouldnt need to mess around with how Autofac suggests setting up the ContainerProvider in global.asax... i can keep it as non shared (not static), and to access this value I do the following:
Dim cpa As IContainerProviderAccessor = (CType(HttpContext.Current.ApplicationInstance, IContainerProviderAccessor))
Dim scope As ILifetimeScope = cpa.ContainerProvider.RequestLifetime
Also, in our webform.aspx page, Public Property MyClass As IMyClass should not be added when we need to pass in parameters to the constructor when resolving (otherwise it will be resolved before we try to manually resolve it!
1: Passing in using TypedParameter
Here is my adjusted code to pass in the parameters using resolve (including the lines above):
Dim cpa As IContainerProviderAccessor = (CType(HttpContext.Current.ApplicationInstance, IContainerProviderAccessor))
Dim scope As ILifetimeScope = cpa.ContainerProvider.RequestLifetime
Dim myClass As MyClass = scope.Resolve(Of IMyClass)(New TypedParameter(GetType(Integer), 123))
Also, having the public property at the top of my WebForm1.aspx needed to be removed, because that will auto resolve, meaning, if i try to "resolve" the object manually, it has already been automatically resolved by autofac (which is why i thought my code wasnt working initially), and has already instantiated the object with the empty constructor!
2: Using a Deligate Factory
the line Public Delegate Sub Factory(myID As integer) isnt correct, it should use a function for Autofac to automaticly set this up! so should be: Public Delegate Function Factory(myID As integer) as MyClass
In Global.asax, I just need to add this builder.RegisterType(Of MyClass)().InstancePerLifetimeScope(), because we require a parameter and using a factory, we cant append the .As(Of IMyClass)
Finally, our webform1.aspx.vb just needs this:
Dim cpa As IContainerProviderAccessor = (CType(HttpContext.Current.ApplicationInstance, IContainerProviderAccessor))
Dim scope As ILifetimeScope = cpa.ContainerProvider.RequestLifetime
Dim myClassFactory As MyClass.Factory = scope.Resolve(Of MyClass.Factory)
_myClass = myClassFactory.Invoke(123)
however, I tweaked that slightly to this:
Dim cpa As IContainerProviderAccessor = (CType(HttpContext.Current.ApplicationInstance, IContainerProviderAccessor))
Dim scope As ILifetimeScope = cpa.ContainerProvider.RequestLifetime
Dim myClass As MyClass = scope.Resolve(Of MyClass.Factory).Invoke(123)

EF inherited object insert failed because of mapping

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() {};
}

Newbie to using Classes Properly - How to Set a Property or Class Value to return of a Function

I have been building various web based programs and things for a while, but am quite new to .NET and doing things "properly." As I am completely self taught, with help from sites like this and so on, my understanding of fundamentals is limited.
So, I have a series of functions which return data that I want depending on parameters put in, this is very basic stuff, and obviously all works. However, I am trying to make it easier to call these functions by using Classes.
So, say I have a function which returns a populated DropDownList converted to an HTML string
Function GetList(ListRequired as String) as String
' Do stuff to return a dropdownlist whos contend is determined by `ListRequired`, converted to an HTML string
End Function
In this example, it works fine, but to use it I must know what to enter for "ListRequired" to get what I want.
So, let's say, the options for the ListRequired para are "mastercategory", "brandlist", "priceranges" to return a different set of lists - each option would send the code off the retrieve information from a database and return accordingly.
Suppose I want a third party developer to be able to call this function with the most basic amount of "instruction" required, and not even have to tell him the list of available ListRequired by making it available as a Class.
Public Class DropDownLists
Public Property MasterCategory
Public Property BrandList
Sub New()
Me.MasterCategory = HTMLControls.RenderSearchFilters("mastercategory")
Me.BrandList = HTMLControls.RenderSearchFilters("brandList")
End Sub
End Class
A developer can then call this from Visual Studio/VWD etc very simply:
Dim dd As New DropDownLists
Dim list1Html as String = dd.MasterCategory
Dim list2Html as String = dd.BrandList
Because VWD etc creates all the handy helpers and displays which properties the Class exposes, it is very easy to use this code without have to constantly refer to a manual.
However... when creating a new instance of the Class:
Dim dd As New DropDownLists
This will cause the server to process all the functions within the class which create the Properties, which would be desperately inefficient if there are lots of properties.
So I have tried using my own interpretation of the logic and written this:
Public Class DropDownLists
Shared Property Master
Shared Property Brand
Sub New()
End Sub
Public Class MasterCategory
Sub New()
DropDownLists.Master = HTMLControls.RenderSearchFilters("mastercategory")
End Sub
End Class
Public Class BrandList
Sub New()
DropDownLists.Brand = HTMLControls.RenderSearchFilters("brandList")
End Sub
End Class
End Class
Hoping I'd be able to create the HTML for a Master Category drop down like:
Dim dd as New DropDownLists.MasterCategory
But that doesn't work, and upon reflection I think I can see why... it's not returning the string, but creating a new type.
So... my question is...
What is the correct way to achieve what I am looking for, which is, to be able to create these string outputs by typing
Dim dd As New DropDownLists
Dim list1Html as String = dd.MasterCategory
Dim list2Html as String = dd.BrandList
Without having to pass potentially unknown string parameters, or causing ALL properties to be created each time the DropDownLists Class is created, ie, only run the code for the output I need.
I'm expanding my comment to give you a clearer idea of what I meant:
Public Class DropDownLists
Enum ListType
Undefined
MasterCategory
Brandlist
End Enum
Public Shared Function GetList(ListRequired As ListType) As String
Select Case ListRequired
Case ListType.Brandlist
Return . . .
Case ListType.MasterCategory
Return . . .
Case ListType.Undefined
Throw New . . . .
End Select
End Function
End Class

How does a class member gets updated automatically in a Sub routine / function even if I pass in ByVal in VB.NET?

Can anyone help explain why the following will change a value in an object's member even if I pass it in ByVal?
Scenario: The name of the person is 'Sab' but when i pass the object in the sub routine, the name changes to 'John'.
I thought object types are passed in by reference only and should not be changed unless forced. Is this a feature in .NET and what is this behavior called?
Sub Main()
Dim p As New Person
p.name = "Sab"
DoSomething(p)
Console.WriteLine(p.name) ' John
Console.Read()
End Sub
Public Sub DoSomething(ByVal p As Person)
p.name = "John"
End Sub
Writing to p.name is not the same as writing to p. ByVal prevents the parameter itself from being modified, e.g.
p = New Person
If you want to prevent the properties of Person from being written to, then you need to re-design the Person class to be immutable instead of mutable. Whether this is an appropriate thing to do depends on how you want your code to behave.
Example:
Public Class Person
' All fields are private
Private _name As String
' All properties are read only
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
' Field values can *only* be set in the constructor
Public Sub New(name As String)
_name = name
End Sub
End Class
An instance of an object is a reference - it's a pointer to the object. If you pass any object by value, you are passing it's reference by value. Effectively, there is no difference in passing an object by value or by reference. .Net creates a new copy of the reference and passes it's value to your method but the new copy of the reference still points to the same object. Some folks say that "all objects are passed by reference" but this is not true, the reference to the object in the called method is NOT the same as the reference in the caller but they point to the same object.
If you really want to pass a copy of the object such that the called method may not modify the originals' properties, then you need to create a copy. See discussions about shallow vs deep copies and be careful to understand references to objects vs simple data types. Do think about your design though. It's rare to actually need to create a copy rather than a new object.

fluent nhibernate Exception error

am trying to implement fluent nhibernate in MVC project...there were no build errors... but when i run the project i get this exception
System.Xml.Schema.XmlSchemaValidationException: The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has incomplete content. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'urn:nhibernate-mapping-2.2'.
have no idea what am doing wrong here... the following is the code for opening session factory...
Private Function CreateSessionFactory() As ISessionFactory
Dim sessionFactoryObject As ISessionFactory
sessionFactoryObject = Fluently.Configure().Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005.ConnectionString("Data Source=.\sqlexpress;Initial Catalog=Designs;User ID=sa;Password=root")).Mappings(Function(x) x.FluentMappings.Add(GetType(DesignMap))).BuildSessionFactory()
Return sessionFactoryObject
End Function
this is really driving me nuts....thanks in advance...:)
update-the mappings
the design table map
Public Class DesignMap
Inherits ClassMap(Of Design)
Public Sub DesignMap()
Table("DesignList")
Id(Function(x) x.DesignId)
Map(Function(x) x.DesignType)
References(Function(x) x.Designer, "DesignerId")
End Sub
End Class
the designer table map
Public Class DesignerMap
Inherits ClassMap(Of Designer)
Public Sub DesignerMap()
Table("DesignerList")
Id(Function(x) x.DesignerId)
Map(Function(x) x.DesignerName)
Map(Function(x) x.DesignerCompany)
HasMany(Function(x) x.DesignersDesigns)
End Sub
End Class
new edit-- the entity property looks like this
Public Overridable Property Name() As String
Get
Return _name
End Get
Protected Set(ByVal value As String)
_name = value
End Set
End Property
am i going the right way..?
I'm not quite sure as the mappings seem ok. I can see one error tough, you have only mapped one of your classes:
.Mappings(Function(x) x.FluentMappings.Add(GetType(DesignMap)))
That should not cause this type of error tough. If you add both your mappings and call the method .ExportTo(#"C:\your\export\path") you will get the actual xml mappings. This way it's easier to see the error. You can do that like this:
.Mappings(Function(x) x.FluentMappings.Add(GetType(DesignMap)).Add(GetType(DesignerMap
).ExportTo(#"C:\your\export\path"))
You can also use the method AddFromAssemblyOf (or some other. There is a few choices) if you don't want to add the mappings one by one.
Try exporting the mappings and see if you can find any error. Or you can post the xml mappings and someone else might find something.
There are several things that can cause this. When using automappings, you will get this if you incorrectly specify the assemblies and namespaces to look in. Other things (more likely in your case) that could cause it, are entity properties that aren't marked as public virtual, having an entity constructor with arguments, but neglecting to make a default constructor, or inheriting your entities from a base class.
I would probably first check to make sure all of your entity properties are "public virtual".
found the problem...the constructor for the map was wrong...it should be like this...
Public Class DesignMap
Inherits ClassMap(Of Design)
Public Sub New()
Table("DesignList")
Id(Function(x) x.DesignId)
Map(Function(x) x.DesignType)
References(Function(x) x.Designer, "DesignerId")
End Sub
End Class
problems of working in both C# and vb.net at the same time i guess..!!
and "Matthew Talbert" was correct...making all the properties Overrideable is important..
thanks guys...:)

Resources