Building a Repository Update method without attaching/detaching entities? - asp.net

I have the following update method, which seems to be working:
public Folder UpdateFolder(Folder folder)
{
Folder serverFolder = _db.Folders.FirstOrDefault(f => f.FolderId == folder.FolderId);
if (serverFolder != null)
{
serverFolder.Title = folder.Title;
serverFolder.Details = folder.Details;
}
SaveChanges();
return serverFolder;
}
I just noticed that most Update methods I've read about have calls to Entry.StateDetached and StateAttached - is what I've done above ok, or am I missing something?

It is okay. _db.Folders.FirstOrDefault reads the Folder and it is attached to the context.
You would need to check if the entity was attached if you were simply passing in a Folder (without reading) and attempting to update it.
Basically, Folder serverFolder = _db.Folders.FirstOrDefault(f => f.FolderId == folder.FolderId); ensures that the Folder is attached so you don't have to worry.

I think you need to do _db.SaveChanges(); instead of SaveChanges();

Related

Localization in ASP.NET MVC 4 using App_GlobalResources

I am trying to accomplish two things:
Localize the “built-in” error messages for “FieldMustBeDate” and "FieldMustBeNumeric".
Localize some of the other error messages you would encounter, for example, "PropertyValueRequired".
By using http://forums.asp.net/t/1862672.aspx/1 for problem 1 and MVC 4 ignores DefaultModelBinder.ResourceClassKey for problem 2 I have managed to get both working locally.
However as soon as I publish to a website, the “built-in” error messages defaults back to English while the other error messages stay localized.
I have read several places that using the App_GlobalResources should be avoided, however I am unable to accomplish problem 1 without using this.
I have created a .resx file with the name “WebResources.resx”, set the Build Action to “Embedded”, set the Copy to Output Directory to “Do no Copy”, set the Custom Tool to “PublicResXFileCodeGenerator” and set the Custom Tool Namespace to “Resources”.
The Project itself is set to only Publish files that are needed.
My Global.asax.cs contains the following (relevant) code:
ClientDataTypeModelValidatorProvider.ResourceClassKey = "WebResources";
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof(RequiredAttribute),
typeof(MyRequiredAttributeAdapter));
And the class MyRequiredAttributeAdapter contains the following code:
public class MyRequiredAttributeAdapter : RequiredAttributeAdapter
{
public MyRequiredAttributeAdapter(
ModelMetadata metadata,
ControllerContext context,
RequiredAttribute attribute
)
: base(metadata, context, attribute)
{
if (attribute.ErrorMessageResourceType == null)
{
attribute.ErrorMessageResourceType = typeof(Resources.WebResources);
}
if (attribute.ErrorMessageResourceName == null)
{
attribute.ErrorMessageResourceName = "PropertyValueRequired";
}
}
}
This is working locally however does anyone have any idea on how to get the "built in" messages to work after this is published?
Thank you for your help!
Best regards,
Andreas
I figured this one out myself. If you are trying to accomplish the above you must separate the localized error messages.
Create a *.resx file for the other error messages fx "PropertyValueRequired" and set the Build Action to “Embedded”, set the Copy to Output Directory to “Do no Copy”, set the Custom Tool to “PublicResXFileCodeGenerator” and set the Custom Tool Namespace to “Resources”.
In my case I have moved "PropertyValueRequired" to a file called LocalDanish.resx (still in the App_GlobalResources folder) and changed the line in my "MyRequiredAttributeAdapter" from
attribute.ErrorMessageResourceType = typeof(Resources.WebResources);
to
attribute.ErrorMessageResourceType = typeof(Resources.LocalDanish);
In order to get the "built in" error messages to work, you must create two *.resx files. I have created WebResources.resx and WebResources.da.resx. Do NOT change anything, leave the settings on them on default (Build Action to "Content", etc.). I guess the website automatically looks for the *.da.resx files in my case because I have set the globalization in my WebConfig:
<globalization uiCulture="da-DK" culture="da-DK"/>
Hope this helps anybody.
Best regards,
Andreas
I have made some minor additions to the original post, which didn't translate all messages in my case.
(String length and invalid property values)
Follow the above steps, to create the *.resx files, set their properties, and then set the locale in web.config, as described by Andreas.
Then create a couple of adapters:
// As described in original post:
public class LocalizedRequiredAttributeAdapter : RequiredAttributeAdapter
{
public LocalizedRequiredAttributeAdapter(
ModelMetadata metadata,
ControllerContext context,
RequiredAttribute attribute
)
: base(metadata, context, attribute)
{
if (attribute.ErrorMessageResourceType == null)
attribute.ErrorMessageResourceType = typeof(Resources.Resources);
if (attribute.ErrorMessageResourceName == null)
attribute.ErrorMessageResourceName = "PropertyValueRequired";
}
}
// Addition to original post:
public class LocalizedStringLengthAttributeAdapter : StringLengthAttributeAdapter
{
public LocalizedStringLengthAttributeAdapter(
ModelMetadata metadata,
ControllerContext context,
StringLengthAttribute attribute
)
: base(metadata, context, attribute)
{
if (attribute.ErrorMessageResourceType == null)
attribute.ErrorMessageResourceType = typeof(Resources.Resources);
if (attribute.ErrorMessageResourceName == null)
attribute.ErrorMessageResourceName = "StringLengthAttribute_ValidationError";
}
}
And in Global.asax.cx:
// Addition to original post: (Used for "PropertyValueInvalid")
DefaultModelBinder.ResourceClassKey = "Resources";
// As described in original post:
ClientDataTypeModelValidatorProvider.ResourceClassKey = "Resources";
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute), typeof(LocalizedRequiredAttributeAdapter));
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(StringLengthAttribute), typeof(LocalizedStringLengthAttributeAdapter));

