How ria services manage transactions - silverlight

I learnt how can we configure transactions in Entity Framework using TransactionScope in one other question of mine. However it still confuses me! I mean how does RIA services execute transactions and how can we specify transaction options? I mean, suppose on the client in Silverlight we specify something like this :-
someContext.Add(someEntity1);
someContext.Add(someEntity2);
someContext.Add(someEntity3);
Now when i call someContext.SubmitChanges() this is going to call InsertSomeEntity() on the server in my domain service class. What is the guarantee that all three records will be inserted into the database and if one fails all of them fails? And how can we change these options?

Chand's link has a good example. WCF RIA will submit a ChangeSet for the SubmitChanges containing all 3 Add's. In your DomainService, you can override the PersistChanges method to complete the transaction.
public class SomeEntityDomainService : DomainService
{
SomeEFContext _someEFContext;
public SomeEntityDomainService()
{
_someEFContext = new SomeEFContext();
}
public void InsertSomeEntity(SomeEntity someEntity)
{
// Called 3 times in your example
_someEFContext.SomeEntities.Add(someEntity);
}
protected override bool PersistChangeSet()
{
// Called exactly once per SubmitChanges() in Silverlight
_someEFContext.SaveChanges();
}
}
All of this happens in one request from the client to the server, not 3 requests.

Related

Where to implement Entity Framework database transactions in modern web applications?

