In one project I was really close to implementing OData with a WCF Service and Entity Framework. With the WCF Service I was able to extend DataService and it was able translate my context to entities queryable by OData. I want to do the same thing except using an ODataController. Ideally, I'd like to have all of my Entity Sets accessible from one controller. Is this possible?
This is the approach that I've tried so far.
How do I bind multiple entity sets to one odata controller?
This the approach I used in my wcf service
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IncludeExceptionDetailInFaults = true)]
public class MyService : MSDataService.System.Data.Services.DataService<MyDataContext>
{
private SPWeb _web;
private string connectionString;
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public MyService() : base()
{
}
#endregion
#region Methods
protected override MyDataContext CreateDataSource()
{
return newMyDataContext();
}
public static void InitializeService(MSDataService::System.Data.Services.DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("MyEntity1", MSDataService::System.Data.Services.EntitySetRights.AllRead);
config.SetEntitySetAccessRule("MyEntity2", MSDataService::System.Data.Services.EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2;
config.DataServiceBehavior.AcceptProjectionRequests = true;
config.UseVerboseErrors = true;
config.EnableTypeAccess("*");
config.SetEntitySetPageSize("*", 1000);
}
#endregion
}
MyDataContext had DbSets for MyEntity1 and MyEntity2.
For the OData Controller, I would currently need two different controllers for each entity, but I would like to have one generic controller.
Sounds like you need RESTier:
Restier is the spiritual successor to WCF Data Services. Instead of
generating endless boilerplate code with the current Web API + OData
toolchain, RESTier helps you boostrap a standardized, queryable
HTTP-based REST interface in literally minutes. And that's just the
beginning.
Like WCF Data Services before it, Restier provides simple and
straightforward ways to shape queries and intercept submissions before
and after they hit the database. And like Web API + OData, you still
have the flexibility to add your own custom queries and actions with
techniques you're already familiar with.
https://github.com/OData/RESTier
Related
NOTE: This example has been simplified
I have got a Client's Contact table and wanted to retrieve specific client contact information from DB. The code I typed belove brings me all contact details. I wanted to use a parameter to only bring me specific client contacts.
I used IClientContactRepository interface like this
public interface IClientContactRepository
{
IQueryable<ClientContactModel> ClientContacts { get; }
}
And i used this class to retrive data from database with dapper
public class ClientContactRepository : IClientContactRepository
{
private readonly IConfiguration configuration;
private List<ClientContactModel> ClientContactList {get;set;}
public ClientContactRepository(IConfiguration config)
{
configuration = config;
SqlConnection conn = new SqlConnection(configuration["ConnectionString"]);
using (var connection = conn)
{
ClientContactList = connection.Query<ClientContactModel>("Select * FROM ContactTable ").ToList();
}
}
public IQueryable<ClientContactModel> ClientContacts => ClientContactList;
}
In my Startup class
services.AddTransient<IClientContactRepository, ClientContactRepository>();
My QUESTION is: can I pass the client's id parameter to the constructor.
I tried this: add a parameter to the constructor
public ClientContactRepository(IConfiguration config, int clientId)
and tried to start up class.
services.AddTransient<IClientContactRepository, ClientContactRepository(int,i)>()
Didn't work....
Can someone help me how to pass parameter please?
Yes, but where are you getting the client ID from - is it a configured value that will be static for the lifetime of the application? If so, you can use the AddTansient method overload that accepts a factory delegate to create the objects.
The better way (will cover all use cases) is registering the type that can provide that information (create one if no such type exists) with the DI container and use that as a parameter in the constructor of your repo.
As an example, let’s say you get your client ID from a claim, so the type you need to inject is IPrincipal:
services.AddScoped<IPrincipal>(
provider => provider.GetService<IHttpContextAccessor>()
.HttpContext
.User);
You would then inject the IPrincipal into your repo constructor and retrieve the client ID. An even better way would be to create your own type “ClientIdAccessor” which is responsible for providing the client ID. You would then not have a dependency on IPrincipal when testing your repo and the implementation of this new type would only depend on external libraries for your asp.net core implementation.
Side note: are you certain you want to use AddTransient for your repo? Usually you’d want to use the same repo object for the lifetime of the request (I.e. AddScoped).
I am in the middle of big web application, I use Entity Framework as my data service, now we need some windows application to work with our data, so I want to give them a service with WCF
But when my client wants to get service some error is happened from my public property which I use for caching Entity Model
public partial class DepositEntities : ObjectContext
{
public static DepositEntities Current
{
get
{
DepositEntities oc =
HttpContext.Current.Items["ObjectContext"] as DepositEntities;
if (oc == null)
{
oc = new DepositEntities();
HttpContext.Current.Items["ObjectContext"] = oc;
}
return oc;
}
}
}
I know the problem is from this line, after I debug my code
DepositEntities oc = System.Web.HttpContext.Current.Items["ObjectContext"] as DepositEntities;
When I change my Current property body to some thing like this
public static DepositEntities Current
{
get
{
DepositEntities oc = new DepositEntities();
return oc;
}
}
everything is OK when I get data from services I have no problem
But everywhere I have join in my codes I have problem because It thinks there are different data source because of new DepositEntities();
You're most likely experiencing problems because WCF doesn't have HttpContext.Current. Read more about contexts in WCF - this question may be a good start: http://social.msdn.microsoft.com/Forums/en/wcf/thread/27896125-b61e-42bd-a1b0-e6da5c23e6fc.
I also think it would be better for you to manage lifetime of an ObjectContext with a DI Container (ie. Castle Windsor). Thanks to this, it won't be necessary to expose static property Current which is a problem for WCF service, unit tests, etc.
Check out "Hosting WCF Services in ASP.NET Compatibility Mode" in wcf service and ASP.NET. It explains how to get a valid HttpContext in a wcf service.
I have written a basic WCF service that uses SubSonic for data retrieval.
After publishing the service I am consuming it in a C# application. When calling the method that uses that SubSonic query, I get back the right number of objects from the database, but none of them contain the database properties and their values. It looks like only SubSonic properties.
The SubSonic DAL is contained in a separate project that is referenced in the WCF service project.
WCF service interface:
[OperationContract]
GeoLocationCollection GetGeoLocations(long websiteID);
Worker method:
public GeoLocationCollection GetWebsiteGeoLocations(long websiteID)
{
GeoLocationCollection locationsCollection = new Select()
.Where(GeoLocation.Columns.WebsiteID).IsEqualTo(1)
.From(GeoLocation.Schema)
.ExecuteAsCollection<GeoLocationCollection>();
return locationsCollection;
}
Both the GeoLocationCollection and GeoLocation have been automatically decorated with [Serializable].
The service is consumed as follows:
MyService.MyServiceClient client = new MyService.MyServiceClient();
var result = client.GetWebsiteGeoLocations(1);
foreach (MyService.GeoLocation location in result)
{
// do stuff
}
So once again, why can I not see any of my actual table properties/values in location?
WCF services require [DataContract] + [DataMember] attribute and not [Serializable]. This maybe the reason for you to not get the attribute values.
VS2K8, C#. I currently have a solution with the following projects:
Core - POCO domain objects and data interfaces
Data - Repository pattern. Implements the interfaces defined in Core. Also has the mapping classes for Fluent NHibernate.
Infrastructure - Used for dependency injection, configuring nhibernate, etc.
Tests - Tests [tests for Core, Data, etc.]
Web - MVC2 web project
Now, with that being said, I'm trying to determine the best course of action for adding things like: Joining a mailing list, a contact information submission, etc.
I don't believe these should be in web. And I don't think they need to be placed in Data, save for when saving the mailing list information and contact information, fwiw.
It sounds like this should be placed on the Core level. With that said, if placed in Core it would rely on saving to the database. I'm a bit perplexed on where to place this and how to architect it. What route what you guys take?
Is this something as simple as just creating an interface on the Core level called MailingList with a method called JoinMailingList(emailAddress), and then implementing that interface on Data? This doesn't sound like the best route as it's a business concern. Thoughts?
Add a services library and include service interfaces in your core library.
public interface IMailingListService
{
void Subscribe(string email);
void Unsubscribe(string email);
}
public interface IMailingListRepository
{
MailingList LoadMailingList();
void SaveMailingList(MailingList list);
}
public class MailingListService: IMailingListService
{
private IMailingListRepository _repository;
public MailingList(IMailingListRepository repository)
{
_repository = repository;
}
public void Subscribe(string email)
{
var list = _repository.LoadMailingList();
list.Subscribe(email);
_repository.SaveMailingList(list);
}
}
if you have an entity which is reference in the client and a webservice like this
public class Post
{
public int ID {get; set;}
string Data {get; set;}
}
public class MyService: System.Web.Services.WebService
{
[WebMethod]
public int Write (Post post)
{
//Do stuff
}
}
on the client in order to use the entity you to instantiate from the proxy class
public void ClientMethod()
{
var post = new proxyclass.Post();
//fill post
new ProxyClass.Myservice().Write(post)
}
how can i use my domain entity to call the webservice?
public void ClientMethod()
{
var post = new Post();
//fill post
new ProxyClass.Myservice().Write(post)
}
Basically, you can't - with regular web-services, at least... the proxy class is completely separate. However, the above is possible with WCF, where you don't actually need proxy classes at all (however, for SOA purity it is a good idea to use them).
You could use reflection (etc) to copy the properties between your domain entities and the proxies, but it is quite hard to get this 100% right (although xml serialization should work [in theory] as an intermediate language).
So; if you want to use assembly sharing; consider using WCF, which supports this ;-p
To get hold of a service without using a proxy layer, you can do tricks like:
public class WcfClient<T> : ClientBase<T> where T : class
{
public T Service { get { return base.Channel; } }
}
(this will access the default configuration from the app.config; for more control you need to add a few constructor overloads matching to the base constructor overloads)
Then:
interface IFoo {void Bar();}
...
using(WcfClient<IFoo> client = new WcfClient<IFoo>()) {
client.Service.Bar();
}
I suspect that one of these might answer your qestion. The common theme is wsdl.exe /sharetypes and svcutil /reference.
Managing 2 web references with shared class dependency in a .NET project
Force .NET webservice to use local object class, not proxy class
.Net Consuming Web Service: Identical types in two different services
How to get a webserice to serialize/deserialize the same type in .net
.NET SOAP Common types
wsdl.exe /sharetypes
You should use WCF for new development whenever possible.
However, you should reconsider your reasons for wanting to use your domain class on the client. It does violate the principles of SOA by exposing to the client some details of the implementation of the service. Why should the client know anything about your entity classes, beyond the data that they contain?
For instance, your entity classes may contain methods to save the entity to the database. Why does your client need access to that method?
Also, one of the principals of SOA is to interoperate with different platforms. As soon as you require your client to use your (.NET) entity, you prevent a Java or PHP client from being written to use your service.
You may have good enough reasons to overcome such objections, but I recommend that you think it through and make sure your reasons are good enough.
For cases like this you're better to use json for sending and receiving data to and from web service.
Newtonsoft.json is the best json serializer for .Net. so you should change your Write method like below:
public void ClientMethod()
{
var post = new Post();
//fill post
new ProxyClass.Myservice().Write(JsonConvert.SerializeObject(post))
}