WCF DataContract with custom List Attribute - asp.net

I have a WCF webservice, that exposes these classes:
[DataContract]
public class TemplatesFormat
{
List<DynAttribute> _dynsattributes = new List<DynAttribute>();
[DataMember]
public List<DynAttribute> DynsAttributes
{
get { return _dynsattributes; }
set { _dynsattributes = value; }
}
}
[DataContract]
public class DynAttribute
{
string _key = "";
string _val = "";
[DataMember]
public string Key
{
get { return _key; }
set { _key = value; }
}
[DataMember]
public string Value
{
get { return _val; }
set { _val = value; }
}
}
Basically, 2 classes. DynAttribute with 2 string attributes and TemplatesFormat, with an attribute that is a List of DynAttribute class.
So far, so good.
But, when I reference the web service from an ASP.NET web page and try to use the TemplatesFormat, I can't see the List attribute.
I mean, I actually "see" it, but it is not a list (does not contain an "Add()") and I don't know how to use it.
I think I am missing something related with de [DataContrat] and the fact that it is a custom type, since, I don't have the same problem with DynAttribute class (I see the Key and Value attributes because they are strings) but, I can't get it right for the List...
Any idea???

When you add reference to wcf service you need to change Collection Type to Generic List.
Please see my post wcf-proxy-returning-array-instead-of-list-even-though-collection-type-generic for more details and snipp picture.

WCF is meant to support consumption by many other platforms. Because List<DynAttribute> is not a primitive type, it is likely converting it to DynAttribute[].
In your consuming application. Try taking your variable and seeing if you can .ToList() it to turn it back into the List<DynAttribute> you're expecting.

Related

How do I get the strongly-typed value of an OutArgument in code?

Given an Activity (created via the designer) that has several OutArgument properties, is it possible to get their strongly-typed value from a property after invoking the workflow?
The code looks like this:
// generated class
public partial class ActivityFoo : System.Activities.Activity....
{
....
public System.Activities.OutArgument<decimal> Bar { ... }
public System.Activities.OutArgument<string> Baz { ... }
}
// my class
var activity = new ActivityFoo();
var result = WorkflowInvoker.Invoke(activity);
decimal d = activity.Bar.Get(?)
string s = activity.Baz.Get(?)
The T Get() method on OutArgument<T> that requires an ActivityContext which I'm not sure how to obtain in code.
I also realize it's possible to get the un-typed values from result["Bar"] and result["Baz"] and cast them, but I'm hoping there's another way.
Updated to make it clear there are multiple Out values, although the question would still apply even if there was only one.
If you look at workflows as code, an Activity is no more than a method that receives input arguments and (potentially) returns output arguments.
It happens that Activities allows one to return multiple output arguments, something that C# methods, for example, don't (actually that's about to change with C# 7 and tuples).
That's why you've an WorkflowInvoker.Invoke() overload which returns a Dictionary<string, object> because the framework obviously doesn't know what\how many\of what type output arguments you have.
Bottom line, the only way for you to do it fully strong-typed is exactly the same way you would be doing on a normal C# method - return one OutArgument of a custom type:
public class ActivityFooOutput
{
public decimal Bar { get; set }
public decimal Baz { get; set; }
}
// generated class
public partial class ActivityFoo : System.Activities.Activity....
{
public System.Activities.OutArgument<ActivityFooOutput> Result { ... }
}
// everything's strongly-typed from here on
var result = WorkflowInvoker.Invoke<ActivityFooOutput>(activity);
decimal d = result.Bar;
string s result.Baz;
Actually, if you don't want to create a custom type for it, you can use said tuples:
// generated
public System.Activities.OutArgument<Tuple<decimal, string>> Result { ... }
// everything's strongly-typed from here on
var result = WorkflowInvoker.Invoke<Tuple<decimal, string>>(activity);
decimal d = result.Item1;
string s result.Item2;
Being the first option obviously more scalable and verbose.

Testing Methods with Reference to Web.Config in .Net C#