Let’s assume that the primary components in your application are an Angular client, which calls an ASP.NET Web API, which uses Entity Framework to perform CRUD operations on your database. So, for example, in your API controllers, the Post (Add) method adds a new entity to the database context and then commits it to the database by calling the Entity Framework SaveChanges method.
This works fine when only one record needs to be added to the database at a time.
But, what if, for example, you want to add several records of different entity types to your database in one transaction? Where do you implement the Database.BeginTransaction and Database.CommitTransaction/RollbackTransaction? If you add a service layer to accomplish this, then what does the Angular client call?
PLEASE SEE BELOW FOR FURTHER DETAIL AND QUESTIONS.
I want to provide more detail about my current approach to solving this problem and ask the following questions:
(1) Is this a good approach, or is there a better way?
(2) My approach does not port to .NET Core, since .NET Core does not support OData yet (see https://github.com/OData/WebApi/issues/229). Any thoughts or ideas about this?
I have stated the problems that I faced and the solutions that I chose below. I will use a simple scenario where a customer is placing an order for several items – so, there is one Order record with several OrderDetail records. The Order record and associated OrderDetail records must be committed to the database in a single transaction.
Problem #1: What is the best way to send the Order and OrderDetail records from the Angular client to the ASP.NET Web API?
Solution #1: I decided to use OData batching, so that I could send all the records in one POST. I am using the datajs library to perform the batching (https://www.nuget.org/packages/datajs).
Problem #2: How do I wrap a single transaction around the Order and OrderDetail records?
Solution #2: I set up an OData batch endpoint in my Web API, which involved the following:
(1) In the client, configure a batch request route.
// Configure the batch request route.
config.Routes.MapODataServiceRoute(
routeName: "batch",
routePrefix: "batch",
model: builder.GetEdmModel(),
pathHandler: new DefaultODataPathHandler(),
routingConventions: conventions,
batchHandler: new TransactionalBatchHandler(GlobalConfiguration.DefaultServer));
}
(2) In the Web API, implement a custom batch handler, which wraps a database transaction around the given OData batch. The batch handler starts the transaction, calls the appropriate ODataController to perform the CRUD operation, and then commits/rolls back the transaction, depending on the results.
/// <summary>
/// Custom batch handler specialized to execute batch changeset in OData $batch requests with transactions.
/// The requests will be executed in the order they arrive, that means that the client is responsible for
/// correctly ordering the operations to satisfy referential constraints.
/// </summary>
public class TransactionalBatchHandler : DefaultODataBatchHandler
{
public TransactionalBatchHandler(HttpServer httpServer)
: base(httpServer)
{
}
/// <summary>
/// Executes the batch request and wraps the execution of the whole changeset within a transaction.
/// </summary>
/// <param name="requests">The <see cref="ODataBatchRequestItem"/> instances of this batch request.</param>
/// <param name="cancellation">The <see cref="CancellationToken"/> associated with the request.</param>
/// <returns>The list of responses associated with the batch request.</returns>
public async override Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(
IEnumerable<ODataBatchRequestItem> requests,
CancellationToken cancellation)
{
if (requests == null)
{
throw new ArgumentNullException("requests");
}
IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
try
{
foreach (ODataBatchRequestItem request in requests)
{
OperationRequestItem operation = request as OperationRequestItem;
if (operation != null)
{
responses.Add(await request.SendRequestAsync(Invoker, cancellation));
}
else
{
await ExecuteChangeSet((ChangeSetRequestItem)request, responses, cancellation);
}
}
}
catch
{
foreach (ODataBatchResponseItem response in responses)
{
if (response != null)
{
response.Dispose();
}
}
throw;
}
return responses;
}
private async Task ExecuteChangeSet(
ChangeSetRequestItem changeSet,
IList<ODataBatchResponseItem> responses,
CancellationToken cancellation)
{
ChangeSetResponseItem changeSetResponse;
// Since IUnitOfWorkAsync is a singleton (Unity PerRequestLifetimeManager) used by all our ODataControllers,
// we simply need to get a reference to it and use it for managing transactions. The ODataControllers
// will perform IUnitOfWorkAsync.SaveChanges(), but the changes won't get committed to the DB until the
// IUnitOfWorkAsync.Commit() is performed (in the code directly below).
var unitOfWorkAsync = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnitOfWorkAsync)) as IUnitOfWorkAsync;
unitOfWorkAsync.BeginTransaction();
// This sends each request in the changeSet to the appropriate ODataController.
changeSetResponse = (ChangeSetResponseItem)await changeSet.SendRequestAsync(Invoker, cancellation);
responses.Add(changeSetResponse);
if (changeSetResponse.Responses.All(r => r.IsSuccessStatusCode))
{
unitOfWorkAsync.Commit();
}
else
{
unitOfWorkAsync.Rollback();
}
}
}
You do not need to implement Database.BeginTransaction and Database.CommitTransaction/RollbackTransaction if you are using Entity Framework. Entity Framework implements UnitOfWork. The only thing that you should care about is to work with a different instance of DbContext for every web request, but exaclty 1 instance for 1 request and call SaveChanges only 1 time when you made all the changes you need.
In case of any Exception during SaveChanges all the changes will be rolled back.
The angular client should not care about this, it only sends the data and checks if everything was fine.
This is very easy to do if you use an IoC framework, like Unity and let your DbContext injected in your Controller or Service.
In this case you should use the following settings (if you use Unity):
container.RegisterType<DbContext, YourDbContext>(new PerRequestLifetimeManager(), ...);
Then you can do this if you want to use it in a Controller:
public class YourController : Controller
{
private YourDbContext _db;
public YourController(DbContext context)
{
_db = context;
}
...
No need to over-complicate things. Add the code to the WebApi project. Pass around your Transaction object and re-use it. See https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx for an example.

WCF RIA Services DomainService error: ContractDescription has zero operations; a contract must have at least one operation

I am developing a small instant messaging application that makes use of few DomainServices on the server side. Trying to access the domain service URL, I encounter the following error:
"ContractDescription 'AppInitService' has zero operations; a contract must have at least one operation".
The domain service Url is this one:
http://givemeword.net/chat/Services/IM-Chat-UI-Web-DomainServices-AppInitService.svc
You can find the domain service class below:
namespace Chat.UI.Web.DomainServices
{
[EnableClientAccess()]
public class AppInitService : DomainService
{
private System.Security.Principal.IPrincipal _user;
private readonly Chat.UI.Web.Services.AppInitService _appInitService;
public AppInitService()
{
_appInitService = new Chat.UI.Web.Services.AppInitService();
}
public InitUserSettingsDTO InitUserSettings(Guid userId)
{
var initUserSettingsDTO = new InitUserSettingsDTO();
return initUserSettingsDTO;
}
}
}
As you can see, I am using a complex type as the return type of the only function of the domain service.
What I can not figure out is why on my testing Windows Server 2012 (not a development machine, just a virtual machine used for testing) or on my development machine everything runs without any problem, but on the hosting account it raise the error above.
Does anyone has any idea about this?
Thank you
Try adding the [Invoke] attribute to your InitUserSettings method:
[Invoke]
public InitUserSettingsDTO InitUserSettings(Guid userId)
{
var initUserSettingsDTO = new InitUserSettingsDTO();
return initUserSettingsDTO;
}
Make sure your web.config is set up as described here: http://msdn.microsoft.com/en-us/library/ff426912(v=vs.91).aspx
Being exasperated with this strange situation and the low support I received from the web hosting company, I have tried the same thing with another web provider. As I was thinking, it was working this time with no problems, so my assumption that the original provider had poor support for WCF RIA Services (or maybe other unidentified problem) was correct.

How to ensure unique column value inside code?

I am learning ASP.NET MVC, and confused as to how can I ensure unique values for columns (username & email) for a table.
Can anybody help me with a sample code or a link to the tutorial which shows & explains this?
EDIT:
I know that I can apply an unique key constraint on my table columns and achieve it. However, I want to know how can I achieve it via ASP.NET MVC code?
UPDATE:
I wish to do a check in my application such that no duplicated values are passed to DAL, i.e. achieve a check before inserting a new row.
Mliya, this is something you are controlling at the database level, not at the application level.
If you are designing a database with a users table in which you would like to constraint the username and email columns to be UNIQUE you should create a UNIQUE INDEX on such columns.
without knowing your backend database (mySQL, SQL Server, MS Access, Oracle...) it's not the case to show you pictures or tell much more, just create the table with the designer and add these unique constraints to those columns and by design you will be sure no duplicated values will ever be inserted for username and email.
I also suggest you to create an ID column which would be set as PK (primary key, which means it will be automatically set as NON NULL and UNIQUE).
From your ASP.NET MVC application you should of course make sure that no duplicated values are then passed to the DAL for username and email. You could do this in different ways, the easiest is probably to check before inserting a new row if any user already exists with that username and/or email and if so you can show a notification message telling the user to please select another pair of values.
In an ASP.NET MVC architecture, you should try to do most of your validation in the Model, but with low-level validation rules like these, it's sometimes impossible. What you should look to for answers then is Domain-driven Design (DDD) where Application Services can solve such low-level needs.
Application Services will have access to the database (either directly, or better yet; indirectly through repositories) and can perform low-level validation and throw ValidationException or something similar (with detailed information the Controller can act upon and respond to the user) when a prerequisite or business rule isn't met.
S#arp Architecture implementes all of this in a best-practice framework that you can use as a basis for your ASP.NET MVC applications. It is highly opinionated towards DDD principles and NHibernate, and it will sometimes force your hand on how you do stuff, which is kind of the point. The most important part about it is that it learns you how to deal with these kinds of problems.
To answer your question more concretely and in the spirit of DDD, this is how I would solve it:
public class UserController
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
// The IUserService will be injected into the controller with
// an "Inversion of Control" container like NInject, Castle Windsor
// or StructureMap:
this.userService = userService;
}
public ActionResult Save(UserFormModel userFormModel)
{
if (userFormModel.IsValid)
{
try
{
// Mapping can be performed by AutoMapper or some similar library
UserDto userDto = Mapper.Map<UserDto>(userFormModel);
this.userService.Save(userDto);
}
catch (ValidationException ve)
{
ViewBag.Error = ve.Detail;
}
}
// Show validation errors or redirect to a "user saved" page.
}
}
public class UserService : IUserService
{
private readonly IUserRepository userRepository;
public UserService(IUserRepository userRepository)
{
// The IUserRepository will be injected into the service with
// an "Inversion of Control" container like NInject, Castle Windsor
// or StructureMap:
this.userRepository = userReposityr;
}
public UserDto Save(UserDto userDto)
{
using (this.userRepository.BeginTransaction())
{
if (!this.userRepository.IsUnique(userDto.UserName))
{
// The UserNameNotUniqueValidationException will inherit from ValidationException
// and build a Detail object that contains information that can be presented to
// a user.
throw new UserNameNotUniqueValidationException(userDto.UserName);
}
userDto = this.userRepository.Save(userDto);
this.userRepository.CommitTransaction();
return userDto;
}
}
}

