MVC with nested actions, WCF Data Service with EF5, and DataServiceQuery<>.Expand() - c#

Consider this code:
var svc = new ProductDesignerEntities(AppSettings.ProductDesignerServiceLocation);
var scenariox = svc.Scenarios
.Where(o => o.ID == id)
.FirstOrDefault();
var scenario = svc.Scenarios
.Expand(o => o.SomeNavigationProperty)
.Where(o => o.ID == id)
.FirstOrDefault();
When this code runs, scenario.SomeNavigationProperty will be unpopulated. Commenting out the code which populates scenariox fixes it. Why is this? Is it possible to fix this through configuration of the service context or the data service, or will I have to change the design of my code in some fashion?
The alternatives that I can think of are all inferior in some capacity:
Create a ProductDesignerEntities instance per action. This kills within-request caching almost entirely.
Create a ProductDesignerEntities instance per controller. Less annoying with a base controller class, but this kills caching between controllers, and wouldn't fix the issue if different actions in the same controller needed different sets of navigation properties from the same table.
Manually populate the property if it is empty. Difficult to consistently implement a manual solution like this; bound to be a source of bugs.
Always manually populate the property. Kind of defeats the purpose of navigation properties.
Ensure that all uses of the table within a request have the same .Expand() list. Extremely annoying and begging for hard-to-fix bugs.
I'm starting to lean toward the first alternative. It seems to have the least issues, despite the additional overhead of a new connection for each instance :/
EDIT: I managed to get the service queries to run through Fiddler, and they look correct. But the navigation property on scenario still winds up empty unless I comment out the scenariox code.

After doing further research, it appears that you're supposed to create a new ProductDesignerEntities instance within each use context anyway, so that's the solution I'll be going with. It bugs me a lot that I wasn't able to properly fix the problem, though!
I'm going to write it off as either a very weird configuration issue, or a bug in WCF data services, because according to Fiddler, it does actually perform the request for the additional data; it just doesn't show up in the returned object.

Related

How can I get structuremap to inject a specific instance given a specific requesting class?

I thought I had this figured out with the object model, but apparently I keep piecing bits together improperly. "queueName" is a Func in the parent method, which is just returning a string ultimately.
I'm adding dependencies with the following:
conf.For<IMessageQueueFactory<TQueue>>()
.Use<MicrosoftMessageQueueFactory<TQueue>>()
.Named(instanceName)
.Ctor<string>("path")
.Is(context => queueName(context.GetInstance<IProjectSettings>()))
.Ctor<QueueAccessMode>("queueAccessMode")
.Is(QueueAccessMode.SendAndReceive)
.Ctor<int>("readTimeoutMilliseconds")
.Is(60000);
conf.For<IMessageQueueReader<TQueue>>()
.Use(context => context.GetInstance<IMessageQueueFactory<TQueue>>().CreateMessageQueueReader())
.Named(instanceName + ".reader");
conf.For<IMessageQueueWriter<TQueue>>()
.Use(context => context.GetInstance<IMessageQueueFactory<TQueue>>().CreateMessageQueueWriter())
.Named(instanceName + ".writer");
This block of code is called about 6 times with different queue configurations, because for the most part TQueue is a different object. We have a case where TQueue is the same (one is an initial production, the other is as an error-handling queue for the other 5 areas), so I need to control which one is injected, which is why the instances are all named.
conf.For<IMessageQueueReader<CopiedObject>>()
.ConditionallyUse(a => a.If(c => typeof(FooEngine).IsAssignableFrom(c.ParentType))
.ThenIt.Is.TheInstanceNamed("queue.copiedobject.writer"));
conf.For<IMessageQueueWriter<CopiedObject>>()
.ConditionallyUse(a => a.If(c => typeof(BarEngine).IsAssignableFrom(c.ParentType))
.ThenIt.Is.TheInstanceNamed("queue.copiedobject.reader"));
Every *Engine object I have in this has an IQueueReader and an IQueueWriter being injected. I'm trying to override just the reader of one engine and the writer of another engine, and let everything else be "defaults". What am I missing?
EDIT: One thing I'm missing is apparently the error message I get -- which of all things is a "bidirectional reference". Absolutely nothing about an IMessageQueueReader or IMessageQueueWriter requires anything I'm writing, as those aren't even in the same assembly. Is it the c.ParentType part requiring a partial instantiation?
EDIT2: Changing the overrides to the following code stops the error, but StructureMap isn't using the overridden objects. It's still using the defaults:
conf.ForConcreteType<FooEngine>().Configure
.Ctor<IMessageQueueWriter<FetchedEmail>>()
.Is(i => i.TheInstanceNamed("queue.copiedobject.writer"));
conf.ForConcreteType<BarEngine>().Configure
.Ctor<IMessageQueueReader<FetchedEmail>>()
.Is(i => i.TheInstanceNamed("queue.copiedobject.reader"));

Entity Set Name Changing Randomly