I searched a lot and still couldn't find a solid solution for this. Suppose you have methods in your application. This methods use "System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration" to access some setting in the web.config. If you try to test these methods, your tests will fail because your test project doesn't have web.config.
What is the best way to solve this problem. For projects with simple config file, I usually use a method like this as facade method.
public class Config
{
public static String getKeyValue(String keyName)
{
if (keyName == String.Empty) return String.Empty;
String result = "";
System.Configuration.Configuration rootWebConfig1 =
System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
if (rootWebConfig1.AppSettings.Settings.Count > 0)
{
System.Configuration.KeyValueConfigurationElement reportEngineKey =
rootWebConfig1.AppSettings.Settings[keyName];
if (reportEngineKey != null)
{
result = reportEngineKey.Value;
}
}
return result;
}
}
Every time I tried to set the path for OpenWebConfiguration( ), I got the error "The relative virtual path is not allowed"
To make that scenario more testable, I usually take the approach of making a "settings manager" of my own, and giving it an interface. So for example:
public interface IConfig
{
string GetSettingValue(string settingName);
}
Then I can have my "real" implementation:
public sealed class Config : IConfig
{
public string GetSettingValue(string settingName)
{
// your code from your getKeyValue() method would go here
}
}
Then my code that uses it would take in an instance of this (this is an example of the Dependency Inversion Principal):
public void DoStuff(IConfig configuration)
{
string someSetting = configuration.GetSettingValue("ThatThingINeed");
// use setting...
}
So now for my production code, I can call DoStuff and pass in an instance of Config.
When I need to test, I can use a mocking tool (Moq, JustMock, RhinoMocks, etc) to create a fake IConfig that returns a known value without hitting the actual .config file, or you can do it without a mocking framework by making your own mocks (and store them in your test project).
public class ConfigMock : IConfig
{
private Dictionary<string, string> settings;
public void SetSettingValue(string settingName, string value)
{
settings[settingName] = value;
}
public string GetSettingValue(string settingName)
{
return settings[settingName];
}
}
and
[Test]
public void SomeExampleTest()
{
var config = new ConfigMock();
config.SetSettingValue("MySetting", "SomeValue");
var underTest = new MyClass();
underTest.DoStuff(config);
}
The easiest way to do this is to use a mocking library such as moq. It takes a bit of time to figure it out, but once you do you can abstract away most of your plumbing to return the values you need for repeatable, consistent testing.

ASP.NET: displaying notification on another page, after succesfully saved data to database

I searched the web but haven't found a real good answer for this question..
Let's say I have a form, on AddToList.aspx, and i want that after you hit send, it will direct you back to List.aspx, with a message "The Item was added to list" in a message box div.
do i need to send List.aspx?msg=my message, or is there another good way of doing it?
EDIT:
so i made this helper class:
public class MessageHelper : System.Web.UI.MasterPage
{
public void SetMessage(String message)
{
Session["Message"] = message;
}
public string GetMessage()
{
if (String.IsNullOrEmpty(Session["Message"]))
{
String temp = Session["Message"];
Session["Message"] = "";
return temp;
}
else
{
return "";
}
}
}
and got this error:
Error 32 The best overloaded method match for 'string.IsNullOrEmpty(string)' has some invalid arguments
Error 33 Argument '1': cannot convert from 'object' to 'string'
Error 34 Cannot implicitly convert type 'object' to 'string'. An explicit conversion exists (are you missing a cast?)
You need to convert to string. Session parameters are stored as objects.
It may also be userful to implement this as a extension method. This way it will be available on all page types (Master and UI)
public static class MessageHelper
{
public static void SetMessage(this Page page, String message)
{
Session["Message"] = message;
}
public static string GetMessage(this Page page)
{
var messageText = Session["Message"] as string;
if (!String.IsNullOrEmpty(messageText ))
{
Session["Message"] = "";
return messageText;
}
return "";
}
}
You could certainly use the query string to pass data to your List.aspx page, but be careful passing text that you're planning on writing out in the HTML - you'll need to protect against XSS attacks.
There are several other ways to do this. Chances are, you're going to have several places in your application where you want to redirect the user to another page, but also display a message that has something to do with what they did on the previous page (saved an item, deleted an item, etc.). It would be better to come up with more of a global scheme for this rather than a one-off just for this particular instance.
One idea is to use the Session for storing a message, then do your redirect.
Session("Message") = "Item was added to list."
Response.Redirect("List.aspx")
Then, on your pages (or a Master Page, perhaps), you check Session("Message") and if it's got something, you show that message to the user, then clear that variable.
If Session("Message") IsNot Nothing Then
Response.Write(CType(Session("Message"), String)) 'or set a label text, or show a pop up div, or whatever'
Session("Message") = Nothing
End If
If you use this approach, I recommend you write a helper class, and just use that to manage your messaging:
MessageHelper.SetMessage("Item added to list.")
and
MessageHelper.GetMessage()
would be the methods you would need.
I believe you could do it by setting the PostBackUrl of the button used to save the data to "List.aspx". Maybe set a variable to true/false on AddToList.aspx and then access it from List.aspx?
Not sure if it's better but it's an option.
I can't comment yet or I would have just commented this to your post. You need to cast your session variable like this: (string)Session["Message"]. So, code would look like this:
public class MessageHelper : System.Web.UI.MasterPage
{
public void SetMessage(String message)
{
Session["Message"] = message;
}
public string GetMessage()
{
if (String.IsNullOrEmpty((string)Session["Message"]))
{
String temp = (string)Session["Message"];
Session["Message"] = "";
return temp;
}
else
{
return "";
}
}
}
Actually there's a better way of writing that class: make it one property instead of two methods. It would look like this: (I also fixed your logic; GetMessage was always returning blank)
public class MessageHelper : System.Web.UI.MasterPage
{
public MessageHelper()
{
}
public string Message
{
set { Session["Message"] = value; }
get
{
if (String.IsNullOrEmpty((string)Session["Message"]))
{
Session["Message"] = "";
}
return (string)Session["Message"];
}
}
}
In your two respective files, you would set and get it like so:
//in AddToList.aspx
MessageHelper msg = new MessageHelper();
msg.Message = "The Item was added to your list.";
//and in List.aspx, assigned to an arbitrary Label named myMessageLabel
MessageHelper msg = new MessageHelper();
myMessageLabel.Text = msg.Message;