Duplex + Silverlight + WCF + Pulling data

I've been looking for it, for quite and while, and I didn't really find anything that cover how to pull data trough duplex connection for Silverlight (pollingHttpDuplex).
I have setup basic sub/pub application with duplex.
Now I wanted to get list of topics, that are users are connected to. My first thought, was to setup simple DataContract (with only one field TopicName), then get data from Dicionary that I've been using to store current topics.
So I end up with something like this:
[OperationContract]
public List<Topic> GetTopicList()
{
List<Topic> topicList;
topicList = (from p in _sessionIDTopic
select new Topic
{
TopicName = p.Value
}).ToList<Topic>();
return topicList;
}
[DataContract]
public class Topic
{
[DataMember]
public string TopicName { get; set; }
}
I'm doing something wrong here. Because big question is how to send this to the client (Silverlight 4), and bind it to controls ?
Also duplex is essential for application. Changing it for anything else is not an option.
Using a duplex connection here doesn't have any effect on how you get the data to the client. The example you specified looks like simple one way communication. To do that, your client project needs a service reference to the server, and the generated proxy will provide the methods the client needs to access the operations on the server.
I think you should start by looking at a simple WCF example project to see what I mean. If this is not your problem, please rephrase the question.

Integration tests with Entity Framework 4 Code First using SQL Server CE 4 or SQLite