I have a local and development environment, each very similar to each other (OS differences aside). They both run the same project which is making use of the Entity Framework. I use LINQpad quite a bit to interrogate data on both environments -- but where required I have access to SQL etc. etc.
So, this is all started with a very strange occurance. Within my business logic layer, I make a call to get a list of Contacts and then marshall that list into a custom type. The custom-type calls for an Initial index based on the name of each of the contacts.
For reference, this is the code that performs the marshalling:-
private static IEnumerable<AlphabetisedContact> _getGroupedContacts(int clientid)
{
return _getLiteContacts(clientid).GroupBy(c => c.Name[0]).Select(
g => new AlphabetisedContact { Initial = g.Key, Contacts = g.ToList() }).OrderBy(g => g.Initial);
}
So, this all seems to work fine. Except, it never returns any Contact with the first initial of a. I decided to try and debug this and using LINQpad found a weirdness. Whether this has anything to do with my code not returning a contacts I don't know (??), but this was the weirdness I found:-
Local Machine:-
Development Machine:-
For the less eagle-eyed of you, the Entity Set Name returned by EF, seems to be different. With the development machine, they're returned with underscores between words -- which is not how the EDMX was setup. For example, it is Name on local, Contact_name on dev. Again, this may have absolutely nothing to do with why I can't get a contacts. I don't get errors on the dev box, contacts are returned etc. etc. but I can't get a contacts.
Can anyone offer some assistance/advice/guidance on how to fix this? It's become a case of "wood-for-the-trees" now...
Help appreciated.
Turns out this was a catastrophic boo-boo on my part. Please see answer to this on this SO:
Why Won't This LINQ Return A Particular Initial?
Thanks to all who took the time to take a peek - and at least a stab at answer.
;)

Trying to separate logic from controller in an MVC Entity Framework app - What is happening here?

Based on my last question, I have tried to separate the business logic from my controller completely.
This however has left a problem which I understand why, but not how to fix.... And, I do not understand why it is doing what is doing.
In my controller, I had the following:
public User GetCurrentUser()
{
User user = db.Users.SingleOrDefault(x => x.UserName == User.Identity.Name);
return user;
}
I now know about [NonAction] which fixes the security concern - however, I know this doesn't follow best practices of not having any non controller stuff in a controller. For this reason, I moved to a new class and modified it to the following:
public User GetCurrentUser(string name)
{
User user = db.Users.SingleOrDefault(x => x.UserName == name);
return user;
}
I have an edit method which before simply set various fields in the user object, then called db.SaveChanges(). This however is now causing issues - I believe it is due to calling the command on a db object that doesn't actually have the object loaded.
But, the part that I really do not understand is when I am redirected back to the home page and perform GetCurrentUser() again, I am presented with the edited details I changed... These are not stored in the database and it is only when I restart the application it goes back to the database results.
I am finding this very confusing! What is happening here and where are the object being stored?
And, how do I fix it? I have tried making the new Class's db function public and calling it's SaveChanges() method, but, this is resulting in the same problem - data that is not being saved to the database.
Anyway, quite frankly, I really liked calling it via just GetCurrentUser(), I wanted this due to the fact I wanted to change the way the user was loaded in the future - but, now that I have to call it via GetCurrentUser(User.Identity.Name), and make other modifications, I think it wouldn't be that much harder to just skip on the method and call the Lambda query directly instead... It just seems it will save a lot of trouble.
Based on the detail in your question, you need to make sure you attaching your Entity object e.g db.Users.Attach(updatedUser)
And then change its state
e.g db.ObjectStateManager.ChangeObjectState(updatedUser, EntityState.Modified)
Before you call db.SaveChanges()
The edit functions I've written for my MVC app usually have one more line of code before I call SaveChanges:
_db.ApplyCurrentValues(OriginalEntity.EntityKey.EntitySetName, NewEntity);
_db.SaveChanges();
Also, maybe I'm missing something but wouldn't this (below) be a simpler way to update the user information in the database?
Membership.UpdateUser();
As for the reason why the non-database data is still showing up for you, I think that is because when you call GetCurrentUser it caches information on the client side. I'm sure someone with more experience here can give a more detailed (or more correct answer) on that part.

C# NHibernate with Spring LazyInitializationException when using the data