ASP.NET - Avoid hardcoding paths

I'm looking for a best practice solution that aims to reduce the amount of URLs that are hard-coded in an ASP.NET application.
For example, when viewing a product details screen, performing an edit on these details, and then submitting the changes, the user is redirected back to the product listing screen. Instead of coding the following:
Response.Redirect("~/products/list.aspx?category=books");
I would like to have a solution in place that allows me to do something like this:
Pages.GotoProductList("books");
where Pages is a member of the common base class.
I'm just spit-balling here, and would love to hear any other way in which anyone has managed their application redirects.
EDIT
I ended up creating the following solution: I already had a common base class, to which I added a Pages enum (thanks Mark), with each item having a System.ComponentModel.DescriptionAttribute attribute containing the page's URL:
public enum Pages
{
[Description("~/secure/default.aspx")]
Landing,
[Description("~/secure/modelling/default.aspx")]
ModellingHome,
[Description("~/secure/reports/default.aspx")]
ReportsHome,
[Description("~/error.aspx")]
Error
}
Then I created a few overloaded methods to handle different scenarios. I used reflection to get the URL of the page through it's Description attribute, and I pass query-string parameters as an anonymous type (also using reflection to add each property as a query-string parameter):
private string GetEnumDescription(Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
return attr.Description;
}
}
return null;
}
protected string GetPageUrl(Enums.Pages target, object variables)
{
var sb = new StringBuilder();
sb.Append(UrlHelper.ResolveUrl(Helper.GetEnumDescription(target)));
if (variables != null)
{
sb.Append("?");
var properties = (variables.GetType()).GetProperties();
foreach (var property in properties)
sb.Append(string.Format("{0}={1}&", property.Name, property.GetValue(variables, null)));
}
return sb.ToString();
}
protected void GotoPage(Enums.Pages target, object variables, bool useTransfer)
{
if(useTransfer)
HttpContext.Current.Server.Transfer(GetPageUrl(target, variables));
else
HttpContext.Current.Response.Redirect(GetPageUrl(target, variables));
}
A typical call would then look like so:
GotoPage(Enums.Pages.Landing, new {id = 12, category = "books"});
Comments?
I'd suggest that you derive your own class ("MyPageClass") from the Page class and include this method there:
public class MyPageClass : Page
{
private const string productListPagePath = "~/products/list.aspx?category=";
protected void GotoProductList(string category)
{
Response.Redirect(productListPagePath + category);
}
}
Then, in your codebehind, make sure that your page derives from this class:
public partial class Default : MyPageClass
{
...
}
within that, you can redirect just by using:
GotoProductList("Books");
Now, this is a bit limited as is since you'll undoubtedly have a variety of other pages like the ProductList page. You could give each one of them its own method in your page class but this is kind of grody and not smoothly extensible.
I solve a problem kind of like this by keeping a db table with a page name/file name mapping in it (I'm calling external, dynamically added HTML files, not ASPX files so my needs are a bit different but I think the principles apply). Your call would then use either a string or, better yet, an enum to redirect:
protected void GoToPage(PageTypeEnum pgType, string category)
{
//Get the enum-to-page mapping from a table or a dictionary object stored in the Application space on startup
Response.Redirect(GetPageString(pgType) + category); // *something* like this
}
From your page your call would be: GoToPage(enumProductList, "Books");
The nice thing is that the call is to a function defined in an ancestor class (no need to pass around or create manager objects) and the path is pretty obvious (intellisense will limit your ranges if you use an enum).
Good luck!
You have a wealth of options availible, and they all start with creating a mapping dictionary, whereas you can reference a keyword to a hard URL. Whether you chose to store it in a configuration file or database lookup table, your options are endless.
You have a huge number of options available here. Database table or XML file are probably the most commonly used examples.
// Please note i have not included any error handling code.
public class RoutingHelper
{
private NameValueCollecton routes;
private void LoadRoutes()
{
//Get your routes from db or config file
routes = /* what ever your source is*/
}
public void RedirectToSection(string section)
{
if(routes == null) LoadRoutes();
Response.Redirect(routes[section]);
}
}
This is just sample code, and it can be implemented any way you wish. The main question you need to think about is where you want to store the mappings. A simple xml file could do it:
`<mappings>
<map name="Books" value="/products.aspx/section=books"/>
...
</mappings>`
and then just load that into your routes collection.
public class BasePage : Page
{
public virtual string GetVirtualUrl()
{
throw new NotImplementedException();
}
public void PageRedirect<T>() where T : BasePage, new()
{
T page = new T();
Response.Redirect(page.GetVirtualUrl());
}
}
public partial class SomePage1 : BasePage
{
protected void Page_Load()
{
// Redirect to SomePage2.aspx
PageRedirect<SomePage2>();
}
}
public partial class SomePage2 : BasePage
{
public override string GetVirtualUrl()
{
return "~/Folder/SomePage2.aspx";
}
}

ASP.NET Key/Value List

I'm doing a custom 404 page for a large website that's undergoing a redesign. There are about 40 high-use pages that customers may have bookmarked, and our new site structure will break these bookmarks.
On my custom 404 page, I want to alert them to the new URL if they attempt to navigate to one of these high-use pages via its old URL. So I have a couple of dynamic controls on the 404 page, one for a "did-you-want-this?" type of dialog, and another for a "if-so-go-here (and update your bookmark)" type of dialog. That's the easy part.
To suggest a new URL, I'm looking at the requested URL. If it has key words in it, I'm going to suggest the new URL based on that, and them I'm firing off the appropriate did-you-want..., and if-so... suggestions on the 404 page as mentioned above.
So I want to store these 40-ish key/value pairs (keyword/new-URL pairs) in a data structure, and I'm not sure what would be best. Dictionary? IDictionary? What's the difference and which is more appropriate?
Or am I totally on the wrong track?
Thanks for your time.
I would use the Dictionary<T,T> from the System.Collections.Generic namespace.
You could use NameValueCollection.
Maybe this is overkill for your use case, but I'd probably allow for multiple keywords per Uri, and a relative weight score. Then, dynamically score the keywords that match.
class UriSuggester {
private List<SuggestedUri> Uris { get; set; }
Uri[] GetSuggestions(Uri originalUri) {
var suggestionHits = new Dictionary<SuggestedUri, int>();
foreach (var keyword in KeyWords.Parse(originalUri)) {
// find suggestions matching that keyword
foreach (var suggestedUri in Uris.Where(u => u.Keywords.Contains(keyword)) {
// add a hit for keyword match
suggestionHits[suggestedUri] += 1;
}
}
// order by weight * hits
return suggestionHits.Keys
.OrderBy(s => s.Weight * suggestionHits[s])
.Select(s => s.Uri)
.ToArray();
}
}
class SuggestedUri {
public Uri Suggested { get; set; }
public int Weight { get; set; }
public Keyword[] Keywords;
}
class Keyword {
public string Value { get; set; }
public static Keyword[] Parse(Uri uri);
override Equals;
override GetHashCode;
}
Dictionary would be fine. Wether you store it as the interface type IDictionary or Dictionary itself wouldn't matter much in this case as it's not going to be passed much around, besides on the 404 page itself.
Have you considered doing some URL rewriting to still support the old URLs?
You can consider writing your own class logic and then assign that to a List data structure as following:
public class KeyValuesClass
{
private string a_key;
private string a_value;
public KeyValuesClass(string a_key, string a_value)
{
this.a_key = a_key;
this.a_value = a_value;
}
public string Key
{
get{ return a_key; }
set { a_key = value; }
}
public string Value
{
get{ return a_value; }
set { a_value = value; }
}
}
somewhere in the code
List<KeyValuesClass> my_key_value_list = new List<KeyValuesClass>();
my_key_value_list.Add(new KeyValuesClass("key", "value");
But you can consider Dictionary as our fellow programmer mentioned it above :)

Resources