I would like to start with integration testing. I am using an ASP.NET MVC 3 app. And I am using Entity Framework 4 Code First CTP5. My integration tests to the database is in a separate project something like MyProject.Data.IntegrationTests.
I am planning on using SQL Server CE 4 or SQLite. Any recommendations/tips/opinions on using any one of these for what I am trying to accomplish?
Does anyone know of any decent articles that I can read on what I am trying to accomplish? And help/feedback would be appreciated.
I am using SQL Server 2008 for my database. But when testing my repositories I would like to test them against one of these database mentioned above, so I will need to specify the connection string.
UPDATE
I work from a service layer (called from my controller) and then the service layer will call my repository. For examples, below is how I would add a news item:
Service class:
public class NewsService : INewsService
{
private INewsRepository newsRepository;
public NewsService(INewsRepository newsRepository)
{
this.newsRepository = newsRepository;
}
public News Insert(News news)
{
// Insert news item
News newNews = newsRepository.Insert(news);
// Insert audit entry
// Return the inserted news item's unique identifier
return newNews;
}
}
Repository class:
public class NewsRepository : INewsRepository
{
MyContext context = new MyContext();
public NewsRepository()
{
}
public News Insert(News news)
{
int newsId = context.Database.SqlQuery<int>("News_Insert #Title, #Body, #Active",
new SqlParameter("Title", news.Title),
new SqlParameter("Body", news.Body),
new SqlParameter("Active", news.Active)
).FirstOrDefault();
news.NewsId = newsId;
// Return the inserted news item
return news;
}
}
I am using Entity Framework 4 Code First CTP5 and NUnit. Does NUnit has something similar to the roll back in XUnit?
If you use a testing framework like XUnit (http://xunit.codeplex.com/), it comes with a feature called [AutoRollback] and that will rollback the transaction ran in the test so none of your data will change!
As far as how to setup the tests, I need to see more of how you setup your data access. Did you use the Repository Pattern? (Entity Framework 4 CTP 4 / CTP 5 Generic Repository Pattern and Unit Testable). If I could see some of your code it would help. Below is a sample integration test with XUnit:
private readonly IUserRepository _repository;
public UserRepositoryTests()
{
_repository = new UserRepository(base._databaseFactory);
}
[Fact, AutoRollback]
public void Should_be_able_to_add_user()
{
var user = new User{Name = "MockName"};
_repository.Add(user);
base._unitOfWork.Commit();
Assert.True(user.Id > 0);
}
So the above test adds a user to my database, then checks its Id property to check that SQL Server auto generated an Id for it. Because the method is decorated with the AutoRollback attribute, the data is then removed from my database after the method ends!

Resources