I’m new to NHibernate.Search and i’ve ran into a problem I need a little help with.
I need to add a [DocumentId] attribute to my ID field, but the Id field is in the Entity class...I found some code on stackoverflow that done this inside my POCO:
[DocumentId]
public virtual int Id
{
get { return base.Id; }
protected set { base.Id = value; }
}
But when I run the UpdateModel() function in my controller, I get an exception:
Exception Details:
System.Reflection.AmbiguousMatchException:
Ambiguous match found.
I think the model binder is seeing 2 Id fields, however i’m not sure of the best way around this.
Paul
I'll answer my own question on this one after a tinkering around a little.
[DocumentId]
public override int Id
{
get
{
return base.Id;
}
protected set { base.Id = value; }
}
Paul
Related
I've started using ORMLite two days ago for refactoring an existing app....
I've some old stored procedure that returns columns with name that don't map 1:1 my dto object but I've managd to use [AliasAttribute] and it works fine.... at the same time I've some column that currently are mapped with some logic... for example
//Consider I've a dataset and I'm processing rows
int average = (int)row["AVERAGE"];
if(average > 50)
{
myDTO.Message = "Warning";
}
else
{
myDTO.Message = "OK";
}
Now we all agree it's not what it should be done at DataLayer but on that 5years old application we do so...is there a way I can tell in my DTO class (as I've done for Alias) to tell how to act when mapping the AVERAGE column?
Another question do ORM performs a trim on string or have I to perform it myself? again on some SP I've got no trim and I get something as "John DOE " ....now I do a .TrimEnd() when I got the value...
Thanks
Add the message as a property on your dto
public class MyDto
{
public int Average { get; set; }
public string Message
{
get { return Average > 50 ? "Warning" : "OK"; }
}
}
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);
Can someone please check out this code, i really dont understand why i got violation of unique when i try to update an record. the code used to create new record work just fine, but when i try to use it to update, it called out violation.
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(User user)
{
if (ModelState.IsValid)
{
userRepository.SaveUser(user);
return RedirectToAction("List");
}
else
return View("Edit");
}
userRepo:
public void SaveUser(User user)
{
user.LAST_ACTIVITY = DateTime.Now;
if (user.USER_ID != 0)
{
usersTable.Attach(user);
usersTable.Context.Refresh(RefreshMode.KeepCurrentValues, user);
}
else
{
usersTable.InsertOnSubmit(user);
}
usersTable.Context.SubmitChanges();
}
and i got an error:
Unable to refresh the specified
object. The object no longer exists
in the database.
when i try to change the userRepo like this for testing purpose.
public void SaveUser(User user)
{
user.LAST_ACTIVITY = DateTime.Now;
usersTable.Attach(user);
usersTable.Context.Refresh(RefreshMode.KeepCurrentValues, user);
usersTable.Context.SubmitChanges();
}
Im wondering if there anyone on this board can find out if i am wrong somewhere in this problem :).
Thank you very much and wish you best regard. :)
Looks like you have conflicts to resolve even though you're telling it to "KeepCurrentValues", try this before the submit changes...
foreach(ObjectChangeConflict conflict in usersTable.Context.ChangeConflicts)
{
foreach(MemberChangeConflict memberConflict in conflict.MemberConflicts)
{
memberConflict.Resolve(RefreshMode.KeepCurrentValues);
}
}
Ignore ID on your model binding.
[Bind(Exclude= "Id")]
public ActionResult Edit(User user)
Kindness,
Dan
I'm looking for a best practice solution that aims to reduce the amount of URLs that are hard-coded in an ASP.NET application.
For example, when viewing a product details screen, performing an edit on these details, and then submitting the changes, the user is redirected back to the product listing screen. Instead of coding the following:
Response.Redirect("~/products/list.aspx?category=books");
I would like to have a solution in place that allows me to do something like this:
Pages.GotoProductList("books");
where Pages is a member of the common base class.
I'm just spit-balling here, and would love to hear any other way in which anyone has managed their application redirects.
EDIT
I ended up creating the following solution: I already had a common base class, to which I added a Pages enum (thanks Mark), with each item having a System.ComponentModel.DescriptionAttribute attribute containing the page's URL:
public enum Pages
{
[Description("~/secure/default.aspx")]
Landing,
[Description("~/secure/modelling/default.aspx")]
ModellingHome,
[Description("~/secure/reports/default.aspx")]
ReportsHome,
[Description("~/error.aspx")]
Error
}
Then I created a few overloaded methods to handle different scenarios. I used reflection to get the URL of the page through it's Description attribute, and I pass query-string parameters as an anonymous type (also using reflection to add each property as a query-string parameter):
private string GetEnumDescription(Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
return attr.Description;
}
}
return null;
}
protected string GetPageUrl(Enums.Pages target, object variables)
{
var sb = new StringBuilder();
sb.Append(UrlHelper.ResolveUrl(Helper.GetEnumDescription(target)));
if (variables != null)
{
sb.Append("?");
var properties = (variables.GetType()).GetProperties();
foreach (var property in properties)
sb.Append(string.Format("{0}={1}&", property.Name, property.GetValue(variables, null)));
}
return sb.ToString();
}
protected void GotoPage(Enums.Pages target, object variables, bool useTransfer)
{
if(useTransfer)
HttpContext.Current.Server.Transfer(GetPageUrl(target, variables));
else
HttpContext.Current.Response.Redirect(GetPageUrl(target, variables));
}
A typical call would then look like so:
GotoPage(Enums.Pages.Landing, new {id = 12, category = "books"});
Comments?
I'd suggest that you derive your own class ("MyPageClass") from the Page class and include this method there:
public class MyPageClass : Page
{
private const string productListPagePath = "~/products/list.aspx?category=";
protected void GotoProductList(string category)
{
Response.Redirect(productListPagePath + category);
}
}
Then, in your codebehind, make sure that your page derives from this class:
public partial class Default : MyPageClass
{
...
}
within that, you can redirect just by using:
GotoProductList("Books");
Now, this is a bit limited as is since you'll undoubtedly have a variety of other pages like the ProductList page. You could give each one of them its own method in your page class but this is kind of grody and not smoothly extensible.
I solve a problem kind of like this by keeping a db table with a page name/file name mapping in it (I'm calling external, dynamically added HTML files, not ASPX files so my needs are a bit different but I think the principles apply). Your call would then use either a string or, better yet, an enum to redirect:
protected void GoToPage(PageTypeEnum pgType, string category)
{
//Get the enum-to-page mapping from a table or a dictionary object stored in the Application space on startup
Response.Redirect(GetPageString(pgType) + category); // *something* like this
}
From your page your call would be: GoToPage(enumProductList, "Books");
The nice thing is that the call is to a function defined in an ancestor class (no need to pass around or create manager objects) and the path is pretty obvious (intellisense will limit your ranges if you use an enum).
Good luck!
You have a wealth of options availible, and they all start with creating a mapping dictionary, whereas you can reference a keyword to a hard URL. Whether you chose to store it in a configuration file or database lookup table, your options are endless.
You have a huge number of options available here. Database table or XML file are probably the most commonly used examples.
// Please note i have not included any error handling code.
public class RoutingHelper
{
private NameValueCollecton routes;
private void LoadRoutes()
{
//Get your routes from db or config file
routes = /* what ever your source is*/
}
public void RedirectToSection(string section)
{
if(routes == null) LoadRoutes();
Response.Redirect(routes[section]);
}
}
This is just sample code, and it can be implemented any way you wish. The main question you need to think about is where you want to store the mappings. A simple xml file could do it:
`<mappings>
<map name="Books" value="/products.aspx/section=books"/>
...
</mappings>`
and then just load that into your routes collection.
public class BasePage : Page
{
public virtual string GetVirtualUrl()
{
throw new NotImplementedException();
}
public void PageRedirect<T>() where T : BasePage, new()
{
T page = new T();
Response.Redirect(page.GetVirtualUrl());
}
}
public partial class SomePage1 : BasePage
{
protected void Page_Load()
{
// Redirect to SomePage2.aspx
PageRedirect<SomePage2>();
}
}
public partial class SomePage2 : BasePage
{
public override string GetVirtualUrl()
{
return "~/Folder/SomePage2.aspx";
}
}
I'm doing a custom 404 page for a large website that's undergoing a redesign. There are about 40 high-use pages that customers may have bookmarked, and our new site structure will break these bookmarks.
On my custom 404 page, I want to alert them to the new URL if they attempt to navigate to one of these high-use pages via its old URL. So I have a couple of dynamic controls on the 404 page, one for a "did-you-want-this?" type of dialog, and another for a "if-so-go-here (and update your bookmark)" type of dialog. That's the easy part.
To suggest a new URL, I'm looking at the requested URL. If it has key words in it, I'm going to suggest the new URL based on that, and them I'm firing off the appropriate did-you-want..., and if-so... suggestions on the 404 page as mentioned above.
So I want to store these 40-ish key/value pairs (keyword/new-URL pairs) in a data structure, and I'm not sure what would be best. Dictionary? IDictionary? What's the difference and which is more appropriate?
Or am I totally on the wrong track?
Thanks for your time.
I would use the Dictionary<T,T> from the System.Collections.Generic namespace.
You could use NameValueCollection.
Maybe this is overkill for your use case, but I'd probably allow for multiple keywords per Uri, and a relative weight score. Then, dynamically score the keywords that match.
class UriSuggester {
private List<SuggestedUri> Uris { get; set; }
Uri[] GetSuggestions(Uri originalUri) {
var suggestionHits = new Dictionary<SuggestedUri, int>();
foreach (var keyword in KeyWords.Parse(originalUri)) {
// find suggestions matching that keyword
foreach (var suggestedUri in Uris.Where(u => u.Keywords.Contains(keyword)) {
// add a hit for keyword match
suggestionHits[suggestedUri] += 1;
}
}
// order by weight * hits
return suggestionHits.Keys
.OrderBy(s => s.Weight * suggestionHits[s])
.Select(s => s.Uri)
.ToArray();
}
}
class SuggestedUri {
public Uri Suggested { get; set; }
public int Weight { get; set; }
public Keyword[] Keywords;
}
class Keyword {
public string Value { get; set; }
public static Keyword[] Parse(Uri uri);
override Equals;
override GetHashCode;
}
Dictionary would be fine. Wether you store it as the interface type IDictionary or Dictionary itself wouldn't matter much in this case as it's not going to be passed much around, besides on the 404 page itself.
Have you considered doing some URL rewriting to still support the old URLs?
You can consider writing your own class logic and then assign that to a List data structure as following:
public class KeyValuesClass
{
private string a_key;
private string a_value;
public KeyValuesClass(string a_key, string a_value)
{
this.a_key = a_key;
this.a_value = a_value;
}
public string Key
{
get{ return a_key; }
set { a_key = value; }
}
public string Value
{
get{ return a_value; }
set { a_value = value; }
}
}
somewhere in the code
List<KeyValuesClass> my_key_value_list = new List<KeyValuesClass>();
my_key_value_list.Add(new KeyValuesClass("key", "value");
But you can consider Dictionary as our fellow programmer mentioned it above :)