My silverlight project has references to the RIA Services Silverlight Client and my .web application has access to RIAServices.Server.
I can't seem to find a good tutorial to learn how to connect these. Once I understand how to get the data from my NumberGenerator method. I can pick it up from there. I have three questions that I need some help with.
First, am I setting up this project correctly? I never done a project with RIA before.
Secondly what reference do I need to use ServiceContract(), FaultContract, and OperationContract? Most examples show that they are in the System.ServiceModel library. In this case with using the external library RIA Services Silverlight Client that is not the case. It throws a error saying I am missing a reference. What other libraries would have those three in it?
My last question is what URI do you use in the SystemDomainContext? Where I found the code at used this MyFirstRIAApplication-Web-EmployeeDomainService.svc but in my case I don't have anything that has a .svc extension. Would I use the .xap or .aspx?
Here is my Silverlight code
Here is my NumberGenerator Silverlight UserControl Page Code
public partial class NumberGenerator : UserControl
{
public NumberGenerator()
{
InitializeComponent();
GenerateNumber();
}
private void GenerateButton_Click(object sender, RoutedEventArgs e)
{
GenerateNumber();
}
private void GenerateNumber()
{
int result = 0
SystemClientServices systemclientservices = SystemClientServices.Instance;
result = systemclientservices.GenerateNumber();
NumberLabel.Content = result;
}
}
Here is my System Client Services class
private readonly static SystemClientServices _instance = new SystemClientServices();
private SystemDomainContext _domainContext = new SystemDomainContext();
private SystemClientServices() { }
public static SystemClientServices Instance
{
get
{
return _instance;
}
}
public int GenerateNumber()
{
//Code goes here to get the information from the Domainservices
LoadOperation load = this._domainContext.Load(this._domainContext.GetNumberGeneratorQuery(), LoadBehavior.KeepCurrent, false);
return Convert.ToInt32(load.Entities);
}
Here is my NumberGenerator class on the silverlight local project
public sealed partial class NumberGenerator : Entity
{
private static readonly NumberGenerator _instance = new NumberGenerator();
public int NumberGenerated { get; set; }
public NumberGenerator()
{
NumberGenerated = 0;
}
public static NumberGenerator Instance
{
get
{
return _instance;
}
}
}
Here is System Domain Conext class on the silverlight local project
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel.DomainServices;
using System.ServiceModel.DomainServices.Client;
using System.ServiceModel.DomainServices.Client.ApplicationServices;
using System.ServiceModel.Web;
using System.ServiceModel;
public sealed partial class SystemDomainConext : DomainContext
{
#region Extensibility Method Definitions
/// <summary>
/// This method is invoked from the constructor once initialization is complete and
/// can be used for further object setup.
/// </summary>
partial void OnCreated();
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeDomainContext"/> class.
/// </summary>
public SystemDomainConext() : this(new WebDomainClient<ISystemDomainServiceContract>(new Uri("MyFirstRIAApplication-Web-EmployeeDomainService.svc", UriKind.Relative)))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeDomainContext"/> class with the specified service URI.
/// </summary>
/// <param name="serviceUri">The EmployeeDomainService service URI.</param>
public SystemDomainConext(Uri serviceUri) : this(new WebDomainClient<ISystemDomainServiceContract>(serviceUri))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeDomainContext"/> class with the specified <paramref name="domainClient"/>.
/// </summary>
/// <param name="domainClient">The DomainClient instance to use for this DomainContext.</param>
public SystemDomainConext(DomainClient domainClient) : base(domainClient)
{
this.OnCreated();
}
/// <summary>
/// Gets the set of <see cref="Employee"/> entity instances that have been loaded into this <see cref="EmployeeDomainContext"/> instance.
/// </summary>
public EntitySet<NumberGenerator> GeneratedNumber
{
get
{
return base.EntityContainer.GetEntitySet<NumberGenerator>();
}
}
/// <summary>
/// Gets an EntityQuery instance that can be used to load <see cref="Employee"/> entity instances using the 'GetEmployee' query.
/// </summary>
/// <returns>An EntityQuery that can be loaded to retrieve <see cref="Employee"/> entity instances.</returns>
public EntityQuery<NumberGenerator> GetNumberGeneratorQuery()
{
this.ValidateMethod("GetGeneratedNumber", null);
return base.CreateQuery<NumberGenerator>("GetNumberGenerator", null, false, true);
}
/// <summary>
/// Creates a new EntityContainer for this DomainContext's EntitySets.
/// </summary>
/// <returns>A new container instance.</returns>
protected override EntityContainer CreateEntityContainer()
{
return new NumberGeneratorDomainContextEntityContainer();
}
/// <summary>
/// Service contract for the 'EmployeeDomainService' DomainService.
/// </summary>
[ServiceContract()]
public interface ISystemDomainServiceContract
{
/// <summary>
/// Asynchronously invokes the 'GetEmployee' operation.
/// </summary>
/// <param name="callback">Callback to invoke on completion.</param>
/// <param name="asyncState">Optional state object.</param>
/// <returns>An IAsyncResult that can be used to monitor the request.</returns>
[FaultContract(typeof(DomainServiceFault), Action="http://tempuri.org/EmployeeDomainService/GetEmployeeDomainServiceFault", Name="DomainServiceFault", Namespace="DomainServices")]
[OperationContract(AsyncPattern=true, Action="http://tempuri.org/EmployeeDomainService/GetEmployee", ReplyAction="http://tempuri.org/EmployeeDomainService/GetEmployeeResponse")]
[WebGet()]
IAsyncResult GetGeneratedNumber(AsyncCallback callback, object asyncState);
/// <summary>
/// Completes the asynchronous operation begun by 'BeginGetEmployee'.
/// </summary>
/// <param name="result">The IAsyncResult returned from 'BeginGetEmployee'.</param>
/// <returns>The 'QueryResult' returned from the 'GetEmployee' operation.</returns>
QueryResult<NumberGenerator> EndGetGeneratedNumber(IAsyncResult result);
/// <summary>
/// Asynchronously invokes the 'SubmitChanges' operation.
/// </summary>
/// <param name="changeSet">The change-set to submit.</param>
/// <param name="callback">Callback to invoke on completion.</param>
/// <param name="asyncState">Optional state object.</param>
/// <returns>An IAsyncResult that can be used to monitor the request.</returns>
[FaultContract(typeof(DomainServiceFault), Action="http://tempuri.org/EmployeeDomainService/SubmitChangesDomainServiceFault", Name="DomainServiceFault", Namespace="DomainServices")]
[OperationContract(AsyncPattern=true, Action="http://tempuri.org/EmployeeDomainService/SubmitChanges", ReplyAction="http://tempuri.org/EmployeeDomainService/SubmitChangesResponse")]
IAsyncResult BeginSubmitChanges(IEnumerable<ChangeSetEntry> changeSet, AsyncCallback callback, object asyncState);
/// <summary>
/// Completes the asynchronous operation begun by 'BeginSubmitChanges'.
/// </summary>
/// <param name="result">The IAsyncResult returned from 'BeginSubmitChanges'.</param>
/// <returns>The collection of change-set entry elements returned from 'SubmitChanges'.</returns>
IEnumerable<ChangeSetEntry> EndSubmitChanges(IAsyncResult result);
}
internal sealed class NumberGeneratorDomainContextEntityContainer : EntityContainer
{
public NumberGeneratorDomainContextEntityContainer()
{
this.CreateEntitySet<NumberGenerator>(EntitySetOperations.Edit);
}
}
}
Here is my System Domain Services class that is on the Silverlight.web
[EnableClientAccess()]
public class SystemDomainServices : DomainService
{
private NumberGenerator numberGenerator = NumberGenerator.Instance;
public int NumberGenerate()
{
return numberGenerator.NumberGenerated;
}
}
Here is the NumberGenerator class on the silverlight.web
private static readonly NumberGenerator _instance = new NumberGenerator();
[Key]
public int NumberGenerated { get; set; }
public NumberGenerator()
{
NumberGenerated = GenerateNumber();
}
public static NumberGenerator Instance
{
get
{
return _instance;
}
}
public int GenerateNumber()
{
string db_date = "";
int db_num = 0;
string todaysdate = "";
int temp_num = db_num;
int result = 0;
using (SqlConnection connection = new SqlConnection(DBConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("SELECT * FROM table", connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
db_date = reader.GetString(0);
db_num = (int)(reader.GetSqlInt32(1));
todaysdate = DateTime.Now.ToString("yMMdd");
temp_num = db_num;
}
reader.Close();
}
if (todaysdate != db_date)
{
using (SqlCommand dateUpate = new SqlCommand("UPDATE table SET tsDate='" + todaysdate + "' WHERE tsDate='" + db_date + "'", connection))
{
dateUpate.ExecuteNonQuery();
}
db_num = 0;
db_date = todaysdate;
}
db_num++;
using (SqlCommand numUpate = new SqlCommand("UPDATE table SET tsNum='" + db_num + "' WHERE tsNum='" + temp_num + "'", connection))
{
numUpate.ExecuteNonQuery();
}
result = Convert.ToInt32(db_date + db_num.ToString().PadLeft(3, '0'));
connection.Close();
connection.Dispose();
}
return result;
}
}
The answer to question two you might be have to go to Tools at the top, NuGet Package Manager, Package Management Console, and then type the following command to install the package for those three functions.
PM> Install-Package RIAServices.Silverlight.DomainDataSource
Related
I'm trying to use NeoLoad for performance with Ranorex and here is my code i have a Ranorex code and Neoload code that calls Ranorex code to be executed. When I run the second code I get this error:
No scenario name available. Please set a valid NeoLoad scenario name. I need to run the second code to perform some application performance monitoring using Neoload package on Ranorex.
First Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Threading;
using WinForms = System.Windows.Forms;
using Ranorex;
using Ranorex.Core;
using Ranorex.Core.Testing;
using Ranorex.Core.Repository;
namespace NewOpe.NeoLoad.Modules
{
#pragma warning disable 0436 //(CS0436) The type 'type' in 'assembly' conflicts with the imported type 'type2' in 'assembly'. Using the type defined in 'assembly'.
/// <summary>
///The loadrecord recording.
/// </summary>
[TestModule("0f400fed-c562-4ef4-99fb-40ee81b4fa59", ModuleType.Recording, 1)]
public partial class loadrecord : ITestModule
{
/// <summary>
/// Holds an instance of the NewOpe.NewOpeRepository repository.
/// </summary>
public static NewOpe.NewOpeRepository repo = NewOpe.NewOpeRepository.Instance;
static loadrecord instance = new loadrecord();
/// <summary>
/// Constructs a new instance.
/// </summary>
public loadrecord()
{
}
/// <summary>
/// Gets a static instance of this recording.
/// </summary>
public static loadrecord Instance
{
get { return instance; }
}
#region Variables
#endregion
/// <summary>
/// Starts the replay of the static recording <see cref="Instance"/>.
/// </summary>
[System.CodeDom.Compiler.GeneratedCode("Ranorex", "8.0")]
public static void Start()
{
TestModuleRunner.Run(Instance);
}
/// <summary>
/// Performs the playback of actions in this recording.
/// </summary>
/// <remarks>You should not call this method directly, instead pass the module
/// instance to the <see cref="TestModuleRunner.Run(ITestModule)"/> method
/// that will in turn invoke this method.</remarks>
[System.CodeDom.Compiler.GeneratedCode("Ranorex", "8.0")]
void ITestModule.Run()
{
Mouse.DefaultMoveTime = 300;
Keyboard.DefaultKeyPressTime = 100;
Delay.SpeedFactor = 1.00;
Init();
Report.Log(ReportLevel.Info, "Application", "Run application 'C:\\Users\\Ahmed Abd El Nasser\\Desktop\\dist\\run.bat' with arguments '' in normal mode.", new RecordItemIndex(0));
Host.Local.RunApplication("C:\\Users\\Ahmed Abd El Nasser\\Desktop\\dist\\run.bat", "", "C:\\Users\\Ahmed Abd El Nasser\\Desktop\\dist", false);
Delay.Milliseconds(0);
Report.Log(ReportLevel.Info, "Delay", "Waiting for 1m.", new RecordItemIndex(1));
Delay.Duration(60000, false);
Report.Log(ReportLevel.Info, "Mouse", "Mouse Left Click item 'FormOsseoViewRelease2Sprint12.JLayeredPane1.CUsersAhmedAbdElNasserDesktopGha' at 49;12.", repo.FormOsseoViewRelease2Sprint12.JLayeredPane1.CUsersAhmedAbdElNasserDesktopGhaInfo, new RecordItemIndex(2));
repo.FormOsseoViewRelease2Sprint12.JLayeredPane1.CUsersAhmedAbdElNasserDesktopGha.Click("49;12");
Delay.Milliseconds(200);
Report.Log(ReportLevel.Info, "Mouse", "Mouse Left Click item 'FormOsseoViewRelease2Sprint12.JLayeredPane1.Both' at 10;13.", repo.FormOsseoViewRelease2Sprint12.JLayeredPane1.BothInfo, new RecordItemIndex(3));
repo.FormOsseoViewRelease2Sprint12.JLayeredPane1.Both.Click("10;13");
Delay.Milliseconds(200);
Second Code:
using System;
using System.Globalization;
using Ranorex.Core.Testing;
namespace Ranorex.NeoLoad
{
/// <summary>
/// Starts a NeoLoad test
/// </summary>
/// <remarks>
/// A NeoLoad test can only be started, when a connection to the runtime and data exchange API was
/// established prior.
/// </remarks>
[TestModule("1702CBFB-DA15-4C42-B6C4-6FCBA8DAE96F", ModuleType.UserCode, 1)]
public class StartNeoLoadTest : ITestModule
{
// For testing, make it mockable
internal static INeoloadApi api = NeoloadApi.Instance;
/// <summary>
/// Check interval for operations that can timeout in the format 'hh:mm:ss'.
/// This interval needs to be smaller than the timeout.
/// </summary>
[TestVariable("29B0EB4D-D82E-4DDA-8ABB-355734B346D2")]
public string Interval { get; set; }
/// <summary>
/// Timeout for the connect operation in the format 'hh:mm:ss'.
/// </summary>
[TestVariable("7E6AAB81-CA2F-4084-A2B2-C0B8792122A1")]
public string Timeout { get; set; }
/// <summary>
/// The name of the scenario. Use this info tho identify the collected data on the NeoLoad server system.
/// </summary>
[TestVariable("7242C624-2E81-447E-A148-533E4BB082BE")]
public string Scenario { get; set; }
public StartNeoLoadTest()
{
this.Interval = "00:00:10";
this.Timeout = "00:01:00";
}
void ITestModule.Run()
{
if (string.IsNullOrWhiteSpace(this.Scenario))
{
throw new InvalidOperationException("No scenario name available. Please set a valid NeoLoad scenario name.");
}
try
{
const string fmt = #"hh\:mm\:ss";
var timeout = TimeSpan.ParseExact(this.Timeout, fmt, CultureInfo.InvariantCulture);
var interval = TimeSpan.ParseExact(this.Interval, fmt, CultureInfo.InvariantCulture);
if (timeout < interval)
{
throw new ArgumentException(string.Format("The given timeout of '{0}' is smaller than the interval with a value of '{1}', but interval has to be smaller than timeout.",
timeout.ToString(fmt), interval.ToString(fmt)));
}
api.StartNeoLoadTest(this.Scenario, timeout, interval);
}
catch (FormatException ex)
{
throw new Exception("'Timeout' or 'Interval' was specified with invalid format. Please use the format 'hh:mm:ss' e.g. '00:01:10' for one minute and ten seconds." + ex);
}
}
}
}
I know it is a fairly old question but the Ranorex/Neoload integration is now managed by Neotys and the source code can be found here:
https://github.com/Neotys-Labs/Ranorex
I'm working on a client-server system at the moment, and I'm trying to get a collection to synchronise across a websocket. Everything is in C# + .Net 4.5, and I was wondering if there was a particular best practise for synchronising data over a websocket. It's a one way sync:
Server: BindingCollection< MyClass > ----- Websocket -----> Client: BindingCollection< MyClass >
The collection could be up to 1000 objects with 20 fields each so sending the whole lot each time seems a little wasteful.
I would use a observer pattern and only send the changed object to be synced.
So I finally took the time to write a small example.
I am using a in-memory generic repository that invokes events on changes. The changes is then sent to all clients so that you do not have to send the complete list/collection.
A simple model to monitor
using System;
namespace SynchronizingCollection.Common.Model
{
public class MyModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
A Generic Repository
Notice the event OnChange that is called when something is added/updated/removed. The event is "subscribed" to in a XSockets long running controller (a singleton) See the "RepoMonitor" class
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace SynchronizingCollection.Server.Repository
{
/// <summary>
/// A static generic thread-safe repository for in-memory storage
/// </summary>
/// <typeparam name="TK">Key Type</typeparam>
/// <typeparam name="T">Value Type</typeparam>
public static class Repository<TK, T>
{
/// <summary>
/// When something changes
/// </summary>
public static event EventHandler<OnChangedArgs<TK,T>> OnChange;
private static ConcurrentDictionary<TK, T> Container { get; set; }
static Repository()
{
Container = new ConcurrentDictionary<TK, T>();
}
/// <summary>
/// Adds or updates the entity T with key TK
/// </summary>
/// <param name="key"></param>
/// <param name="entity"></param>
/// <returns></returns>
public static T AddOrUpdate(TK key, T entity)
{
var obj = Container.AddOrUpdate(key, entity, (s, o) => entity);
if(OnChange != null)
OnChange.Invoke(null,new OnChangedArgs<TK, T>(){Key = key,Value = entity, Operation = Operation.AddUpdate});
return obj;
}
/// <summary>
/// Removes the entity T with key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Remove(TK key)
{
T entity;
var result = Container.TryRemove(key, out entity);
if (result)
{
if (OnChange != null)
OnChange.Invoke(null, new OnChangedArgs<TK, T>() { Key = key, Value = entity, Operation = Operation.Remove});
}
return result;
}
/// <summary>
/// Removes all entities matching the expression f
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static int Remove(Func<T, bool> f)
{
return FindWithKeys(f).Count(o => Remove(o.Key));
}
/// <summary>
/// Find all entities T matching the expression f
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static IEnumerable<T> Find(Func<T, bool> f)
{
return Container.Values.Where(f);
}
/// <summary>
/// Find all entities T matching the expression f and returns a Dictionary TK,T
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public static IDictionary<TK, T> FindWithKeys(Func<T, bool> f)
{
var y = from x in Container
where f.Invoke(x.Value)
select x;
return y.ToDictionary(x => x.Key, x => x.Value);
}
/// <summary>
/// Returns all entities as a Dictionary TK,T
/// </summary>
/// <returns></returns>
public static IDictionary<TK, T> GetAllWithKeys()
{
return Container;
}
/// <summary>
/// Returns all entities T from the repository
/// </summary>
/// <returns></returns>
public static IEnumerable<T> GetAll()
{
return Container.Values;
}
/// <summary>
/// Get a single entity T with the key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T GetById(TK key)
{
return Container.ContainsKey(key) ? Container[key] : default(T);
}
/// <summary>
/// Get a single entity T as a KeyValuePair TK,T with the key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static KeyValuePair<TK, T> GetByIdWithKey(TK key)
{
return Container.ContainsKey(key) ? new KeyValuePair<TK, T>(key, Container[key]) : new KeyValuePair<TK, T>(key, default(T));
}
/// <summary>
/// Checks if the repository has a key TK
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool ContainsKey(TK key)
{
return Container.ContainsKey(key);
}
}
}
Event argument and an enum to know what change just happend
using System;
namespace SynchronizingCollection.Server.Repository
{
/// <summary>
/// To send changes in the repo
/// </summary>
/// <typeparam name="TK"></typeparam>
/// <typeparam name="T"></typeparam>
public class OnChangedArgs<TK,T> : EventArgs
{
public Operation Operation { get; set; }
public TK Key { get; set; }
public T Value { get; set; }
}
}
namespace SynchronizingCollection.Server.Repository
{
/// <summary>
/// What kind of change was performed
/// </summary>
public enum Operation
{
AddUpdate,
Remove
}
}
The Controller that send changes to the clients...
using System;
using SynchronizingCollection.Common.Model;
using SynchronizingCollection.Server.Repository;
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
using XSockets.Plugin.Framework;
using XSockets.Plugin.Framework.Attributes;
namespace SynchronizingCollection.Server
{
/// <summary>
/// Long running controller that will send information to clients about the collection changes
/// </summary>
[XSocketMetadata(PluginRange = PluginRange.Internal, PluginAlias = "RepoMonitor")]
public class RepositoryMonitor : XSocketController
{
public RepositoryMonitor()
{
Repository<Guid, MyModel>.OnChange += RepositoryOnChanged;
}
private void RepositoryOnChanged(object sender, OnChangedArgs<Guid, MyModel> e)
{
switch (e.Operation)
{
case Operation.Remove:
this.InvokeTo<Demo>(p => p.SendUpdates, e.Value,"removed");
break;
case Operation.AddUpdate:
this.InvokeTo<Demo>(p => p.SendUpdates, e.Value, "addorupdated");
break;
}
}
}
}
The XSockets controller that clients call to add/remove/update the collection.
using System;
using SynchronizingCollection.Common.Model;
using SynchronizingCollection.Server.Repository;
using XSockets.Core.XSocket;
namespace SynchronizingCollection.Server
{
public class Demo : XSocketController
{
public bool SendUpdates { get; set; }
public Demo()
{
//By default all clients get updates
SendUpdates = true;
}
public void AddOrUpdateModel(MyModel model)
{
Repository<Guid, MyModel>.AddOrUpdate(model.Id, model);
}
public void RemoveModel(MyModel model)
{
Repository<Guid, MyModel>.Remove(model.Id);
}
}
}
And a demo client in C# that adds and removed 10 different objects... But it would be easy to use the JavaScript API as well. Especially with knockoutjs for manipulating the collection on the client.
using System;
using System.Threading;
using SynchronizingCollection.Common.Model;
using XSockets.Client40;
namespace SynchronizingCollection.Client
{
class Program
{
static void Main(string[] args)
{
var c = new XSocketClient("ws://127.0.0.1:4502","http://localhost","demo");
c.Controller("demo").OnOpen += (sender, connectArgs) => Console.WriteLine("Demo OPEN");
c.Controller("demo").On<MyModel>("addorupdated", model => Console.WriteLine("Updated " + model.Name));
c.Controller("demo").On<MyModel>("removed", model => Console.WriteLine("Removed " + model.Name));
c.Open();
for (var i = 0; i < 10; i++)
{
var m = new MyModel() {Id = Guid.NewGuid(), Name = "Person Nr" + i, Age = i};
c.Controller("demo").Invoke("AddOrUpdateModel", m);
Thread.Sleep(2000);
c.Controller("demo").Invoke("RemoveModel", m);
Thread.Sleep(2000);
}
Console.ReadLine();
}
}
}
You can download the project from my dropbox: https://www.dropbox.com/s/5ljbedovx6ufkww/SynchronizingCollection.zip?dl=0
Regards
Uffe
Sorry for the general title but it's a bit hard to explain in few words what is my problem currently.
So I have a simple class factory like this:
public Model Construct<T>(T param) where T : IModelable
{
new Model {Resource = param};
return n;
}
The Model class looks like this:
public class Model
{
public object Resource { get; set; }
}
The problem is, that you can see, is the Resource is currently an object. And I would like that Resource should be the type, what is get from the Construct and not lost the type-safe...
I tried to solve it with type parameter but it fails, because I can extend Model class with type parameter but what if I would like to store it to a simple class repository?
Then Construct will work, but if I would like to get the instanced class from the repository, I have to declare the type paramter again like:
Repository.Get<Model<Spaceship>>(0) .... and of course it's wrong because I would like that Model itself knows, what type of Resource has been added in Construct.
Does anybody any idea how to handle this?
The whole code currently look like this:
/// <summary>
/// Class Repository
/// </summary>
public sealed class Repository
{
/// <summary>
/// The _lock
/// </summary>
private static readonly object _lock = new object();
/// <summary>
/// The _syncroot
/// </summary>
private static readonly object _syncroot = new object();
/// <summary>
/// The _instance
/// </summary>
private static volatile Repository _instance;
/// <summary>
/// The _dict
/// </summary>
private static readonly Dictionary<int, object> _dict
= new Dictionary<int, object>();
/// <summary>
/// Prevents a default data of the <see cref="Repository" /> class from being created.
/// </summary>
private Repository()
{
}
/// <summary>
/// Gets the items.
/// </summary>
/// <value>The items.</value>
public static Repository Data
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null) _instance = new Repository();
}
}
return _instance;
}
}
/// <summary>
/// Allocates the specified id.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="parameter">The parameter.</param>
/// <resource name="id">The id.</resource>
/// <resource name="parameter">The parameter.</resource>
public void Allocate(int id, object parameter)
{
lock (_syncroot)
{
_dict.Add(id, parameter);
}
}
/// <summary>
/// Gets the specified id.
/// </summary>
/// <typeparam name="T">The type of the tref.</typeparam>
/// <param name="id">The id.</param>
/// <returns>``0.</returns>
/// <resource name="id">The id.</resource>
public T Get<T>(int id)
{
lock (_syncroot)
{
return (T) _dict[id];
}
}
}
/// <summary>
/// Class IModelFactory
/// </summary>
public sealed class ModelFactory
{
/// <summary>
/// The _lock
/// </summary>
private static readonly object _lock = new object();
/// <summary>
/// The _instance
/// </summary>
private static volatile ModelFactory _instance;
/// <summary>
/// Prevents a default instance of the <see cref="ModelFactory" /> class from being created.
/// </summary>
private ModelFactory()
{
}
/// <summary>
/// Gets the data.
/// </summary>
/// <value>The data.</value>
public static ModelFactory Data
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null) _instance = new ModelFactory();
}
}
return _instance;
}
}
/// <summary>
/// Constructs the specified param.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="param">The param.</param>
/// <returns>Model{``0}.</returns>
public Model Construct<T>(T param) where T : IModelable
{
var n = new Model {Resource = param};
return n;
}
}
/// <summary>
/// Class Model
/// </summary>
/// <typeparam name="T"></typeparam>
public class Model
{
public object Resource { get; set; }
}
/// <summary>
/// Interface IModelable
/// </summary>
public interface IModelable
{
/// <summary>
/// Gets or sets the mass.
/// </summary>
/// <value>The mass.</value>
float Mass { get; set; }
}
/// <summary>
/// Class spaceship
/// </summary>
public class Spaceship : IModelable
{
/// <summary>
/// Gets or sets the mass.
/// </summary>
/// <value>The mass.</value>
public float Mass { get; set; }
}
So the problem will be lighted here:
Add to the Repository:
Repository.Data.Allocate(1, ModelFactory.Data.Construct(new Spaceship()));
It's okay, but after:
var test_variable = Repository.Data.Get<Model>(1);
So now I have a non type-safe object from a type parameter, I don't know, that what type of class has been stored with the c model construction.
I'm very thankful for the suggestions of using type paramter on the Model class as well, but than it will come up another problem, because I have to change the Get function with it:
var test_variable = Repository.Data.Get<Model<Spaceship>>(1);
But that's definitely wrong, because I won't know, that what kind of type of class has been stored in the model..., so I would like to achieve to avoid this type parameter definition when I would like to load the instance from the Repository.
You can solve this by making your Model class generic, like this:
public class Model<T>
{
public T Resource { get; set; }
}
Then, your Construct method could work like this:
public Model<T> Construct<T>(T param) where T : IModelable<T>
{
return new Model<T>() {Resource = param};
}
You probably need a generic type in the model class:
public class Model<T>
{
public T Resource { get; set; }
}
This sort of structure is one approach you could take:
public Model<T> Construct<T>(T param) where T : IModelable
{
var n = new Model<T> {Resource = param};
return n;
}
public class Model<T> : IModel<T> where T : IModelable
{
public T Resource { get; set; }
}
public interface IModel<out T> where T : IModelable
{
T Resource { get; }
}
This covariant interface allows you to refer to the types more generically where you wish, in the same way that you can pass an IEnumerable<string> into something expecting an IEnumerable<object>.
IModel<Spaceship> shipModel = // whatever
IModel<IModelable> model = shipModel;
//or even:
List<Model<Spaceship>> shipModels = // whatever
IEnumerable<IModel<IModelable>> models = shipModels;
Here's the situation:
/// <summary>
/// A business logic class.
/// </summary>
public class BusinessClassWithInterceptor : BusinessClass, IBusinessClass
{
/// <summary>
/// Initializes a new instance of the <see cref="BusinessClassWithoutInterceptor"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
public BusinessClassWithInterceptor(Logger logger)
: base(logger)
{
}
/// <summary>
/// Displays all cows.
/// </summary>
public void DisplayAllCows()
{
this.Logger.Write("Displaying all cows:");
var repository = new CowRepository();
foreach (CowEntity cow in repository.GetAllCows())
{
this.Logger.Write(" " + cow);
}
}
/// <summary>
/// Inserts a normande.
/// </summary>
public void InsertNormande(int id, string name)
{
this.DisplayAllCows();
var repository = new CowRepository();
repository.InsertCow(new CowEntity { Id = id, Name = name, Origin = CowOrigin.Normandie });
}
}
With castle windsor, this class is configured to be intercepted with this interceptor:
/// <summary>
/// Interceptor for logging business methods.
/// </summary>
public class BusinessLogInterceptor : IInterceptor
{
/// <summary>
/// Intercepts the specified invocation.
/// </summary>
/// <param name="invocation">The invocation.</param>
public void Intercept(IInvocation invocation)
{
Logger logger = ((IBusinessClass)invocation.InvocationTarget).Logger;
var parameters = new StringBuilder();
ParameterInfo[] methodParameters = invocation.Method.GetParameters();
for (int index = 0; index < methodParameters.Length; index++)
{
parameters.AppendFormat("{0} = {1}", methodParameters[index].Name, invocation.Arguments[index]);
if (index < methodParameters.Length - 1)
{
parameters.Append(", ");
}
}
logger.Format("Calling {0}( {1} )", invocation.Method.Name, parameters.ToString());
invocation.Proceed();
logger.Format("Exiting {0}", invocation.Method.Name);
}
}
The issue takes place during the call to InsertNormande.
The call to InsertNormande is well intercepted, but the call to DisplayAllCows in InsertNormande is not intercepted...
It really bothers me.
Is there a way to achieve interception in this scenario ?
I don't think there's an easy way of doing it... method calls that are internal to the class can't be intercepted, since they don't go through the proxy.
You could achieve logging of all methods by other means, such as an AOP framework like PostSharp
I try to compile Xsd2Db solution in .NET 4.0 framework. I add a reference to "Microsoft ADO Ext. 6.0 for DDL and Security" and use it in this way -
using System;
using System.IO;
using ADOX;
namespace Xsd2Db.Data
{
/// <summary>
/// Summary description for JetDataSchemaAdapter.
/// </summary>
public sealed class JetDataSchemaAdapter : AdoxDataSchemaAdapter
{
/// <summary>
///
/// </summary>
internal const string Extension = ".mdb";
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
internal static string GetPath(string name)
{
return Path.GetFullPath(
Path.ChangeExtension(name, Extension));
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
internal static string GetConnectionString(string name)
{
return String.Format(
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Jet OLEDB:Engine Type=5;",
GetPath(name));
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
protected override void DeleteCatalog(string name)
{
File.Delete(GetPath(name));
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
protected override void CreateCatalog(string name)
{
// Force the run-time to let go of the file! Otherwise,
// cleanup and other operations might fail because the file
// will still be in use.
Catalog catalog = new CatalogClass();
catalog.Create(GetConnectionString(name));
catalog.ActiveConnection = null;
catalog = null;
GC.Collect();
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
protected override Catalog OpenCatalog(string name)
{
Catalog catalog = new CatalogClass();
catalog.let_ActiveConnection(GetConnectionString(name));
return catalog;
}
}
}
Now I have an error during compiling -- "The type 'ADOX.CatalogClass' has no constructors defined".
Clicking the error link brings me to the meta data -
#region Assembly Interop.ADOX.dll, v4.0.30319
// C:\xsd\xsd2db\xsd2db\Common\DataSchemaAdapter\obj\Debug\Interop.ADOX.dll
#endregion
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace ADOX {
[Guid("00000602-0000-0010-8000-00AA006D2EA4")]
[ClassInterface(0)]
[TypeLibType(2)]
public class CatalogClass : _Catalog, Catalog {
public CatalogClass();
[DispId(1)]
public virtual dynamic ActiveConnection { get; set; }
[DispId(4)]
public virtual Groups Groups { get; }
[DispId(2)]
public virtual Procedures Procedures { get; }
[DispId(0)]
public virtual Tables Tables { get; }
[DispId(5)]
public virtual Users Users { get; }
[DispId(3)]
public virtual Views Views { get; }
[DispId(6)]
public virtual dynamic Create(string ConnectString);
[DispId(7)]
public virtual string GetObjectOwner(string ObjectName, ObjectTypeEnum ObjectType, object ObjectTypeId = Type.Missing);
[DispId(1)]
public virtual void let_ActiveConnection(object pVal);
[DispId(8)]
public virtual void SetObjectOwner(string ObjectName, ObjectTypeEnum ObjectType, string UserName, object ObjectTypeId = Type.Missing);
}
}
We can see public CatalogClass(); but not a constructor.
I believe in .NET 1.x this worked. But why .NET 4.0 has this problem? Anyone encoutered this and What is the best way to handle this?
Thanks!
I did a search and found out the problem does exist in .NET 4.0. Here are some explanations and solutions -
http://blogs.msdn.com/b/mshneer/archive/2009/12/07/interop-type-xxx-cannot-be-embedded-use-the-applicable-interface-instead.aspx;
http://mokosh.co.uk/post/2010/04/10/net-4-0-interop-type-cannot-be-embedded-use-the-applicable-interface-instead/