Custom Authorize Attribute additional Param? - asp.net

im looking for a way to customize my Authorize Attribute so that i can implement it correctly with my own MembershipProvider.
What i need is to have the IsInRoles(string role, int perm) for example, in other word, i want to have it replace with a new IsinRoles or maybe create another method to archive this result.
Is it possible? or i need to write a different authorize attribute?
Thank you very much for your concern...
PS: im working on ASP.net MVC, so i need to have the [Authorize] filter up and running.

I think you can just add a public property to your custom AuthorizeAttribute.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// Add the allowed roles to this property.
/// </summary>
public YourCustomRoles RequiredRole;
public int YourCustomValue;
/// <summary>
/// Checks to see if the user is authenticated and has the
/// correct role to access a particular view.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
// Make sure the user is authenticated.
if (httpContext.User.Identity.IsAuthenticated == false) return false;
// Can use your properties if needed and do your checks
bool authorized = DoSomeCustomChecksHere();
return authorized;
}
}
Usage I think would be (haven't tried it though):
[CustomAuthorizeAttribute (RequiredRole=MyCustomRole.Role1 | MyCustomRole.Role2, YourCustomValue=1234)]

Related

Reduce IdentityServer4 access_token length by removing claims in profile service

I am working on IdentityServer4 application. The generated Access_token length is becoming too lengthy due to the claim value added to the context.IssuedClaims. I tried to remove claim type called "entity" and it helps in reducing the access_token length. But its removing the claim from ClaimsPrincipal as well. Is there a way I can add this claim back to my ClaimsPrincipal so that I can access it from all my client applications? Currently I am invoking a separate API to get the claim back every time. Below is the code from my ProfileService where I am filtering the claim.
public async Task GetProfileDataAsync(IdentityServer4.Models.ProfileDataRequestContext context)
{
var user = await _userManager.GetUserAsync(context.Subject);
var principal = await _claimsFactory.CreateAsync(user);
//Retrieve all the claims associated with the user
var claims = from claimsdata in principal.Claims select new System.Security.Claims.Claim(claimsdata.Type, claimsdata.Value);
//Exclude claim type "entity" since its huge in numbers and causing access_token size to grow
var claimsWithoutEntity = claims.Where(x => x.Type != "entity");
context.IssuedClaims.AddRange(claimsWithoutEntity);
var roleClaims = _roleService.GetRoleClaims(user);
context.IssuedClaims.AddRange(roleClaims);
}
A different alternative to reduce the cookie size without modifying the access token is to create a SessionStore, and you can set it using the SessionStore parameter here:
}).AddCookie(options =>
{
...
options.SessionStore = new MySessionStore();
})
What does a SessionStore do?
See this picture, taken from one of my training classes:
Here's a sample in-memory store
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Serilog;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace SessionStore
{
/// <summary>
/// MySessionStore
///
/// Custom session store, to hold the tokens in memory instead of storing them inside the Cookie.
///
/// This provides an abstract storage mechanic to preserve identity information on the server while
/// only sending a simple identifier key to the client. This is most commonly used to mitigate issues
/// with serializing large identities into cookies.
///
/// TODO:
/// - Needs logic to remove older items, otherwise we might run out of memory here.
/// - Use the MemoryCache instead of a dictionary?
///
/// Written by Tore Nestenius to be used in the IdentityServer in production training class.
/// https://www.tn-data.se
///
/// </summary>
internal class MySessionStore : ITicketStore
{
private readonly Serilog.ILogger _logger;
private readonly ConcurrentDictionary<string, AuthenticationTicket> mytickets = new();
public MySessionStore()
{
_logger = Log.Logger;
}
/// <summary>
/// Remove the identity associated with the given key.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task RemoveAsync(string key)
{
_logger.Debug("MySessionStore.RemoveAsync Key=" + key);
if (mytickets.ContainsKey(key))
{
mytickets.TryRemove(key, out _);
}
return Task.FromResult(0);
}
/// <summary>
/// Tells the store that the given identity should be updated.
/// </summary>
/// <param name="key"></param>
/// <param name="ticket"></param>
/// <returns></returns>
public Task RenewAsync(string key, AuthenticationTicket ticket)
{
_logger.Debug("MySessionStore.RenewAsync Key=" + key + ", ticket = " + ticket.AuthenticationScheme);
mytickets[key] = ticket;
return Task.FromResult(false);
}
/// <summary>
/// Retrieves an identity from the store for the given key.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<AuthenticationTicket> RetrieveAsync(string key)
{
_logger.Error("MySessionStore.RetrieveAsync Key=" + key);
if (mytickets.ContainsKey(key))
{
var ticket = mytickets[key];
return Task.FromResult(ticket);
}
else
{
return Task.FromResult((AuthenticationTicket)null!);
}
}
/// <summary>
/// Store the identity ticket and return the associated key.
/// </summary>
/// <param name="ticket"></param>
/// <returns></returns>
public Task<string> StoreAsync(AuthenticationTicket ticket)
{
//Only add one at the time to avoid race conditions
lock(this)
{
//Make sure the key is does not already exist in the dictionary
bool result = false;
string key;
do
{
key = Guid.NewGuid().ToString();
result = mytickets.TryAdd(key, ticket);
} while (result == false);
string username = ticket?.Principal?.Identity?.Name ?? "Unknown";
_logger.Debug("MySessionStore.StoreAsync ticket=" + username + ", key=" + key);
return Task.FromResult(key);
}
}
}
}