How should I get and set a folder's group and user permission settings via Core Service?

I can get strings representing group and user permissions for a given folder with the following.
Code
// assumes Core Service client "client"
var folderData = client.Read("tcm:5-26-2", new ReadOptions()) as FolderData;
var accessControlEntryDataArray =
folderData.AccessControlList.AccessControlEntries;
Console.WriteLine(folderData.Title);
foreach (var accessControlEntryData in accessControlEntryDataArray)
{
Console.WriteLine("{0} has {1}",
accessControlEntryData.Trustee.Title,
accessControlEntryData.AllowedPermissions.ToString());
}
Output
Some Folder
Everyone has Read
Editor has None
Chief Editor has None
Publication Manager has None
Interaction Manager has None
T2011-CB-R2\areyes has All
[scope] Editor 020 Create has Read, Write
T2011-CB-R2\local1 has Read, Write, Delete
[rights] Author - Content has None
Seems like the four possible values for `AllowedPermissions are:
None
Read
Read, Write
Read, Write, Delete
All
This works great for my use case to create a folder permissions report. I can .Replace() these to a familiar notation (e.g. rw-- or rwdl).
But is manipulating these string values the right approach to set permissions as well? I'd imagine I'd want objects or maybe enums instead. Could someone point me in the right direction?
Also I noticed I get some, but not all non-applicable groups set as None. I don't specifically need them here, but I'm curious at what determines whether those get returned--did I miss something in my code?
Rights and Permissions are enums, indeed. You can set using the method below. If you want to set multiple rights you should do something like "Rights.Read | Rights.Write"
Keep in mind that this method will return you object that you have to save \ update \ create after
public static OrganizationalItemData SetPermissionsOnOrganizationalItem(
OrganizationalItemData organizationalItem,
TrusteeData trustee,
Permissions allowedPermissions,
Permissions deniedPermissions = Permissions.None)
{
if (organizationalItem.AccessControlList == null)
{
organizationalItem.AccessControlList
= new AccessControlListData
{AccessControlEntries = new AccessControlEntryData[0]};
}
var entries = organizationalItem.AccessControlList
.AccessControlEntries.ToList();
// First check if this trustee already has some permissions
var entry = entries.SingleOrDefault(
ace => ace.Trustee.IdRef == trustee.Id);
if (entry != null)
{
// Remove this entry
entries.Remove(entry);
}
entries.Add(new AccessControlEntryData
{
AllowedPermissions = allowedPermissions,
DeniedPermissions = deniedPermissions,
Trustee = new LinkToTrusteeData { IdRef = trustee.Id }
});
organizationalItem.AccessControlList.AccessControlEntries
= entries.ToArray();
return organizationalItem;
}

Saving changes of Entity Framework in Asp.Net

I have created an entity Appraiser and there are methods to select values, display data etc.
Now I want to save the changes made after data is displayed, I have a button named SAVE, which will be used to save changes.
I am not able to get how to save the changes of this Entity?
Entity name is Appraiser, and I have created methods like get AppriaserDetails etc in DAL, BL and used them in aspx.cs.
This is my code:
public void UpdateData(Appraiser appId)
{
var Appsave = context.Appraisers.FirstOrDefault(App => App.AppraiserId == appId.AppraiserId);
Appsave.AppraiserName = appId.AppraiserName;
Appsave.AppraiserPhones = appId.AppraiserPhones;
Appsave.AppraiserAppraiserCompanyId = appId.AppraiserAppraiserCompanyId;
Appsave.Address = appId.Address;
Appsave.City = appId.City;
Appsave.ProvinceState = appId.ProvinceState;
Appsave.Email = appId.Email;
context.SaveChanges();
}
If u want to insert new record, then can use
MyContext.Appraisers.AddObject(appraiserEntityObject);
MyContext.SaveChanges();
In case of update
if (appraiserEntityObject.EntityState == EntityState.Detached)
{
// In case of web, we got an existing record back from the browser. That object is not attached to the context yet.
MyContext.Appraisers.Attach(appraiserEntityObject);
MyContext.ObjectStateManager.ChangeObjectState(appraiserEntityObject, EntityState.Modified);
}
MyContext.SaveChanges();
Here MyContext is ur ObjectContext

Javascript permission denied error when using Atalasoft DotImage

Have a real puzzler here. I'm using Atalasoft DotImage to allow the user to add some annotations to an image. When I add two annotations of the same type that contain text that have the same name, I get a javascript permission denied error in the Atalasoft's compressed js. The error is accessing the style member of a rule:
In the debugger (Visual Studio 2010 .Net 4.0) I can access
h._rule
but not
h._rule.style
What in javascript would cause permission denied when accessing a membere of an object?
Just wondering if anyone else has encountered this. I see several people using Atalasoft on SO and I even saw a response from someone with Atalasoft. And yes, I'm talking to them, but it never hurts to throw it out to the crowd. This only happens in IE8, not FireFox.
Thanks, Brian
Updates: Yes, using latest version: 9.0.2.43666
By same name (see comment below) I mean, I created default annotations and they are named so they can be added with javascript later.
// create a default annotation
TextData text = new TextData();
text.Name = "DefaultTextAnnotation";
text.Text = "Default Text Annotation:\n double-click to edit";
//text.Font = new AnnotationFont("Arial", 12f);
text.Font = new AnnotationFont(_strAnnotationFontName, _fltAnnotationFontSize);
text.Font.Bold = true;
text.FontBrush = new AnnotationBrush(Color.Black);
text.Fill = new AnnotationBrush(Color.Ivory);
text.Outline = new AnnotationPen(new AnnotationBrush(Color.White), 2);
WebAnnotationViewer1.Annotations.DefaultAnnotations.Add(text);
In javascript:
CreateAnnotation('TextData', 'DefaultTextAnnotation');
function CreateAnnotation(type, name) {
SetAnnotationModified(true);
WebAnnotationViewer1.DeselectAll();
var ann = WebAnnotationViewer1.CreateAnnotation(type, name);
WebThumbnailViewer1.Update();
}
There was a bug in an earlier version that allowed annotations to be saved with the same unique id's. This generally doesn't cause problems for any annotations except for TextAnnotations, since they use the unique id to create a CSS class for the text editor. CSS doesn't like having two or more classes defined by the same name, this is what causes the "Permission denied" error.
You can remove the unique id's from the annotations without it causing problems. I have provided a few code snippets below that demonstrate how this can be done. Calling ResetUniques() after you load the annotation data (on the server side) should make everything run smoothly.
-Dave C. from Atalasoft
protected void ResetUniques()
{
foreach (LayerAnnotation layerAnn in WebAnnotationViewer1.Annotations.Layers)
{
ResetLayer(layerAnn.Data as LayerData);
}
}
protected void ResetLayer(LayerData layer)
{
ResetUniqueID(layer);
foreach (AnnotationData data in layer.Items)
{
LayerData group = data as LayerData;
if (group != null)
{
ResetLayer(data as LayerData);
}
else
{
ResetUniqueID(data);
}
}
}
protected void ResetUniqueID(AnnotationData data)
{
data.SetExtraProperty("_atalaUniqueIndex", null);
}

Working with SubSonic 'deleted' rows

When loading data with SubSonic (either using ActiveRecord or a collection), only records with IsDeleted set to false will load. How can I show those rows that have been deleted?
For example, deleting an Employee with:
Employee.Delete(1)
Now employee 1 is marked as deleted. Now I want the option to undo the delete and / or show a list of deleted employees, how can I do that? Either it will be undone if the user accidentally deleted the employee, or they want to go to a 'trash' list with previously deleted employees (i.e. only those with IsDeleted set to true).
Edit:
Using SubSonic 2.2
ActiveRecord doesn't have this built in. You'll need to set up additional queries for this. You didn't specify 2.2 or 3.0. This is 2.2 syntax.
public EmployeeCollection FetchAll(bool isDeleted)
{
return new SubSonic.Select().From(Employee.Schema).Where(IsDeletedColumn).IsEqualTo(isDeleted).ExecuteAsCollection<EmployeeCollection>();
}
public EmployeeCollection GetTrashList()
{
return FetchAll(true);
}
I was running into this problem yesterday with subsonic 3 and decided to alter the T4 templates to "fix" it. I added these definitions for a new function LogicalAll. As an alternative you could change the definitions of All to this but then you would have no way of getting at the deleted records.
public static IQueryable<<#=tbl.ClassName#>> LogicalAll(string connectionString, string providerName) {
<#if(tbl.HasLogicalDelete()){#>
var results = GetRepo(connectionString,providerName).GetAll();
if(results == null)
{
return new List<<#=tbl.ClassName#>>().AsQueryable();
}
return results.Where(x=> x.<#=tbl.DeleteColumn.CleanName#> == false);
<#} else {#>
return GetRepo(connectionString,providerName).GetAll();
<# } #>
}
public static IQueryable<<#=tbl.ClassName#>> LogicalAll() {
<#if(tbl.HasLogicalDelete()){#>
var results = GetRepo().GetAll();
if(results == null)
{
return new List<<#=tbl.ClassName#>>().AsQueryable();
}
return results.Where(x=> x.<#=tbl.DeleteColumn.CleanName#> == false);
<#} else {#>
return GetRepo().GetAll();
<# } #>
}
I'm running into the same issue.
I'm working in a project that's using the ActiveRecord scheme. I can retrieve logically deleted records just fine by querying for them specifically.
The problem is that the ActiveRecord generated classes do not have any properties or methods to modify the deleted status of the record.
It should be as simple as setting "IsDeleted = false" but this functionality doesn't seem to exist.
-- Nevermind on this. I regenerated my ActiveRecord class, and now my Deleted column is accessible by calling code. Must've gotten stuck somewhere.
it is easy to show these rows simply by creating a query by hand instead of using the collection loaders
ie.
ProductsCollection col = new ProductsCollection().Load();
becomes
ProductsCollection col = new Select()
.From(Tables.Products)
.ExecuteAsCollection<ProductsCollection>();
This should load everything for you. Futhermore you can set the options yourself:
ProductsCollection col = new Select()
.From(Tables.Products)
.Where(Products.Columns.IsDeleted).IsEqualTo(false)
.And(Products.Columns.IsDeleted).IsEqualTo(null)
.ExecuteAsCollection<ProductsCollection>();
This would load all the nulls (if you forgot to set your default value on your column to false) AND it will also load the falses
Hope this helps

Resources