I have a linq query and I am trying to put that in to a serializable object for a distributed caching (Velocity) but its failing due to a LINQ-to-SQL lazy list
like so
return from b in _datacontext.MemberBlogs
let cats = GetBlogCategories(b.MemberBlogID)
select new MemberBlogs
{
MemberBlogID = b.MemberBlogID,
MemberID = b.MemberID,
BlogTitle = b.BlogTitle,
BlogURL = b.BlogURL,
BlogUsername = b.BlogUsername,
BlogPassword = b.BlogPassword,
Categories = new LazyList<MemberBlogCategories>(cats)
};
LazyList is the same class Rob Conery uses in his MVC storefront...
all three classes are marked serializable (MemberBlogs,MemberBlogCategories,LazyList... any ideas?
If you are putting it in a distributed cache you will need to avoid the LazyList altogether. You can then call .ToList() around the whole LINQ statement as in:
(from x select new MemberBlogs).ToList()
This should then be cachable because it forces the queries to be evaluated.
I'm just guessing, but I'd say the problem is that it is serializing the query instead of the results; I don't know what the implementation of the LazyList looks like, but you can probably add an OnSerializing method that actually executes the query prior to serializing it; Something like:
[OnSerializing]
private void ExecuteLinqQuery(StreamingContext context)
{
if (!SomethingThatIndicatesThisLinqQueryHasNotBeenExecuted)
LinqVariable.ToList()
}
This way you get to keep the Lazy Load (for anything that doesn't go into your cache), but then also if it does hit the cache, it'll execute the linq query and cache the results.
If you're caching it why are you using a lazy list? Don't use a lazy list, use caching, and the problem goes away.
I know this is an old post but I had the same issue as I wanted to execute my LazyList and put them into the AppFabric Cache. I ended up putting some custom serialization logic into the LazyList type.
The first part now looks like this:
public class LazyList<T> : IList<T>, ISerializable
{
public LazyList()
{
this.query = new List<T>().AsQueryable();
}
public LazyList(SerializationInfo info, StreamingContext context)
{
try {
this.inner = (List<T>)info.GetValue("InnerList", typeof(List<T>));
}
catch (Exception ex)
{
this.inner = null;
}
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (this.inner != null)
info.AddValue("InnerList", this.inner.ToList());
}
public LazyList(IQueryable<T> query)
{
this.query = query;
}
public LazyList(List<T> l)
{
inner = l;
}
}
Related
I have a method where I READ objects from DB, for instance:
public Object getProduct(int categoryId, int productId)
{
DataClassesDataContext db = new DataClassesDataContext(Settings.getDefaultConnectionStringName());
switch (categoryId)
{
case CCategorii.CARTI_ID:
{
IEnumerable<Carti> product = (from c in db.Cartis
where c.Carti_id == productId
&& c.Vizibil == true
select c);
if (product.Count() != 0)
return product.First();
break;
}
//so on
}
}
Now I have another method where I do the update:
public void updateProduct()
{
Object productToBeUpdated = getProduct(1,1);
DataClassesDataContext db = new DataClassesDataContext(Settings.getDefaultConnectionStringName());
//update some properties of the product
productToBeUpdated.setQuantity(productToBeUpdated.getQuantity()+1);
db.submitChanges();
}
Well, the product was succcesfully read from previous method but changes were not done into the DB.
I think the cause is that I do this READ-UPDATE in two different DataContext...If this is the cause how do you threat this situations?
Oh yeah, I can read the product and update in the same method but this means to duplicate the method I use for reading and add to it update stuff... and I would like to avoid this.
I would assume it's because you are using a different context for the read and write. Try moving your DataClassesDataContext variable to class level.
One option is: use a common data context, and pass it to your getXXX methods as a parameter:
public Object getProduct(DataClassesDataContext db, int categoryId, int productId)
{
switch (categoryId)
{
case CCategorii.CARTI_ID:
{
IEnumerable<Carti> product = (from c in db.Cartis
where c.Carti_id == productId
&& c.Vizibil == true
select c);
if (product.Count() != 0)
return product.First();
break;
}
//so on
}
}
and then:
public void updateProduct()
{
using (DataClassesDataContext db = new DataClassesDataContext(Settings.getDefaultConnectionStringName()))
{
Object productToBeUpdated = getProduct(db, 1,1);
//update some properties of the product
productToBeUpdated.setQuantity(productToBeUpdated.getQuantity()+1); // THX #AVD, didn't notice that.
db.submitChanges();
}
}
You are using two different instances of your DataContext.
When implementing a web app, the best option is usually to align the lifetime of your DataContext to the lifetime of one http request. The lifetime you use is just too short.
Another option is to attach the object to the write DataContext:
db.Cartis.Attach(yourReadObject);
updateProperties(yourReadObject);
db.submitChanges();
EDIT
Ok, you have to detach the object from your other context first. See this article on how to do it.
But i really would recommend to use a single DataContext object and extend the lifetime to the httprequest scope.
This can be done really nice with an ioc container like autofac.
You can't use ++ operator and use the same context to update an object. Try this,
productToBeUpdated.setQuantity(productToBeUpdated.getQuantity()+1);
As soon as your DataContext goes out of scope your entity becomes detached from it. That means it's no longer being tracked by your Context and it can't save the changes you make to it.
You could share the context so the entity doesn't get detached from your context or you could reattach it to the second context (DataContext.Attach)
I have an object I want to update in the database. I'm new to EF but have done a fair bit of reading. Clearly my approach is wrong, but I don't understand why. FYI the Context referenced throughout is an ObjectContext which is newly instantiated as this code begins and is disposed immediately after. Here is my Update method - the View is the object I want to update in the database and it has 4 ICollection properties whose changes I also wish to save to the database:
public void Update(View view)
{
var original = Read(view.Username, view.ViewId);
original.ViewName = view.ViewName;
ProcessChanges<CostCentre, short>(Context.CostCentres, original.CostCentres, view.CostCentres, "iFinanceEntities.CostCentres", "CostCentreId");
ProcessChanges<LedgerGroup, byte>(Context.LedgerGroups, original.LedgerGroups, view.LedgerGroups, "iFinanceEntities.LedgerGroups", "LedgerGroupId");
ProcessChanges<Division, byte>(Context.Divisions, original.Divisions, view.Divisions, "iFinanceEntities.Divisions", "DivisionId");
ProcessChanges<AnalysisCode, short>(Context.AnalysisCodes, original.AnalysisCodes, view.AnalysisCodes, "iFinanceEntities.AnalysisCodes", "AnalysisCodeId");
int test = Context.SaveChanges();
}
First I get the original from the database because I want to compare its collections with the new set of collections. This should ensure the correct sub-objects are added and removed. I compare each collection in turn using this ProcessChanges method:
private void ProcessChanges<TEntity, TKey>(ObjectSet<TEntity> contextObjects, ICollection<TEntity> originalCollection, ICollection<TEntity> changedCollection, string entitySetName, string pkColumnName)
where TEntity : class, ILookupEntity<TKey>
{
List<TKey> toAdd = changedCollection
.Select(c => c.LookupKey)
.Except(originalCollection.Select(o => o.LookupKey))
.ToList();
List<TKey> toRemove = originalCollection
.Select(o => o.LookupKey)
.Except(changedCollection.Select(c => c.LookupKey))
.ToList();
toAdd.ForEach(a =>
{
var o = changedCollection.Single(c => c.LookupKey.Equals(a));
AttachToOrGet<TEntity, TKey>(entitySetName, pkColumnName, ref o);
originalCollection.Add(o);
});
toRemove.ForEach(r =>
{
var o = originalCollection.Single(c => c.LookupKey.Equals(r));
originalCollection.Remove(o);
});
}
This compares the new collection to the old one and works out which objects to add and which to remove. Note that the collections all contain objects which implement ILookupEntity.
My problems occur on the line where I call AttachToOrGet. This method I got from elsewhere on stackoverflow. I'm using this because I was often getting a message saying that "An object with the same key already exists in the ObjectStateManager" when attaching a new subobject. Hopefully you'll understand my confusion around this when I post the code of this method below:
public void AttachToOrGet<TEntity, TKey>(string entitySetName, string pkColumnName, ref TEntity entity)
where TEntity : class, ILookupEntity<TKey>
{
ObjectStateEntry entry;
// Track whether we need to perform an attach
bool attach = false;
if (Context.ObjectStateManager.TryGetObjectStateEntry(new EntityKey(entitySetName, pkColumnName, entity.LookupKey), out entry))
//if (Context.ObjectStateManager.TryGetObjectStateEntry(Context.CreateEntityKey(entitySetName, entity), out entry))
{
// Re-attach if necessary
attach = entry.State == EntityState.Detached;
// Get the discovered entity to the ref
entity = (TEntity)entry.Entity;
}
else
{
// Attach for the first time
attach = true;
}
if (attach)
Context.AttachTo(entitySetName, entity);
}
Basically this is saying if the entity is not already attached then attach it. But my code is returning false on the Context.ObjectStateManager.TryGetObjectStateEntry line, but throwing an exception on the final line with the message "An object with the same key already exists in the ObjectStateManager". To me this is paradoxical.
As far as I'm concerned I'm trying to achieve something very simple. Something it would take 20 minutes to write a stored procedure for. A simple database update. Frankly I don't care what is attached and what isn't because I don't wish to track changes or create proxies or lazy load or do anything else EF offers me. I just want to take a very simple object and update the database using a minimal number of trips between servers. How is this so complicated? Please someone help me - I've spent a whole day on this!
Update
Here's my ILookupEntity class:
public interface ILookupEntity<TKey>
{
TKey LookupKey { get; }
string DisplayText { get; }
}
Here's how it is implemented in CostCentre:
public partial class CostCentre : IFinancialCode, ILookupEntity<short>
{
#region IFinancialCode Members
public short ID { get { return CostCentreId; } }
public string DisplayText { get { return string.Format("{0} - {1}", Code, Description); } }
#endregion
#region ILookupEntity Members
public short LookupKey
{
get { return ID; }
}
#endregion ILookupEntity Members
}
Well, I've worked through this and found a solution, but I can't say I understand it. The crucial ingredient came when I was performing a check after the comment by #Slauma. I wanted to check I was using the correct entity set name etc so I included the following lines near the top of my AttachToOrGet method:
var key = new EntityKey(entitySetName, pkColumnName, entity.LookupKey);
object temp;
if (!Context.TryGetObjectByKey(key, out temp))
throw new Exception(string.Format("No entity was found in {0} with key {1}", entitySetName, entity.LookupKey));
Bizarrely this alone resolved the problem. For some reason, once I'd called the TryGetObjectByKey then the ObjectStateManager.TryGetObjectStateEntry call actually started locating the attached entity. Miraculous. I'd love it if anyone can explain this.
By the way, I also needed to include the following code, but that's just because in my case the modelled entities are located in a separate assembly from the context itself.
Assembly assembly = typeof(CostCentre).Assembly;
Context.MetadataWorkspace.LoadFromAssembly(assembly);
I have a method in a datalibrary that looks like this
public IEnumerable<GeneralContractLine> getContractLines(int GeneralContractID)
{
return db.GeneralContractLines.Where(l => l.FKGeneralContractID == GeneralContractID);
}
public void UpdateContractLine(GeneralContractLine line)
{
//Update the object "line"
}
The first method is ok, the db is just the datacontext object that is been initiliazied earlier.
The update method I would like to do something like:
db.GeneralContractLine.update(line);
db.submitChanges();
I know I can find the object, replace it then update, but are there any better way?
I can predict that you use the db object to get the GeneralContractLine object somewhere in your code and do some changes on it's properties and you want to save these updates so my solution for you is to just use the db.submitChanges() and all changes done on the GeneralContractLine object will be saved.
with Linq to SQL, you just change the object and then call SubmitChanges when you're done.
public void UpdateContractLine(GeneralContractLine line)
{
//Update the object "line"
line.PropertyX = "Foo";
db.SubmitChanges();
}
what you could do is create a extension method for the Table class for GeneralContractLine and add a method into it called Update().
Something Like
public static class LinqToSqlExtensions
{
public static void Update(this System.Data.Linq.Table<GeneralContractLine> table, GeneralContractLine item)
{
var connectedItem = table.FirstOrDefault(x => x.ID == item.ID);
//Update logic in this case updating only the name
connectedItem.Name = item.Name;
table.Context.SubmitChanges();
}
}
just as a not, i have not tested any of this but it may be something that is worth trying
Oh and if it works, calling it should just be a case of adding a using statement to the namespace and then calling db.GeneralContractLine.Update(line);
public static string MakeAnchor(string text)
{
string pattern = #"((?<Protocol>\w+):\/\/(?<Domain>[\w#][\w.:#]+)\/?[\w\.?=%&=\-#/$,]*)";
Regex r = new Regex(pattern, RegexOptions.Multiline);
var anchor = r.Replace(text, "$1");
return anchor;
}
This method returns <a href="http://google.com/" target="_blank">http://google.com/</a> if I pass it some text containing http://google.com/
instead of <a href="http://google.com/" target="_blank">http://google.com/</a>
I'm using razor views with MVC 3. I'm stumped. Is there some sort of new view encoding in MVC 3 that I'm unaware of?
If you use #: (in Razor) or <%: (in the WebForm View Engine) the text will be automatically html encoded. Try #= or <%=
Make your method return an IHtmlString to indicate to the framework that your output is already encoded.
The simplest implementation of that is return new HtmlString(anchor);
IHtmlString is implemented in .NET 4, so if you're in .NET 3.5, you'll have to use the #= or <%= syntax (as #Andrew mentions) to avoid encoding.
I found this blog post (http://www.akngo.com/) that shows how to "wrap" Htmlstring for convenience sake. Usage is RawView.SomeString
I have been using the Razor engine for
a few days now, and I am liking it a
lot. I noticed one big problem right
from the start, all strings are HTML
encoded. For our uses, this becomes
quite an annoying “feature” when we
deal with JSON strings. For example,
given a simple JSON string, ["1", "2",
"3", "4"]
The actual HTML encoded output of this
became, ["1", "2",
"3", "4"]
The quotes have been escaped, which is
not the desired output. If we need to
send HTML back out, or XML, we would
have to then individually escape
those. Overall, I find this quite
annoying as we would have to wrap it
inside of an HtmlString as shown in
this StackOverflow post.
In our scenario, we are generating
JSON/XML from C#, so most of the time,
our serialized data is inside of the
ViewModel. Which would result in us
doing things like, #(new
HtmlString(View.Foo)) #(new
HtmlString(View.Bar))
As you can see, this can become quite
cumbersome. So I decided to
plagiarized part of MVC to create a
RawView. In the end, the usage will
be, #RawView.Foo #RawView.Bar
I also went ahead to allow the usage
of the index operator as well in case
there’s a need to loop over it with a
list of keys. // Assuming we have Foo,
Bar, and Baz in our ViewModel #{
List keys = new List { "Foo", "Bar", "Baz" };
foreach (var key in keys) {
#RawView[key]
} }
To achieve this, I would need to
duplicate DynamicViewDataDictionary.
Naturally, I named it
DynamicRawViewDataDictionary, public
class DynamicRawViewDataDictionary :
DynamicObject
{
private readonly Func viewThunk;
public DynamicRawViewDataDictionary(Func
viewThunk)
{
this.viewThunk = viewThunk;
}
private ViewDataDictionary ViewData
{
get
{
ViewDataDictionary viewData = this.viewThunk();
return viewData;
}
}
public object this[string key]
{
set { setData(key, value); }
get { return getData(key); }
}
public override IEnumerable<string>
GetDynamicMemberNames()
{
return ViewData.Keys;
}
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
result = getData(binder.Name);
return true;
}
public override bool TrySetMember(SetMemberBinder binder,
object value)
{
setData(binder.Name, value);
return true;
}
private object getData(string key)
{
object value = ViewData[key];
if (value is string)
{
return new HtmlString(value.ToString());
}
return value;
}
private void setData(string key, object value)
{
ViewData[key] = value;
}
}
The reason for the duplication is
because the DynamicViewDataDictionary
is an internal sealed class. There was
no way I could get around this
restriction other than copying and
pasting. The obvious change I made to
this class is the getData method. I
checked to see if the object is a
string, if it is, I wrapped it with an
HtmlString().
Now I need to use this inside of a
WebViewPage, since all views , I have
to have a customized version of a
WebViewPage as well. Good thing that
MS didn’t make this class internal
sealed, so I can easily inherit from
it. I needed to make a generic and a
non-generic version, I just plopped
both of this inside of one file.
public abstract class
CustomWebViewPage :
WebViewPage
{
private DynamicRawViewDataDictionary _rawData;
public dynamic RawView
{
get
{
if (_rawData == null)
{
_rawData = new DynamicRawViewDataDictionary(() =>
ViewData);
}
return _rawData;
}
}
}
public abstract class CustomWebViewPage : WebViewPage
{
private DynamicRawViewDataDictionary _rawData;
public dynamic RawView
{
get
{
if (_rawData == null)
{
_rawData = new DynamicRawViewDataDictionary(() =>
ViewData);
}
return _rawData;
}
}
}
For the final touch to actually make
this work, at the top of the cshtml
file, you should have something like,
#inherits
Namespace.To.CustomWebViewPage
//or #inherits
Namespace.To.CustomWebViewPage
Now that the cshtml file inherits from
the new CustomWebViewPage, it should
have access to RawView, which will
wrap all strings with an HtmlString.
Once you have this in place, you’ll
still need to make sure that all of
your new views inherits from
CustomWebViewPage. I highly recommend
using custom code templates in your
MVC project to generate the view. This
will help with automating a lot of the
mundane tasks.
Htmlstring is the answer.
#(new HtmlString(SomeString))
I've created my own cache manager for a web site I'm developing and I was looking to find the best way to clear the cache under certain circumstances.
I found many articles saying the proper way to clear the cache is to call HttpRuntime.Close()
However, in my unit tests setup I call the encapsulated function HttpRuntime.Close() and the cache is NOT being cleared out.
I expected it to perform something similar to
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache)
{
HttpRuntime.Cache.Remove(cacheItem.Key.ToString());
}
The foreach loop works great in my encapsulated function, but the Close() never works right.
Am I misunderstanding the purpose of HttpRuntime.Close() or is there something more sinister going on here?
Don't use Close, it does more than the docs say. And the docs also say not to use it while processing normal requests...
This is the reflected source of Close():
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public static void Close() {
if (_theRuntime.InitiateShutdownOnce()) {
SetShutdownReason(ApplicationShutdownReason.HttpRuntimeClose, "HttpRuntime.Close is called");
if (HostingEnvironment.IsHosted) {
HostingEnvironment.InitiateShutdown();
} else {
_theRuntime.Dispose();
}
}
}
Also, you cannot iterate over a collection and remove items from it at the same time, as this renders the enumeration invalid.
So, try this instead, which doesn't change what it loops over:
List<string> toRemove = new List<string>();
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache) {
toRemove.Add(cacheItem.Key.ToString());
}
foreach (string key in toRemove) {
HttpRuntime.Cache.Remove(key);
}
That being said, really, you should try to use cache dependencies to have the invalid cache entries cleared automatically for you, and then all this becomes unnecessary.
I understand the issue with enumeration but for some reason the Cache doesn't seem to have a problem removing an item while walking through the list.
If you drill down to the detail implementation, you will find the Enumerator is created by CacheSingle.CreateEnumerator, a new Hashtable instance is created for enumeration.
That's why you can do the remove in a foreach loop.
you could simply implement your own Cache class, check the below one:
public sealed class YourCache<T>
{
private Dictionary<string, T> _dictionary = new Dictionary<string, T>();
private YourCache()
{
}
public static YourCache<T> Current
{
get
{
string key = "YourCache|" + typeof(T).FullName;
YourCache<T> current = HttpContext.Current.Cache[key] as YourCache<T>;
if (current == null)
{
current = new YourCache<T>();
HttpContext.Current.Cache[key] = current;
}
return current;
}
}
public T Get(string key, T defaultValue)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException("key should not be NULL");
T value;
if (_dictionary.TryGetValue(key, out value))
return value;
return defaultValue;
}
public void Set(string key, T value)
{
if (key == null)
throw new ArgumentNullException("key");
_dictionary[key] = value;
}
public void Clear()
{
_dictionary.Clear();
}
}
you could call items from cache or even clear them using the following:
// put something in this intermediate cache
YourCache<ClassObject>.Current.Set("myKey", myObj);
// clear this cache
YourCache<ClassObject>.Current.Clear();