Injecting dependencies in Global.asax does not seem to always work. Sometimes it does, sometimes I get a ContextDisposedException (it seems that the problem occurs when I'm doing a Page.Redirect ??). I'm in an ASP.NET WebForm context.
Here is my code:
public class Global : HttpApplication
{
[Inject]
public UserManager UserManager { get; set; }
private void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
GlobalSecurityContext.SetPrincipal(User);
string path = Request.AppRelativeCurrentExecutionFilePath;
if (path.Contains(".aspx"))
{
// Get the current user
var userData = UserManager.GetByIdWithLogin(User.Identity.Name);
if (userData != null)
{
LoginDataDTO data = userData.LoginData;
if (data.XXX && ...)
{
Response.Redirect(...);
}
}
}
}
}
protected void Session_End(Object sender, EventArgs e)
{
UserManager.Logout();
}
}
In this post How to inject dependencies into the global.asax.cs, Mark Seemann says that one should not use Dependency Injection in global.asax because global.asax IS the Composition Root.
So what is the best way to solve my problem since I don't want to directly call my UserManager because the constructor needs a repository
public UserManager(IGenericRepository repository) : base(repository)
{
}
and the GenericRepository has itself a constructor that needs a IContext
public GenericRepository(IContext context)
{
}
I could probably do new UserManager(new GenericRepository(new MyContext)) but
I will not reuse the same context for the whole request
I need to add a reference on my AccessLayer in the GUI, what I would like to avoid
Just as an information, currently I'm injecting the Context like this:
// Dynamically load the context so that we dont have a direct reference on it!
string contextType = // read type from web.config
if (!String.IsNullOrEmpty(contextType))
{
Type context = Type.GetType(contextType);
Bind<IContext>().To(context).InRequestScope();
}
Any help would be greatly appreciated !
[Edit]:
Changing the UserProperty property like this works:
public UserManager UserManager
{
get { return ServiceLocator.Current.GetInstance<UserManager>(); }
}
In this case you can build your Ninject container, and use it a Service Locator for this particular instance (since you're in the Composition Root - you can't inject anything at this point).
Related
I have a base controller and before every page load I want to get the current user. I originally had a constructor in my BaseController that looked like this
public BaseController(ISystemUserCommand command)
{
_systemUserCommand = command
}
The problem with this then is that every controller that inherits from the BaseController would have to contain the ISystemUserCommand in its constructor, which I don't think would be good.
Instead I tried to create just an instance of the service class (shown below - it's the commented line under var sid...) but I need to pass in user service. How would I pass in the user service here or is this a bad way of doing it?
public abstract class BaseController : Controller
{
public SystemUserViewModel CurrentUser { get; set; }
private readonly ISystemUserCommand _systemUserCommand;
public SystemUserViewModel GetCurrentUser()
{
if (HttpContext == null || HttpContext.User == null) return null;
if (CurrentUser != null) return CurrentUser;
var sid = System.Web.HttpContext.Current.Request.LogonUserIdentity.User.ToString();
//var command = new SystemUserCommand();
CurrentUser = _systemUserCommand.GetUser(sid);
return CurrentUser;
}
public void SetUserInformation(SystemUserViewModel currentUser)
{
ViewBag.UserId = currentUser.SystemUserId;
ViewBag.FullName = string.Format("{0} {1}", currentUser.FirstName, currentUser.LastName);
ViewBag.FirstName = currentUser.FirstName;
ViewBag.LastName = currentUser.LastName;
ViewBag.CurrentUser = currentUser;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentUser = GetCurrentUser();
if (currentUser != null)
{
if (currentUser.IsActive)
{
SetUserInformation(currentUser);
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
base.OnActionExecuting(filterContext);
}
}
public class SystemUserCommand : ISystemUserCommand
{
private readonly ISystemUserBusiness _systemUserBusiness;
public SystemUserCommand(ISystemUserBusiness systemUserBusiness)
{
_systemUserBusiness = systemUserBusiness;
}
...
}
You could use property injection instead of constructor injection, via the base class, eg using unity:
public abstract class BaseController : Controller
{
[Dependency]
public ISystemUserCommand SystemUserCommand { get; set; }
}
This would mean the interface reference is only on the base class.
See here for the full examples.
EDIT, Autofac example:
You don't need property attributes on the dependency,
public abstract class BaseController : Controller
{
public ISystemUserCommand SystemUserCommand { get; set; }
}
Just to register the properites to auto resolve on the autofac builder:
builder.RegisterControllers(typeof(MvcApplication).Assembly).Where(t => t.IsAssignableFrom(typeof(BaseController))).PropertiesAutowired();
See autofac property injection here.
First of all, it does not seem a good idea to have OnActionExecuting override in the controller. You can use filters, that are specially designed for this purpose. And it seems that is the main reason you created the BaseController at all.
Regarding the problem with injecting the system command in all the required service, I would do so, but without inheriting from a base class, since I generally prefer aggregation to inheritance. That would mean that each controller that needs to work with the service will get it.
Another option that I have used few times to abstract some operations is to create a UserSerivce that will provide the required operations to the controllers. It will have ISystemUserCommand and HttpContext injected inside so that all of your controllers won't have to do the job. You can either use HttpContext.Current as static or abstract it away if you need testability.
Moreover I would not recommend property injection since it is more obscure than constructor injection that should be preferred if possible.
You can read more about filters here. Unfortunately if you use filters it's not that easy to inject in filters themselves and mostly done with property injection or ServiceLocator pattern (which is not good usually). It's possible to do better with some amount of voodoo though. I think that SimpleInjector has a lot of examples and tutorials on how to apply DI to filters in MVC, maybe they even have a nuget package now to ahieve that.
I have two assemblies which implement the same interfaces (two different implementations for the same interface). When user logs into web forms application certain variable (flag) is being set to specific value. This variable should be used for loading implementations from one of these assemblies.
When application starts, I have the following code in Global.asax to register or known implementations - I have tried using Autofac and SimpeInjector:
// SimpleInjector
private static void Bootstrap()
{
var container = new Container();
// 2. Configure the container (register)
container.Register<IUserRepository, UserRepository>();
// 3. Store the container for use by Page classes.
Global.container = container;
// 4. Optionally verify the container's configuration.
// Did you know the container can diagnose your configuration?
// For more information, go to: https://bit.ly/YE8OJj.
container.Verify();
VerifyPages(container);
}
// Autofac
protected void Application_Start(object sender, EventArgs e)
{
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Build up your application container and register your dependencies.
var builder = new ContainerBuilder();
builder.RegisterType<UserRepository>().As<IUserRepository>().InstancePerRequest();
// ... continue registering dependencies...
// Once you're done registering things, set the container
// provider up with your registrations.
_containerProvider = new ContainerProvider(builder.Build());
}
Now, after a user logs in, I need to register additional implementations, but from one of the two assemblies. I need to do this from code behind of login.aspx page, I believe.
Now I don't know if I should update the container by registering additional types somehow or if I can override some method of Autofac or SimpleInjector so when it tries to instantiate an implementation for an interface, I can point it to a specific assembly.
How could I implement this behavior? Please advise. Thanks.
What you need is some abstraction that allows you to request some contextual information for the user that allows you to base the decision of on which type to load. For instance:
public interface IUserContext
{
bool IsAdministrator { get; }
}
Here the Administator property is the thing that determines what types to load for the user. How to implement this class of course completely depends on how to store this information. Perhaps you retrieve this information from the database, or from a cookie, the session, what ever.
If you need an implementation that is depending on something that is ASP.NET specific (cookie, sessie, request), you will need to have an Web application specific implementation, such as:
public class AspNetUserContext : IUserContext
{
public bool IsAdministrator
{
get
{
return HttpContext.Current != null &&
(bool?)HttpContext.Current.Session["isadmin"] == true;
}
}
}
You can register this context using Simple Injector as follows:
container.Register<IUserContext, AspNetUserContext>();
Now if you have a certain abstraction where the implementations must differ based on this flag, you can register a factory delegate as follows:
container.Register<AdministratorUserRepository>();
container.Register<NormalUserRepository>();
container.Register<IUserRepository>(() =>
{
container.GetInstance<IUserContext>().IsAdministrator
? container.GetInstance<AdministratorUserRepository>()
: container.GetInstance<NormalUserRepository>();
});
Or better it would be to create some sort of composite proxy type that allows delegating to the actual repository:
public class AdminSelectableUserRepositoryProxy : IUserRepository
{
private readonly AdministratorUserRepository adminRepo;
private readonly NormalUserRepository normalRepo;
private readonly IUserContext userContext;
public AdminSelectableUserRepositoryProxy(
AdministratorUserRepository adminRepo,
NormalUserRepository normalRepo,
IUserContext userContext)
{
this.adminRepo = adminRepo;
this.normalRepo = normalRepo;
this.userContext = userContext;
}
public User GetById(Guid id)
{
return this.Repository.GetById(id);
}
private IUserRepository Repository
{
get
{
return this.userContext.IsAdministrator ? this.adminRepo : this.normalRepo;
}
}
}
Now the registration will can be simplified to the following:
container.Register<AdministratorUserRepository>();
container.Register<NormalUserRepository>();
container.Register<IUserRepository, AdminSelectableUserRepositoryProxy>();
I am working on a multi-tenant app with asp.net mvc. I have to identify tenant for each request so i have created a class below:
public class TenantProvider
{
public static Tenant Tenant
{
get
{
Tenant tenant = HttpContext.Current.Items["Tenant"] as Tenant;
if (tenant == null)
{
var tenantUsername = HelperUtility.GetCurrentRequestHost();
//The below line of code is my problem
TenantRepository tenantRepository = new TenantRepository(new AppointContext());
tenant = tenantRepository.GetByUsername(tenantUsername);
HttpContext.Current.Items.Add("Tenant", tenant);
}
return tenant;
}
}
}
This class static property returning the Tenant for current request. It will first check for the Tenant in cache, if not found than if will get the Tenant from the database, initialize the cache and return the Tenant.
For getting Tenant form the database i am creating a TenantRepository instance. TenantRepository has a dependency over database context, which i am passing it while creating its instance.
Now when i have to do other database operation on current Tenant than i have to create a new Repository instance at some other place and have to pass new Context, so the actual context with which i have extracted the Tenant and the new Context differ which i think may create the problem.
So my question is how can i handle this scenario, so that same context instance will be used ??
The solution you are looking for is the Unit of Work design pattern. From Martin Fowler:
Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
ref: http://martinfowler.com/eaaCatalog/unitOfWork.html
This pattern allows you to enroll multiple transactions into a single context. This is a very common pattern, and here is one possible implementation. First, create a Unit of Work object which will hold a reference to your central context, and which will initialize your repositories with that context (this implementation uses the Entity Framework):
public class UnitOfWork : IUnitOfWork
{
internal EntitiesContext _context = new EntitiesContext ();
private ITenantRepository _tenantRepository;
private IOtherRepository _otherRepository;
public ITenantRepository TenantRepository
{
get
{
if (_tenantRepository== null)
{
_tenantRepository= new TenantRepository(_context);
}
return _tenantRepository;
}
}
public IOtherRepository OtherRepository
{
get
{
if (_otherRepository== null)
{
_otherRepository= new OtherRepository(_context);
}
return _otherRepository;
}
}
public void Save()
{
_context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
Note that if you use any repository with this pattern, they will all use the same context.
Your controller should either initializing the Unit of of Work, or even better, have it injected into its constructor:
public TenantController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_tenantRepository = unitOfWork.TenantRepository;
_otherRepository = unitOfWork.OtherRepository;
}
If you need to use the UnitOfWork to another layer, you would typically pass it as an argument to another object's constructor:
public ActionResult Index()
{
TenantProvider provider = new TenantProvider(_unitOfWork);
_otherRepository.DoWork();
_unitOfWork.Save();
}
Now your TenantProvider can do some work with its respective repository, but the Unit of Work's OtherRepository can also do some work using the same context.
In addition to #ChrisHardie, I want to add some MVC specifics: I think it is a very good practice to inject the Unit of Work into the controllers. In order to do so, you can create a custom ControllerFactory that is derived from DefaultControllerFactory and is registered at application startup:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
// Analyze whether instance of controllerType should be created directly or
// whether the Unit of Work should be injected
if (needsUnitOfWork)
return (IController)Activator.CreateInstance(controllerType, unitOfWork);
else
return (IController)Activator.CreateInstance(controllerType);
}
}
In order to discern between controllers that need a Unit of Work and those that do not, you could use reflection (custom attributes or inspecting constructor arguments). Maybe you can also assume that each controller needs a Unit of Work for the moment.
If you are already using an Inversion of Control container, it can support you in creating the instances (IoC containers are usually plugged in in a ControllerFactory in MVC). If not, you might consider to start using one.
You can register the ControllerFactory like this:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// ...
RegisterCustomControllerFactory ();
// ...
}
private void RegisterCustomControllerFactory ()
{
IControllerFactory factory = new CustomControllerFactory();
ControllerBuilder.Current.SetControllerFactory(factory);
}
}
As this answers builds upon the extension points for Dependency Injection in MVC, this link might help.
I decided to clean this post up and I posted a sample project at ge.tt/3EwoZEd/v/0?c
Spent around 30 hours on this already and still can't figure it out... help would be really appreciated!
I have an ASP.NET Web API solution that uses this code: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/ to implement "Basic HTTP authentication in ASP.NET Web API using Message Handlers". I'm new to IoC/DI and I'm trying to get this to work with Castle Windsor.
I've been trying a lot of different things but I get 1 of the following errors depending on what I did wrong:
"Looks like you forgot to register the http module Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule"
"Object reference not set to an instance of an object." for the PrincipalProvider in BasicAuthMessageHandler
"No component for supporting the service *.DummyPrincipalProvider was found"
Below is my code:
Global.asax.cs:
private static IWindsorContainer _container;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var config = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
IncludeErrorDetailPolicy errorDetailPolicy;
switch (config.Mode)
{
case CustomErrorsMode.RemoteOnly:
errorDetailPolicy
= IncludeErrorDetailPolicy.LocalOnly;
break;
case CustomErrorsMode.On:
errorDetailPolicy
= IncludeErrorDetailPolicy.Never;
break;
case CustomErrorsMode.Off:
errorDetailPolicy
= IncludeErrorDetailPolicy.Always;
break;
default:
throw new ArgumentOutOfRangeException();
}
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = errorDetailPolicy;
ConfigureWindsor(GlobalConfiguration.Configuration);
GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler()
{
PrincipalProvider = _container.Resolve<IProvidePrincipal>()
});
}
public static void ConfigureWindsor(HttpConfiguration configuration)
{
// Create / Initialize the container
_container = new WindsorContainer();
// Find our IWindsorInstallers from this Assembly and optionally from our DI assembly which is in abother project.
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
//Documentation http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx
// Set the WebAPI DependencyResolver to our new WindsorDependencyResolver
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
}
Windsor Installer:
public class PrincipalsInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<DelegatingHandler>());
container.Register(
Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
);
}
}
Modified DummyPrincipalProvider (from the original I got from the URL above):
public class DummyPrincipalProvider : IProvidePrincipal
{
private IUserRepository _userRepo;
public DummyPrincipalProvider(IUserRepository userRepo)
{
this._userRepo = userRepo;
}
public IPrincipal CreatePrincipal(string username, string password)
{
try
{
if (!this._userRepo.ValidateUser(username, password))
{
return null;
}
else
{
var identity = new GenericIdentity(username);
IPrincipal principal = new GenericPrincipal(identity, new[] { "User" });
if (!identity.IsAuthenticated)
{
throw new ApplicationException("Unauthorized");
}
return principal;
}
}
catch
{
return null;
}
}
}
WindsorDependencyResolver.cs:
internal sealed class WindsorDependencyResolver : IDependencyResolver
{
private readonly IWindsorContainer _container;
public WindsorDependencyResolver(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
}
public object GetService(Type t)
{
return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
}
public IEnumerable<object> GetServices(Type t)
{
return _container.ResolveAll(t).Cast<object>().ToArray();
}
public IDependencyScope BeginScope()
{
return new WindsorDependencyScope(_container);
}
public void Dispose()
{
}
}
WindsorDependencyScope.cs:
internal sealed class WindsorDependencyScope : IDependencyScope
{
private readonly IWindsorContainer _container;
private readonly IDisposable _scope;
public WindsorDependencyScope(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
_scope = container.BeginScope();
}
public object GetService(Type t)
{
return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
}
public IEnumerable<object> GetServices(Type t)
{
return _container.ResolveAll(t).Cast<object>().ToArray();
}
public void Dispose()
{
_scope.Dispose();
}
}
I assume IProvidePrincipal is your own implementation.
Best way, the only one IMHO, to use an IoC container is the Composition Root.
The entry point/composition root for web api has been well explained by ploeh's blog.
DelegatingHandler are not part of the "request resolving", so you may choose to resolve it within global asax Application_start where the container lives as private variable.
GlobalConfiguration.Configuration.MessageHandlers.Add(container.Resolve<BasicAuthMessageHandler>());
If you properly registered your handler and all its dependencies in the container, nothing else has to be done: handler instance you extracted from the container and added among MessageHandlers will have an IPrincipalProvider and (I)UserRepository. Keep in mind BasicAuthMessageHandler will act a singleton, so if you need a new instance of (I)UserRepository on each method call... you may consider TypedFactory to create your (I)UserRepository as late dependencies
Of course, any component starting from you top graph component have to be register in the container.
That's the easy way... in case you need somenthing more sophisticate, you may end up creating your "composition root" for DelegatingHandlers as well.
BTW: never, ever, doing somenthing like
UserRepository userRepo = new UserRepository();
or PrincipalProvider = new DummyPrincipalProvider()
none of the "Behaviour instance" should be created explicitly: container take care of providing right instance at the right time...
As per Jon Edit:
now DummyPrincipalProvider looks fine: just keep in mind since DummyPrincipalProvider is created among the message handler(act as singleton due to global registration), you are reusing always same instance.
Instead of your plumbing
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
I rather use ploeh implementation(see above).
Your registration
container.Register(
Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
.UsingFactoryMethod(kernel => kernel.Resolve<DummyPrincipalProvider>())
);
should be replaced with
container.Register(
Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
);
that's wrong... container has to resolve it, not you explicitly
GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler());
stick with my configuration as above: BasicAuthMessageHandler resolved via container.
Let me know if it works.
PS: You registered the TypedFactory facility in the container, but you are not using it... just to let you know.
You registered DelegatingHandler(s) as Transient, but keep in mind they gonna be "singleton" by design: adding it to the MessageHandlers collection imply they gonna be reused on each request.
As per Jon Edit 2:
I added a sample on github. You should be able to build it and run it using NuGet Package Restore
Your issue about PerWebRequestdepends on the depencies of UserRepository on the NHibernate factory session creating session "PerWebRequest": you cannot resolve IPrincipalProvider->IUserRepository->ISession in Application_Start due to HttpContext. If you really need a IUserRepositry working w/ IPrincipalProvider dependency has to be to a IUserRepositoryFactory(TypedFactory) instead.
I tried to fix your sample using the typed factory and it works, but than I had an issue w/ NHibernate configuration and since I'm not an expert of that, I didn't go any further.
If you need help w/ the factory thing... LMK and I'll update my git sample using a factory within the DummyPrincipalProvider
I try to access my repository from a class for RSS service. So I used dependency injection for that.
Here is what I do in the NinjectControllerFactory:
ninjectKernel.Bind<IPostRepositoryFactory>().To<NinjectPostRepositoryFactory>().InSingletonScope();
Here is my IPostRepositoryFactory class:
public interface IPostRepositoryFactory
{
IPostRepository GetRepository();
}
public class NinjectPostRepositoryFactory : IPostRepositoryFactory
{
private readonly IKernel kernel;
public NinjectPostRepositoryFactory(IKernel kernel)
{
if (kernel == null)
throw new ArgumentNullException("kernel");
this.kernel = kernel;
}
public IPostRepository GetRepository()
{
return kernel.Get<IPostRepository>();
}
}
Here is the call from my controller:
public ActionResult Feed(int? page = 1)
{
var mgr = new SyndicationManager();
return mgr.GetFeedResult(page);
}
Here is the SyndicationManager class:
public class SyndicationManager
{
[Inject]
public IPostRepositoryFactory m_PostRepositoryFactory { get; set; }
private SiteConfiguration m_SiteConfiguration;
public SyndicationManager()
{
m_SiteConfiguration = SiteManager.CurrentConfiguration;
}
public ActionResult GetFeedResult(int? page = 1)
{
IPostRepository repository = m_PostRepositoryFactory.GetRepository();
var feed = new SyndicationFeed(m_SiteConfiguration.Name,
m_SiteConfiguration.Description,
new Uri(SiteManager.GetBaseUrl()));
So I started debugging from my Feed action controller. Then accessing GetFeedResult and there is the problem: error is that my m_PostRepositoryFactory is always null.
Any help is greatly appreciated.
Why aren't you using constructor injection? It makes it easier to find dependencies of your classes and all resolves will fail directly if any of the dependencies are missing.
Just use the ninject.mvc3 nuget package to configure ASP.NET MVC to use dependency injection for the controllers.
And I don't see the use of a singleton factory to find the repository? Why can't you resolve the repository interface directly?
#Jehof is correct. ninject will not resolve any dependencies when you create objects yourself. But as I said: Don't use the kernel directly, configure MVC3+NInject correctly instead.
http://nuget.org/packages/Ninject.MVC3/2.2.2.0
I´m not an expert on Ninject, but i use Unity as DependencyInjection-Framework. I think the problem is that you instantiate SyndicationManager using its default constructor.
You need to get a reference to the SyndicationManager by resolving it from the Ninject-Kernel, otherwise the dependencies won´t be injected.
public ActionResult Feed(int? page = 1)
{
IKernel kernel = // get the kernel from somewhere
var mgr = kernel.Get<SyndicationManager>();
return mgr.GetFeedResult(page);
}