Below I have some code that that I cannot Unit test because it tries to read settings from IIS7 and unfortunately our nightly build machine does not have IIS7. The only thing I can think of is to pass the ServerManager into the method, but then again in the caller I will have a ServerManager that will make that method unable to be unit tested. We use MOQ for our Mock library.
public ISection GetCurrentSettings(string location, Action<string> status)
{
#region Sanity Checks
if (string.IsNullOrEmpty(location))
{
throw new ArgumentNullException("location");
}
if (status == null)
{
throw new ArgumentNullException("status");
}
#endregion
ISection section = null;
_logger.Debug(string.Format("Retrieving current IIS settings for app at {0}.", location));
status("Getting current IIS settings.");
using (ServerManager manager = new ServerManager())
{
var data = (from site in manager.Sites
from app in site.Applications
from vdir in app.VirtualDirectories
where vdir.PhysicalPath.Equals(location, StringComparison.CurrentCultureIgnoreCase)
select new {Website = site, App = app}).SingleOrDefault();
if (data == null)
{
_logger.Debug(string.Format("Could not find an application at {0} in IIS. Going to load the defaults instead.", location));
//ToDo possibly load defaults
}
else
{
_logger.Debug(string.Format("Application found in IIS with website: {0} and a path of {1}", data.Website.Name, data.App.Path));
int port =
data.Website.Bindings.Where(b => b.EndPoint != null).Select(b => b.EndPoint.Port).Single();
section = new IISSection
{
ApplicationPoolName = data.App.ApplicationPoolName,
VirtualDirectoryAlias = data.App.Path,
WebsiteName = data.Website.Name,
WebsiteRoot = data.App.VirtualDirectories[0].PhysicalPath,
Port = port.ToString(CultureInfo.InvariantCulture),
WillApply = true,
AnonymousUser = _userService.GetUserByType(UserType.Anonymous)
};
}
return section;
}
Without rewriting your code fully, the general idea would be to pass in an ISettingReader* (implemented as IisSettingReader), which would expose methods that would get the data you need from IIS. Then, you can stub in the ISettingReader to return what you need, by passing ISettingReader into the method/class
*Or, IServerManager as it seems to be the current name, but I am not sure if that is IIS specific
UPDATE
To be more specific, as Darin Dimitrov elaborated, you need to pull all of the dependencies outside of the method and pass them in via parameter/constructor/property injection. This will require a rewrite of the code as it stands in its current state.
If not (and I do suggest a rewrite), then you can use something like TypeMock, which supposedly can fake the dependencies INSIDE a class, but I have not used this myself and only know what I have read on it.
Use Moq.
This will allow you to create a mocked version of ISettings rather than having to create a real one. It has the added advantage of allowing you to specify your own functionality as well.
Related
We're trying to use the REST API of Administration Service to manage the Configuration Manager
(What is the administration service in Configuration Manager?)
We have successfully queried entities of different types and executed some custom static methods (i.e. MoveMembers Method on SMS_ObjectContainerItem). It's all mostly blind shots as there is barely any documentation, but those basic functionalities seem to work fine.
Now we have hit the wall, trying to add collection rules to a SMS_Collection (existing or new). This was normally done calling the AddMembershipRule on the instance itself, that was previously fetched by e.g. WqlConnectionManager or some other proxy. However, this is clearly a no-go on a plain object fetched from the REST service.
We have tried to use the wmi OData service (by a generated proxy) as it clearly offers similar functionality, but this ends up with a "Not supported exception":
var savedCollection = Proxy.SMS_Collection.Where(c => c.CollectionID == result.CollectionID).FirstOrDefault();
savedCollection.AddMembershipRule(inclusionRule);
Proxy.UpdateObject(savedCollection);
Proxy.SaveChanges(); //EXCEPTION
I have tried running POST request in numerous ways, using urls like:
SMS_Collection.AddMembershipRule?CollectionID=DV000037 -> 404
SMS_Collection/DV000037/AddMembershipRule -> 404
SMS_Collection.DV000037.AddMembershipRule -> 404
SMS_Collection/DV000037.AddMembershipRule -> treated it as post to SMS_Collection/DV000037, and therefore triggers an update
or just
SMS_Collection.AddMembershipRule with collectionID as param
As for the request body I've used (or just the AddCollectionMembershipRuleRequestRule):
public class AddCollectionMembershipRuleRequest
{
public AddCollectionMembershipRuleRequestRule CollectionRule { get; set; }
}
public class AddCollectionMembershipRuleRequestRule
{
public string RuleName { get; set; }
public string IncludeCollectionID { get; set; }
}
I have also tried to Post an existing or new collection, with CollectionRules prefilled, but this ends up with an exception complaining about IncludeCollectionID not being part of CollectionRule (base class) - looks like validation being too strict and not dealing well with the inheritance.
var collectionRequest = new ECMCollectionCreationRequest()
{
Name = collectionName,
CollectionType = 2,
RefreshType = 4,
LimitToCollectionID = DefaultLimitingCollectionID,
CollectionRules = new List<SMS_CollectionRule>()
{
new SMS_CollectionRuleIncludeCollection()
{
RuleName = "MembershipRule",
IncludeCollectionID = "DV100020"
}
}
};
Stil, no luck with any of those. Do you have any idea if such a scenario (modification of CollectionRules) is even supported with the Rest /OData service? If so, what would be the right way to achieve so?
It looks like this part is simply not supported at the moment. Looking at the code it seems that the service is not interpreting the arguments properly and therefore causing validation issues.
However, the same can be achieved, in a bit less clean and structured way, using ManagementScope and ManagementObject
var scope = new ManagementScope(siteAddress);
scope.Connect();
using (ManagementObject collection = new ManagementObject(scope, new ManagementPath($"SMS_Collection.CollectionID='{collectionID}'"), new ObjectGetOptions()))
{
if (collection == null)
throw new Exception($"Unable to find collection with ID '{collectionID}'");
collection.Get();
using (ManagementBaseObject inParams = collection.GetMethodParameters("AddMembershipRule"))
using (ManagementClass ruleClass = new ManagementClass(scope, new ManagementPath("SMS_CollectionRuleDirect"), new ObjectGetOptions()))
using (ManagementObject rule = ruleClass.CreateInstance())
{
rule["ResourceClassName"] = "SMS_R_System";
rule["ResourceID"] = ecmResourceID;
rule["RuleName"] = machineName;
inParams["collectionRule"] = rule;
collection.InvokeMethod("AddMembershipRule", inParams, null);
}
}
One can add and remove all the other rule types in similar way.
Another alternative is of course to use PowerShell. Sill, I hope that with one of the next iterations of the Administration Service, support of those methods will be added.
Similarly, there seems to be no way to add/remove application or package and import/export them, using the admin services or even in the way mentioned above.
$Rule="{'collectionRule':{`"#odata.type`": `"#AdminService.SMS_CollectionRuleDirect`", `"ResourceClassName`": `"SMS_R_System`", `"ResourceID`": $DeviceID,`"RuleName`": `"$MachineName`"}}"
$RuleCreated = (Invoke-RestMethod -Method Post -Uri "https://$($CMProvider)/AdminService/wmi/SMS_Collection('$CollectionID')/AdminService.AddMembershipRule" -Body $Rule -ContentType 'application/json' -Credential $Cred)
I am working on writing a unit test for the following function..
public virtual FacadeClass InitNewAMSObject()
{
FacadeClass output = null;
if (ClassMapInfo != null)
{
// Override the metadata caching option
// sets property in external dll
Avectra.netForum.Common.Config.CacheMetaData = false;
if (!String.IsNullOrEmpty(ClassMapInfo.AMSClassName))
{
output = DataUtils.InstantiateFacadeObject(ClassMapInfo.AMSClassName);
}
}
else
{
throw new System.ApplicationException("Need to add the attribute");
}
return output;
}
I cannot get past the line with the comment "sets property in external dll". My shim never seems to 'make it over' to the function being tested. It always throws an error, and its actually trying to use the dll instead of my shim. It may look like i'm setting a property to false only, but the dll uses the property like a method in it's Setter. The .Config is always in an errored state because its constructors (in the dll) are attempting to set up database connections. I expect it to be in at least a null state, and also have the .Fakes. in the class name. I just want to skip over it because I do not write this dll, its 3rd party.
I've researched for a day now and cannot find an example anywhere of how to shim a property that is set on a referenced dll.
Here is my current test for what its worth
[TestMethod]
public void InitNewAMSObjectTEST()
{
using (ShimsContext.Create())
{
Address amsc = new Address();
bool test = true;
ShimConfig.CacheMetaDataGet = () => test;
ShimConfig.CacheMetaDataSetBoolean = value => test = value;
amsc.InitNewAMSObject();
}
}
I have an application that manage IIS Application instances so I am looking for a kind of GIUD to identify each applications. This GUID must be created when the application is deployed in IIS and must be persistent to IIS/Windows updates/restarts.
I did not need the use of Microsoft.Web.Administration: I want a simple way, for each IIS application, it returns its unique ID (by a method called within it).
Here is an example of what I'm looking for and I'd like to have an unique id returned by this.????? :
public class MvcApplication : System.Web.HttpApplication
{
string myUniqueID {
get { return this.?????; }
}
}
Thanks for help.
HostingEnvironment.ApplicationID
https://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.applicationid(v=vs.110).aspx
I had to do something similar.
Read the web.config file for a HostId setting. Preferably split your configuration file into two, with one config file that is local to the install, and doesn't get replaced upon upgrading to a new version of the website.
If the HostId value doesn't exist in the web.config, call Guid.NewGuid() to generate a new value.
Save the new value to the web config, preferably in the local section/file.
Return the value.
Here is some psuedo-code:
public Guid HostId
{
get
{
var result = GetSetting(ConfigFileLocalSettingList.HostId).TryToGuid();
if (result == null)
{
result = Guid.NewGuid();
SetSetting(ConfigFileLocalSettingList.HostId, result.ToString());
Save();
}
return result.Value;
}
}
You can use the assembly GUID for this purpose: In AssemblyInfo.cs, you can find
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("307E39B9-2C41-40CF-B29F-84C8BBCD6519")]
To read this value, you can use:
public static string AssemblyGUID
{
get {
var assembly = Assembly.GetExecutingAssembly();
var attribute = (System.Runtime.InteropServices.GuidAttribute)assembly.GetCustomAttributes(typeof(System.Runtime.InteropServices.GuidAttribute), true)[0];
var GUID = attribute.Value;
return GUID;
}
}
which is taken from another SO answer (you can find it here).
And if it is required, Visual Studio allows you to create a new GUID via menu Tools -> Create GUID - if you need a different one.
Or in C# you simply use
var newGuid=(Guid.NewGuid()).ToString();
Console.WriteLine(newGuid);
to create a new GUID.
I develops a C# Winform application, it is a client and connect to web service to get data. The data returned by webservice is a DataTable. Client will display it on a DataGridView.
My problem is that: Client will take more time to get all data from server (web service is not local with client). So I must to use a thread to get data. This is my model:
Client create a thread to get data -> thread complete and send event to client -> client display data on datagridview on a form.
However, when user closes the form, user can open this form in another time, and client must get data again. This solution will cause the client slowly.
So, I think about a cached data:
Client <---get/add/edit/delete---> Cached Data ---get/add/edit/delete--->Server (web service)
Please give me some suggestions.
Example: cached data should be developed in another application which is same host with client? Or cached data is running in client.
Please give me some techniques to implement this solution.
If having any examples, please give me.
Thanks.
UPDATE : Hello everyone, maybe you think my problem so far. I only want to cache data in client's lifetime. I think cache data should be stored in memory. And when client want to get data, it will check from cache.
If you're using C# 2.0 and you're prepared to ship System.Web as a dependency, then you can use the ASP.NET cache:
using System.Web;
using System.Web.Caching;
Cache webCache;
webCache = HttpContext.Current.Cache;
// See if there's a cached item already
cachedObject = webCache.Get("MyCacheItem");
if (cachedObject == null)
{
// If there's nothing in the cache, call the web service to get a new item
webServiceResult = new Object();
// Cache the web service result for five minutes
webCache.Add("MyCacheItem", webServiceResult, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
else
{
// Item already in the cache - cast it to the right type
webServiceResult = (object)cachedObject;
}
If you're not prepared to ship System.Web, then you might want to take a look at the Enterprise Library Caching block.
If you're on .NET 4.0, however, caching has been pushed into the System.Runtime.Caching namespace. To use this, you'll need to add a reference to System.Runtime.Caching, and then your code will look something like this:
using System.Runtime.Caching;
MemoryCache cache;
object cachedObject;
object webServiceResult;
cache = new MemoryCache("StackOverflow");
cachedObject = cache.Get("MyCacheItem");
if (cachedObject == null)
{
// Call the web service
webServiceResult = new Object();
cache.Add("MyCacheItem", webServiceResult, DateTime.Now.AddMinutes(5));
}
else
{
webServiceResult = (object)cachedObject;
}
All these caches run in-process to the client. Because your data is coming from a web service, as Adam says, you're going to have difficulty determining the freshness of the data - you'll have to make a judgement call on how often the data changes and how long you cache the data for.
Do you have the ability to make changes/add to the webservice?
If you can Sync Services may be an option for you. You can define which tables are syncronised, and all the sync stuff is managed for you.
Check out
http://msdn.microsoft.com/en-us/sync/default.aspx
and shout if you need more information.
You might try the Enterprise Library's Caching Application Block. It's easy to use, stores in memory and, if you ever need to later, it supports adding a backup location for persisting beyond the life of the application (such as to a database, isolated storage, file, etc.) and even encryption too.
Use EntLib 3.1 if you're stuck with .NET 2.0. There's not much new (for caching, at least) in the newer EntLibs aside from better customization support.
Identify which objects you would like to serialize, and cache to isolated storage. Specify the level of data isolation you would like (application level, user level, etc).
Example:
You could create a generic serializer, a very basic sample would look like this:
public class SampleDataSerializer
{
public static void Deserialize<T>(out T data, Stream stm)
{
var xs = new XmlSerializer(typeof(T));
data = (T)xs.Deserialize(stm);
}
public static void Serialize<T>(T data, Stream stm)
{
try
{
var xs = new XmlSerializer(typeof(T));
xs.Serialize(stm, data);
}
catch (Exception e)
{
throw;
}
}
}
Note that you probably should put in some overloads to the Serialize and Deserialize methods to accomodate readers, or any other types you are actually using in your app (e.g., XmlDocuments, etc).
The operation to save to IsolatedStorage can be handled by a utility class (example below):
public class SampleIsolatedStorageManager : IDisposable
{
private string filename;
private string directoryname;
IsolatedStorageFile isf;
public SampleIsolatedStorageManager()
{
filename = string.Empty;
directoryname = string.Empty;
// create an ISF scoped to domain user...
isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain,
typeof(System.Security.Policy.Url), typeof(System.Security.Policy.Url));
}
public void Save<T>(T parm)
{
using (IsolatedStorageFileStream stm = GetStreamByStoredType<T>(FileMode.Create))
{
SampleDataSerializer.Serialize<T>(parm, stm);
}
}
public T Restore<T>() where T : new()
{
try
{
if (GetFileNameByType<T>().Length > 0)
{
T result = new T();
using (IsolatedStorageFileStream stm = GetStreamByStoredType<T>(FileMode.Open))
{
SampleDataSerializer.Deserialize<T>(out result, stm);
}
return result;
}
else
{
return default(T);
}
}
catch
{
try
{
Clear<T>();
}
catch
{
}
return default(T);
}
}
public void Clear<T>()
{
if (isf.GetFileNames(GetFileNameByType<T>()).Length > 0)
{
isf.DeleteFile(GetFileNameByType<T>());
}
}
private string GetFileNameByType<T>()
{
return typeof(T).Name + ".cache";
}
private IsolatedStorageFileStream GetStreamByStoredType<T>(FileMode mode)
{
var stm = new IsolatedStorageFileStream(GetFileNameByType<T>(), mode, isf);
return stm;
}
#region IDisposable Members
public void Dispose()
{
isf.Close();
}
}
Finally, remember to add the following using clauses:
using System.IO;
using System.IO.IsolatedStorage;
using System.Xml.Serialization;
The actual code to use the classes above could look like this:
var myClass = new MyClass();
myClass.name = "something";
using (var mgr = new SampleIsolatedStorageManager())
{
mgr.Save<MyClass>(myClass);
}
This will save the instance you specify to be saved to the isolated storage. To retrieve the instance, simply call:
using (var mgr = new SampleIsolatedStorageManager())
{
mgr.Restore<MyClass>();
}
Note: the sample I've provided only supports one serialized instance per type. I'm not sure if you need more than that. Make whatever modifications you need to support further functionalities.
HTH!
You can serialise the DataTable to file:
http://forums.asp.net/t/1441971.aspx
Your only concern then is deciding when the cache has gone stale. Perhaps timestamp the file?
In our implementation every row in the database has a last-updated timestamp. Every time our client application accesses a table we select the latest last-updated timestamp from the cache and send that value to the server. The server responds with all the rows that have newer timestamps.
Recently I have observed the following interesting scenario in one of the application I'm developing using .NET 3.5. In this particualr application I have a singletion object which I access as a static variable. I exepected that the .NET run time should initializes this singleton object at the very first time I access it, but it seems this is not the case. .NET runtime initialize it way before I access this particualr object. Following is some peudo code,
if(!initSingleton)
//Do some work without using the singletion class.
else
//Do some work using the singletion class.
Even at runtime my code only executes the code in side the if statement .NET runtime still initializes the singleton object. In some of the application runs I don't need to access this pariticualr object at all!
Also I don't see this behavior with debug builds. Seems this has something to do with optimized builds (release builds).
Is this is the expected behavior of the .NET runtime?
Update:
Following is the actual code,
private void InitServiceClient(NetworkCredential credentials, bool https)
{
string uri = currentCrawlingWebUrl;
if (!uri.EndsWith("/"))
uri = string.Concat(uri, "/");
uri = string.Concat(uri, siteDataEndPointSuffix);
siteDataService = new SiteData.SiteDataSoapClient();
siteDataService.Endpoint.Address = new EndpointAddress(uri);
if (credentials != null)
{
siteDataService.ClientCredentials.Windows.ClientCredential = credentials;
}
else if (MOSSStateHandler.Instance.UserName.Length > 0 && MOSSStateHandler.Instance.Password.Length > 0)
{
siteDataService.ClientCredentials.Windows.ClientCredential.UserName = MOSSStateHandler.Instance.UserName;
siteDataService.ClientCredentials.Windows.ClientCredential.Password = MOSSStateHandler.Instance.Password;
siteDataService.ClientCredentials.Windows.ClientCredential.Domain = MOSSStateHandler.Instance.Domain;
}
BasicHttpBinding httpBinding = (BasicHttpBinding)siteDataService.Endpoint.Binding;
httpBinding.Security.Mode = (https ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.TransportCredentialOnly);
string authmode = MOSSConnectorConfiguration.Instance.Config.GetString(ConfigConstants.SHAREPOINT_AUTH_PROVIDER, "ntlm");
if (authmode.Equals("ntlm", StringComparison.OrdinalIgnoreCase))
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
else if (authmode.Equals("kerberos", StringComparison.OrdinalIgnoreCase))
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
else
throw new Exception(string.Format("Not supported"));
}
Even though my application doesn't execute the code in side else if block the class MOSSStateHandler get initialized.
I suggest you read Jon Skeet's article about Singleton pattern in C#, and its appendix about lazy-loading etc. You'll get better understanding about the implementation issues.
From C# language spec:
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
An instance of the class is created.
Any of the static members of the class are referenced.