I'm working on an NHibernate project, and where I had trouble loading collections earlier (http://stackoverflow.com/questions/4213506/c-hibernate-criteria-loading-collection), I now have problems using data.
I'm using C# in combination with the NHibernate and Spring.Net framework, and I get an LazyInitializationException after I load for instance an 'ordercredit', and then accessing an object of the ordercredit.
I use this code for getting the OrderCredit:
OrderCredit oc = CreditService.getOrderCredit(ordercredit.Id);
The code I use for loading is done using a DAO implementation:
[Transaction(TransactionPropagation.Required, ReadOnly = true)]
public OrderCredit GetOrderCredit(long ordercreditid)
{
var creditrules = Session.CreateCriteria(typeof(OrderCredit));
creditrules.Add(Restrictions.Eq("Id", ordercreditid));
return creditrules.List<OrderCredit>()[0];
}
When I run this on my local machine, everything works fine, and I actually intended to load a list of those 'ordercredits', but that went wrong as well, so I tried a simpler step first.
The objects within the 'OrderCredit' are defined as [OneToMany].
When I put this on the testserver, and try to access the 'OrderObject' object of the loaded OrderCredit, I get the error:
NHibernate.LazyInitializationException: Initializing[.OrderObject#5496522]-Could not initialize proxy - no Session.
Code that fails:
Log.Debug(oc.OrderObject.Name);
Code that works:
Log.Debug(oc.Id);
This happens for any object that's part of the OrderCredit, but I am able to access the property fields of the OrderCredit (for instance the OrderCredit.Id).
Also, when I access any of the objects BEFORE I return the data to the original function calling the method, then it does cache the information or so, as I can access it then.
I've read a lot about this problem, like turning off Lazy, but that did not work for me either (or I did that on the wrong place).
The thing that frustrates me most, is the fact that it actually does work on my local machine, and not on the testserver. What could I be doing wrong?
Any help is highly appreciated.
1st update:
I am using now a GenericDao, using the default method of loading 1 ordercredit. I use the following code to load 1 ordercredit by Id.
OrderCredit oc = GenericService.Load<OrderCredit>(Id);
The code that's inside the GenericDAO is the following, BUT it does not end or breaks the session, which means I am able to access the objects attached to the ordercredit:
[Transaction(TransactionPropagation.Supports, ReadOnly = true)]
public T Load<T>(long id) where T : ISaveableObject
{
var obj = Session.Load<T>(id);
return obj;
}
This is nearly the same code as I had in the function which I included earlier in this question.
I'm now really confused because I don't know what it could be that ends the session. I will work with it now as it works, but I want to change it later on, so I can use my function to call the entire collection and access them via a for each loop.
Currently, I use my 'getOrderCredits' function to get the list of OrderCredit objects, and in the foreach, I get the Id, and use the GenericDao.Load to get the actual item, and can access the objects and such. Of course this is not the way it should be and needs to be.
I'd be amazed if I get this solved.
This is a common problem people have when using NHibernate. It happens because:
You open a session
You load an entity from the database which references another entity
You close the session
You try to access a property on your referenced entity
NHibernate tries to lazily load the entity from the database using the same session that loaded the parent entity
The session is closed, so NHibernate throws exceptions like woah.
You have a few options here:
Keep your session open longer, preferably using something like the unit of work pattern, which will give you tighter control.
Eagerly load your referenced entities when you query:
In your case as spring is managing your transaction for you the 2nd option is probably the quickest/easiest solution.
var creditrules = Session.CreateCriteria(typeof(OrderCredit));
creditrules.Add(Restrictions.Eq("Id", ordercreditid))
.SetFetchMode("OrderObject", FetchMode.Eager);
This will load the OrderObject when you load the OrderCredit.

LinqToSql static DataContext in a web application

In a web application that I have run across, I found the following code to deal with the DataContext when dealing with LinqToSQL
public partial class DbDataContext
{
public static DbDataContext DB
{
get
{
if (HttpContext.Current.Items["DB"] == null)
HttpContext.Current.Items["DB"] = new DbDataContext();
return (DbDataContext)HttpContext.Current.Items["DB"];
}
}
}
Then referencing it later doing this:
DbDataContext.DB.Accounts.Single(a => a.accountId == accountId).guid = newGuid;
DbDataContext.DB.SubmitChanges();
I have been looking into best practices when dealing with LinqToSQL.
I am unsure about the approach this one has taken when dealing with DataContext not being ThreadSafe and keeping a static copy of it around.
Is this a good approach to take in a web application?
#Longhorn213 - Based on what you said and the more I have read into HttpContext because of that, I think you are right. But in the application that I have inherited it is confusing this because at the beginning of each method they are requerying the db to get the information, then modifying that instance of the datacontext and submitting changes on it.
From this, I think this method should be discouraged because it is giving the false impression that the datacontext is static and persisting between requests. If a future developer thinks that requerying the data at the beginning of a method because they think it is already there, they could run into problems and not understand why.
So I guess my question is, should this method be discouraged in future development?
This is not a static copy. Note that the property retrieves it from Context.Items, which is per-request. This is a per-request copy of the DataContext, accessed through a static property.
On the other hand, this property is assuming only a single thread per request, which may not be true forever.
A DataContext is cheap to make and you won't gain much by caching it in this way.
I have done many Linq to Sql web apps and I am not sure if what you have would work.
The datacontext is supposed to track the changes you make to your objects and it will not do that in this instance.
So when you go hit submit changes, it will not know that any of your objects where updated, thus not update the database.
You have to do some extra work with the datacontext in a disconnected environment like a web application. It is hardest with an update, but not really that bad. I would not cache and just recreate it.
Also the context itself is not transactional so it is theoretically possible an update could occur on another request and your update could fail.
I prefer to create a Page base class (inherit from System.Web.UI.Page), and expose a DataContext property. It ensures that there is one instance of DataContext per page request.
This has worked well for me, it's a good balance IMHO. You can just call DataContext.SubmitChanges() at the end of the page and be assured that everything is updated. You also ensure that all the changes are for one user at a time.
Doing this via static will cause pain -- I fear DataContext will lose track of changes since it's trying to track changes for many users concurrently. I don't think it was designed for that.

Categories