Switch between UserControls in View based on property of ViewModel

How can I change between UserControls in the View, based on a property of the ViewModel, in a Windows 8 store app?
Say my ViewModel has a property which looks something like this:
class MyViewModel
{
public string CurrentStatus
{
get { return (string)GetValue(CurrentStatusProperty); }
set { SetValue(CurrentStatusProperty, value); }
}
public static readonly DependencyProperty CurrentStatusProperty =
DependencyProperty.Register("CurrentStatus", typeof(string), typeof(MyViewModel), new PropertyMetadata("default", CurrentStatusChanged));
...
}
How do I make my View change an UserControl according to the value of the CurrentStatus from the ViewModel?
The strightforward solution for me would have been to create a binding between the CurrentStatus from the ViewModel and another string from the View, but apparently data binding can only be used for a DependencyObject (which a string isn't).
Edit:
The xaml file contains just a StackPanel in which I want to put a UserControl, based on the CurrentStatus. So, if the CurrentStatus is "one" I want the StackPanel to contain UserControlOne and so on...
Any ideas or nice solutions to this problem?
Thanks a lot!
I usually use a ContentControl and set it's ContentTemplate based on a DataTrigger
I have an example in my blog post Switching between Views/UserControls using MVVM, but here's a sample of what that may look like using your scenario:
<DataTemplate x:Key="DefaultTemplate">
<local:DefaultUserControl />
</DataTemplate>
<DataTemplate x:Key="ClosedTemplate">
<local:ClosedUserControl />
</DataTemplate>
<Style x:Key="MyContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentStatus}" Value="Closed">
<Setter Property="ContentTemplate" Value="{StaticResource ClosedTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<ContentControl Style="{StaticResource MyContentControlStyle}" />
EDIT
Apparently DataTriggers are not supported in WinRT, and have been replaced with the VisualStateManager. I haven't had the chance to use this yet, however from what I'm reading they used the same approach for WinRT as they did Silverlight (which also didn't support DataTriggers until v5), and my solution for Silverlight was to use a DataTemplateSelector
I hope that can point you in the right direction :)
Not sure I fully understand what you are trying to do. Can you post the xaml?
If what you are wanting is to present the controls differently based on the status, then use a converter and you can present a different template based upon the status:
public class StatusToTemplateConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((string) value)
{
case "Status1":
return Application.Current.Resources["Status1Template"];
case "Status2":
return Application.Current.Resources["Status2Template"];
default:
return Application.Current.Resources["Status3Template"];
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
#endregion
}
the above assumes you have your templates defined in a resource file
If all you ae wanting to do is something simpler such as red text for Status1 or Green text for status2 you could have a converter that just converts the status to a colour and bind the FontColor to the status and use the converter.
But like I said, without more code being posted, I'm not 100% clear on what you are trying to achieve
While I've used the DataTemplateSelector and IValueConverter in such cases before - now my favorite approach is using the VisualStateManager. I have created a most basic attached property that implements the attached behavior pattern in WinRT XAML Toolkit here - that looks like this:
/// <summary>
/// Defines an attached property that controls the visual state of the element based on the value.
/// </summary>
public static class VisualStateExtensions
{
#region State
/// <summary>
/// State Attached Dependency Property
/// </summary>
public static readonly DependencyProperty StateProperty =
DependencyProperty.RegisterAttached(
"State",
typeof(string),
typeof(VisualStateExtensions),
new PropertyMetadata(null, OnStateChanged));
/// <summary>
/// Gets the State property. This dependency property
/// indicates the VisualState that the associated control should be set to.
/// </summary>
public static string GetState(DependencyObject d)
{
return (string)d.GetValue(StateProperty);
}
/// <summary>
/// Sets the State property. This dependency property
/// indicates the VisualState that the associated control should be set to.
/// </summary>
public static void SetState(DependencyObject d, string value)
{
d.SetValue(StateProperty, value);
}
/// <summary>
/// Handles changes to the State property.
/// </summary>
/// <param name="d">
/// The <see cref="DependencyObject"/> on which
/// the property has changed value.
/// </param>
/// <param name="e">
/// Event data that is issued by any event that
/// tracks changes to the effective value of this property.
/// </param>
private static void OnStateChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var stateName = (string)e.NewValue;
var ctrl = (Control)d;
VisualStateManager.GoToState(ctrl, stateName, true);
}
#endregion
}
You should define an enum-style class like the VisualStates class in Silverlight Toolkit that lists all your visual states (so you don't have duplicates), e.g.
internal static class VisualStates
{
#region GroupCommon
/// <summary>
/// Common state group.
/// </summary>
public const string GroupCommon = "CommonStates";
/// <summary>
/// Normal state of the Common state group.
/// </summary>
public const string StateNormal = "Normal";
/// <summary>
/// Normal state of the Common state group.
/// </summary>
public const string StateReadOnly = "ReadOnly";
/// <summary>
/// MouseOver state of the Common state group.
/// </summary>
public const string StateMouseOver = "MouseOver";
/// <summary>
/// Pressed state of the Common state group.
/// </summary>
public const string StatePressed = "Pressed";
/// <summary>
/// Disabled state of the Common state group.
/// </summary>
public const string StateDisabled = "Disabled";
#endregion GroupCommon
#region GroupFocus
/// <summary>
/// Focus state group.
/// </summary>
public const string GroupFocus = "FocusStates";
/// <summary>
/// Unfocused state of the Focus state group.
/// </summary>
public const string StateUnfocused = "Unfocused";
/// <summary>
/// Focused state of the Focus state group.
/// </summary>
public const string StateFocused = "Focused";
#endregion GroupFocus
}
Once you have these - you can have a property in your view model like
public string VisualState { get; set; /* Raise PropertyChange event your preferred way here */ }
And in your view say
<Page
...
xmlns:extensions="using:WinRTXamlToolkit.Controls.Extensions"
extensions:VisualStateExtensions.State="{Binding VisualState}">...
Then your view model can easily drive the change of the visual state and you can use Blend to define what that state looks like - e.g. change the Content or ContentTemplate or simply visibility of various elements in the layout. I think this method has best support of the designer tools since you can flip between views with a click of a button and make updates to these views in Blend.

How do I properly get the entity framework to use my composite primary key?

I have a table defined as such:
CREATE TABLE [dbo].[Sitemap](
[EntityId] [int] NOT NULL,
[PageUrl] [nvarchar](400) NOT NULL,
[Frequency] [nvarchar](400) NOT NULL,
[PageType] [nvarchar](400) NOT NULL,
[UpdatedOn] [datetime] NOT NULL,
CONSTRAINT [PK_Sitemap] PRIMARY KEY CLUSTERED
(
[EntityId] ASC,
[PageType] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
I went to my edmx file and did an update from database. It imported everything but didn't mark both fields as a primary key. I tried added the second field as a primary key, but it still won't treat them as composites.
The issue I am having is when I run an update at the table that has a similar entry such as
EntityId = 1, PageType = 'Product',....
and
EntityId = 1, PageType = 'Artist',...
I am getting an error saying:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
How do I get my Model or code to properly use the composite as the primary key? Or do I have to make some kind of composite field to do this?
UPDATE
My Code is a fork from NopCommerce 1.90
As such I have added an interface and service and registered that with the IoC resolver.
I have the following class that for this table:
//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.nopCommerce.com/License.aspx.
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
//
// Contributor(s): _______.
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using NopSolutions.NopCommerce.BusinessLogic.CustomerManagement;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Payment;
using NopSolutions.NopCommerce.BusinessLogic.Promo.Affiliates;
using NopSolutions.NopCommerce.BusinessLogic.Promo.Discounts;
using NopSolutions.NopCommerce.BusinessLogic.Shipping;
using NopSolutions.NopCommerce.BusinessLogic.Tax;
namespace NopSolutions.NopCommerce.BusinessLogic.SEO.Sitemaps
{
/// <summary>
/// Represents a Sitemap
/// </summary>
[Serializable]
public partial class Sitemap : BaseEntity
{
#region Utilities
#endregion
#region Properties
/// <summary>
/// Gets or Sets EntityId (Product ID, Show ID, etc.)
/// </summary>
public int EntityId { get; set; }
/// <summary>
/// Gets or sets the Page Url
/// </summary>
public string PageUrl { get; set; }
/// <summary>
/// Gets of set the Page Frequency (Should be one of the following - Always, Hourly, Daily, Weekly, Monthly, Yearly, Never)
/// </summary>
public string Frequency { get; set; }
/// <summary>
/// Gets or sets the page type. For example: Product, or Show
/// </summary>
public string PageType { get; set; }
/// <summary>
/// Gets or sets the date and time of sitemap entry update
/// </summary>
public DateTime UpdatedOn { get; set; }
#endregion
}
}
Then I have the interface and service:
Interface:
//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.nopCommerce.com/License.aspx.
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
//
// Contributor(s): _______.
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using NopSolutions.NopCommerce.BusinessLogic.CustomerManagement;
using NopSolutions.NopCommerce.BusinessLogic.Payment;
using NopSolutions.NopCommerce.BusinessLogic.Shipping;
using NopSolutions.NopCommerce.Common;
namespace NopSolutions.NopCommerce.BusinessLogic.SEO.Sitemaps
{
/// <summary>
/// Sitemap service
/// </summary>
public partial interface ISiteMapService
{
/// <summary>
/// Gets a Sitemap entry based on url.
/// </summary>
/// <param name="url">Fully Qualified URL (e.g. http://www.onlinesheetmusic.com/default.aspx)</param>
/// <returns></returns>
Sitemap GetSitemap(string url);
/// <summary>
/// Gets the most recent entry to the sitemap table
/// </summary>
/// <returns></returns>
Sitemap GetLastAddedSitemap(string pageType);
/// <summary>
/// Gets a list of Sitemap entries that are in the init state of the given Page Type
/// </summary>
/// <param name="pageType">Page Type</param>
/// <returns></returns>
List<Sitemap> GetInitEntries(string pageType);
/// <summary>
/// Bool to determine if a given pageType has any entries in the init state.
/// </summary>
/// <param name="pageType"></param>
/// <returns></returns>
bool IsInit(string pageType);
void InitSitemap(string pageType, int startProdId = 0);
/// <summary>
/// Inserts a sitemap entry
/// </summary>
/// <param name="order">Order</param>
void InsertSitemap(Sitemap sm);
/// <summary>
/// Updates the order
/// </summary>
/// <param name="order">The order</param>
void UpdateSitemap(Sitemap sm);
}
}
Service:
//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.nopCommerce.com/License.aspx.
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
//
// Contributor(s): _______.
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using NopSolutions.NopCommerce.BusinessLogic.Audit;
using NopSolutions.NopCommerce.BusinessLogic.Caching;
using NopSolutions.NopCommerce.BusinessLogic.Configuration.Settings;
using NopSolutions.NopCommerce.BusinessLogic.CustomerManagement;
using NopSolutions.NopCommerce.BusinessLogic.Data;
using NopSolutions.NopCommerce.BusinessLogic.Directory;
using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;
using NopSolutions.NopCommerce.BusinessLogic.Localization;
using NopSolutions.NopCommerce.BusinessLogic.Messages;
using NopSolutions.NopCommerce.BusinessLogic.Messages.SMS;
using NopSolutions.NopCommerce.BusinessLogic.Payment;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.Products.Attributes;
using NopSolutions.NopCommerce.BusinessLogic.Profile;
using NopSolutions.NopCommerce.BusinessLogic.Promo.Discounts;
using NopSolutions.NopCommerce.BusinessLogic.QuickBooks;
using NopSolutions.NopCommerce.BusinessLogic.Security;
using NopSolutions.NopCommerce.BusinessLogic.Shipping;
using NopSolutions.NopCommerce.BusinessLogic.Tax;
using NopSolutions.NopCommerce.Common;
using NopSolutions.NopCommerce.Common.Extensions;
using NopSolutions.NopCommerce.Common.Utils;
using NopSolutions.NopCommerce.Common.Utils.Html;
namespace NopSolutions.NopCommerce.BusinessLogic.SEO.Sitemaps
{
/// <summary>
/// Sitemap service
/// </summary>
public partial class SiteMapService : ISiteMapService
{
#region Fields
/// <summary>
/// Object context
/// </summary>
private readonly NopObjectContext _context;
/// <summary>
/// Cache service
/// </summary>
private readonly ICacheManager _cacheManager;
#endregion Fields
#region Ctor
/// <summary>
/// Ctor
/// </summary>
/// <param name="context">Object context</param>
public SiteMapService(NopObjectContext context)
{
this._context = context;
this._cacheManager = new NopRequestCache();
}
#endregion Ctor
#region Utilities
#endregion Utilities
#region Methods
/// <summary>
/// Gets a sitemap entry
/// </summary>
/// <param name="url">The url of the sitempa item</param>
/// <returns>Order</returns>
public Sitemap GetSitemap(string url)
{
if (!url.IsNotNullOrEmpty())
return null;
var query = from sm in _context.Sitemaps
where sm.PageUrl.Contains(url)
select sm;
var sitemap = query.SingleOrDefault();
return sitemap;
}
public Sitemap GetLastAddedSitemap(string pageType)
{
var query = (from sm in _context.Sitemaps
where sm.PageType.Equals(pageType)
orderby sm.UpdatedOn descending
select sm).Take(1);
var sitemap = query.SingleOrDefault();
return sitemap;
}
public List<Sitemap> GetInitEntries(string pageType)
{
var query = (from sm in _context.Sitemaps
where sm.PageType.Equals(pageType)
&& sm.Frequency == "Init"
select sm).Take(500);
return query.ToList();
}
/// <summary>
/// Bool to check if a given type has any entries in the init state.
/// </summary>
/// <param name="pageType">Page Type</param>
/// <returns>True or False</returns>
public bool IsInit(string pageType)
{
var query = (from sm in _context.Sitemaps
where sm.PageType.Equals(pageType)
&& sm.PageUrl.Equals("Init")
select sm).Take(1);
if (query == null)
return true;
else
return false;
}
public void InitSitemap(string pageType, int startProdId = 0)
{
_context.Sp_SitemapInit(pageType, startProdId);
}
/// <summary>
/// Inserts a sitemap entry
/// </summary>
/// <param name="sm">Sitemap Entry to Insert</param>
public void InsertSitemap(Sitemap sm)
{
if (sm == null)
throw new ArgumentNullException("sitemap");
sm.PageUrl = CommonHelper.EnsureNotNull(sm.PageUrl);
sm.PageType = CommonHelper.EnsureNotNull(sm.PageType);
sm.Frequency = CommonHelper.EnsureNotNull(sm.Frequency);
_context.Sitemaps.AddObject(sm);
_context.SaveChanges();
}
/// <summary>
/// Updates a sitemap entry
/// </summary>
/// <param name="sm">Sitemap Entry to update</param>
public void UpdateSitemap(Sitemap sm)
{
if (sm == null)
throw new ArgumentNullException("sitemap");
sm.PageUrl = CommonHelper.EnsureNotNull(sm.PageUrl);
sm.PageType = CommonHelper.EnsureNotNull(sm.PageType);
sm.Frequency = CommonHelper.EnsureNotNull(sm.Frequency);
if(_context.Sitemaps.Any(s => (s.PageUrl == sm.PageUrl || s.EntityId == sm.EntityId)))
{
if (!_context.IsAttached(sm))
_context.Sitemaps.Attach(sm);
}
else
{
_context.Sitemaps.AddObject(sm);
}
_context.SaveChanges();
}
#endregion Methods
#region Properties
/// <summary>
/// Gets a value indicating whether cache is enabled
/// </summary>
public bool CacheEnabled
{
get
{
return IoC.Resolve<ISettingManager>().GetSettingValueBoolean("Cache.OrderManager.CacheEnabled");
}
}
#endregion Properties
}
}
When I call
var sitemapservice = IoC.Resolve<ISiteMapService>();
sitemapservice.InitSitemap("Artist");
var smEntries = sitemapservice.GetInitEntries("Artist");
foreach (Sitemap sm in smEntries)
{
using (MvcMiniProfiler.MiniProfiler.StepStatic("Processing Entry: " + sm.EntityId + "," + sm.PageType))
{
sm.Frequency = "Monthly";
sm.PageUrl = SEOHelper.GetUrl(this.ArtistService.GetArtistById(sm.EntityId), this.SettingManager.StoreUrl);
using (MvcMiniProfiler.MiniProfiler.StepStatic("Updating database"))
{
sitemapservice.UpdateSitemap(sm);
}
curCount++;
}
}
It is supposed to get all the entries that are in the init stage and set them up with the correct url, but for whatever reason I keep getting errors saying An object with the same key already exists in the ObjectStateManager. Though the strange thing is the DB seems to be updating properly it just keeps giving me this error.
I think this is what you are referring to...
public class CollectionItem
{
public int CollectionId { get; set; }
public int ItemId { get; set; }
[RelatedTo(ForeignKey = "CollectionId")]
public Collection Collection { get; set; }
[RelatedTo(ForeignKey = "ItemId")]
public Item Item { get; set; }
}
And use model builder like so
var builder = new ModelBuilder();
// ...
builder.Entity<CollectionItem>().HasKey(p=>new {p.CollectionId, p.ItemId});
// ...
model = builder.CreateModel();
Hope this helps, I have tried to keep the names generic

Custom Activity override double-click and disable open Activity in detail

I'm using custom Activity and overriding OnMouseDoubleClick method. Everything works good but after double click on Activity is that self displayed in designer. It means that in designer is not shown whole workflow but only this Activity. How to disable self-opening Activity in custom designer.
Here is my code in ActivityDesigner.xaml.cs
/// <summary>
/// Raises the <see cref="E:System.Windows.Controls.Control.MouseDoubleClick"/> routed event.
/// </summary>
/// <param name="e">The event data.</param>
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
e.Handled = true;
this.OpenDialogOnDoubleClick();
}
To disable that behabiour you've to use ActivityDesignerOptionsAttribute, in particular its AllowDrillIn property.
Use it on your activity class:
[ActivityDesignerOptions(AllowDrillIn = false)]
public sealed class MyActivity : CodedActivity
{
/* ... */
}
Or if you're using IRegisterMetadata:
internal class Metadata : IRegisterMetadata
{
private AttributeTable attributes;
// Called by the designer to register any design-time metadata.
public void Register()
{
var builder = new AttributeTableBuilder();
builder.AddCustomAttributes(
typeof(MyActivity),
new ActivityDesignerOptionsAttribute{ AllowDrillIn = false });
MetadataStore.AddAttributeTable(builder.CreateTable());
}
}

Passing parameters to modelbinder on action parameter

I wish to use a modelbinder I've made directly on an action method parameter. Such as:
public ActionResult MyAction([ModelBinder(typeof(MyBinder))] string param1)
However, I need to pass a string into the binder itself, so I was wondering if you could do something along the lines of:
public ActionResult MyAction([MyBinder("mystring")] string param1)
Is it possible?
No, you cannot pass parameters via the attribute declaration. If you look at the source code for ModelBinderAttribute, you will see that its constructor takes only a type argument, that it has no other properties, and that it is a sealed class. So this road is a dead end.
The only way to get information into the ModelBinder that I know of is the FormCollection itself.
You could, however, make a parent binder type and subtype it for each param value which you intend to use. It's messy, but it would work in the example you give.
Yes, it's possible. You should a create class drived from System.Web.Mvc.CustomModelBinder, and override the method GetBinder. For example, here implementation that takes in addition to a Type object, also an object array for model binder constructor parameters:
public sealed class MyModelBinderAttribute : CustomModelBinderAttribute
{
/// <summary>
/// Gets or sets the type of the binder.
/// </summary>
/// <returns>The type of the binder.</returns>
public Type BinderType { get; }
/// <summary>
/// Gets or sets the parameters for the model binder constructor.
/// </summary>
public object[] BinderParameters { get; }
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Web.Mvc.ModelBinderAttribute" /> class.
/// </summary>
/// <param name="binderType">The type of the binder.</param>
/// <param name="binderParameters">The parameters for the model binder constructor.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="binderType" /> parameter is null.</exception>
public MyModelBinderAttribute(Type binderType, params object[] binderParameters)
{
if (null == binderType)
{
throw new ArgumentNullException(nameof(binderType));
}
if (!typeof(IModelBinder).IsAssignableFrom(binderType))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"An error occurred when trying to create the IModelBinder '{0}'. Make sure that the binder has a public parameterless constructor.",
binderType.FullName), nameof(binderType));
}
this.BinderType = binderType;
this.BinderParameters = binderParameters ?? throw new ArgumentNullException(nameof(binderParameters));
}
/// <summary>Retrieves an instance of the model binder.</summary>
/// <returns>A reference to an object that implements the <see cref="T:System.Web.Mvc.IModelBinder" /> interface.</returns>
/// <exception cref="T:System.InvalidOperationException">An error occurred while an instance of the model binder was being created.</exception>
public override IModelBinder GetBinder()
{
IModelBinder modelBinder;
try
{
modelBinder = (IModelBinder)Activator.CreateInstance(this.BinderType, this.BinderParameters);
}
catch (Exception ex)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"An error occurred when trying to create the IModelBinder '{0}'. Make sure that the binder has a public parameterless constructor.",
this.BinderType.FullName), ex);
}
return modelBinder;
}
}
Just a thought, I am looking at needing a similar requirement and I wanted to pass in a castled service.
I wonder if you could use the ControllerContext passed into the Binder and expose the service/property or whever via a property?
Just a thought, I am going to try this now and I will feed back
Rich

Resources