Order of fields in Dynamic Data? - asp.net

Does anybody know if it is possible to choose the order of the fields in Dynamic Data (of course, without customizing the templates of each table) ?
Thanks !

In .NET 4.0, using the 4.0 release of the Dynamic Data dll, you can set data annotations like so:
[Display(Name = " Mission Statement", Order = 30)]
public object MissionStatement { get; set; }
[Display(Name = "Last Mod", Order = 40)]
public object DateModified { get; private set; }

As per this thread - you can use the ColumnOrderAttribute in the dynamic data futures dll. You can grab the futures from codeplex.

You can do this by modifying the order of the public properties in your LINQ to SQL file.
For example, I went into Northwind.designer.cs which was my auto-generated LINQ to SQL file and moved the public property named Products above the public property CategoryName in the public partial class Category. Then I recompiled and the default template displayed the columns in my new order.
Of course, this means your editing auto-generated code and if you regenerate it, your changes are lost, so this technique is not without peril.

You have to create a custom page in DynamicData folder.
Here are the steps:
Create a folder that is the same name as your table name that you want to customize the ordering of columns under "DynamicData\CustomPages" folder
Create a custom page under "DynamicData\CustomPages\[folder with table name]" folder.
I just copy the existing "List.aspx" file from "DynamicData\PageTemplates" into the folder above.
Open the aspx file and modify GridView control to "AutoGenerateColumns='false'"
Inside columns section of GridView, add "DynamicControl" controls with the "DataField" attribute value to the name of your column in the order you want.
Here is a screencast from ScottHa:
http://www.asp.net/learn/3.5-SP1/video-293.aspx

GridView have ColumnsGenerator property, use it by implementing GenerateFields method of IAutoFieldGenerator interface in which you can set fields orders based on your custom rules (attributes, meta info, ...)
protected override void OnInit(EventArgs e)
{
...
this.gvItemsList.ColumnsGenerator = new EntityFieldsGenerator(CurrentDataSource.CurrentTableMetadata);
...
}
public class EntityFieldsGenerator : IAutoFieldGenerator {
...
public ICollection GenerateFields(Control control)
{
// based on entity meta info
var fields = from item in this.entityMetadata.Columns
where this.IncludeColumn(item.Value)
orderby item.Value.Order
select new DynamicField
{
DataField = item.Value.Column.Name,
HeaderText = item.Value.DisplayName,
DataFormatString = item.Value.DataFormatString,
UIHint = GetColumnUIHint(item.Value)
};
return fields.ToList();
} }

To avoid using the Dynamic Data futures dll, you can roll your own ColumnOrder attribute as follows:
[AttributeUsage(AttributeTargets.Property)]
public class ColumnOrderAttribute : Attribute
{
public int Order { get; private set; }
public ColumnOrderAttribute() { Order = int.MaxValue; }
public ColumnOrderAttribute(int order) { Order = order; }
public static ColumnOrderAttribute Default = new ColumnOrderAttribute();
}
and then in your class that implements IAutoFieldGenerator, you have
public static class ExtensionMethods
{
public static int GetOrder (this MetaColumn column)
{
var orderAttribute = column.Attributes.OfType<ColumnOrderAttribute>().DefaultIfEmpty(ColumnOrderAttribute.Default).Single();
return orderAttribute.Order;
}
}
public ICollection GenerateFields(Control control)
{
var fields = new List<DynamicField>();
var columns = _table.Columns.OrderBy(column => column.GetOrder());
foreach (var column in columns)
{
if (!column.Scaffold) { continue; }
fields.Add(new DynamicField {DataField = column.Name});
}
}
and finally your usage would look like
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer {}
public class CustomerMetadata
{
[ColumnOrder(1)]
public object FirstName {get;set;}
[ColumnOrder(2)]
public object LastName {get;set;}
}

I'm answering an old question because it seems to me that the possible solution changed in newer versions of the framework.
It seems that the Display(Order) works now directly as asked (Visual Web Developer 2010 on .NET 4.0) without any particular workaround.
Example:
[Display(Order = 50)]
An important thing it's to check the correct object name to map the foreignkey:
in one project a field OperatoreID translated in the entity class as:
public object Operatore { get; set; }
being Operatore the source table of the foreignkey; for a second reference on the same table it will get something like 1 and so on.

Related

Unwanted unique constraint in many to many relationship

I'm trying to set up a Tagging tool for images. Basically I have two tables, one for pictures, and one for tags. Both are connected with a many to many setup. I can already add a single tag to a picture, and the same tag to different pictures. However, when I try to add a second tag to an image I get an exception complaining about a unique constraint that I simply don't see.
public class MediaEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<TagEntity> Tags { get; set; }
}
public class TagEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<MediaEntity> MediaEntities { get; set; }
}
public void updateMedia(MediaEntity model)
{
using (var db = new MediaContext(_dbLocation))
{
db.Update(model);
db.SaveChanges();
}
}
public class MediaContext : DbContext
{
private const string DB_NAME = "PT.db";
private string _path;
public DbSet<MediaEntity> MediaTable { get; set; }
public DbSet<TagEntity> TagTable { get; set; }
public MediaContext(string path)
{
_path = path;
ChangeTracker.AutoDetectChangesEnabled = false;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={Path.Combine(_path, DB_NAME )}");
}
As far as I can tell my setup should create a normal many-to-many relationship, and it the database I also see pretty much this. EF automatically creates a TagTable, MediaTable, and MediaEntityTagEntityTable. But when I try to add a second tag I get this:
SqliteException: SQLite Error 19: 'UNIQUE constraint failed:
MediaEntityTagEntity.MediaEntitiesId, MediaEntityTagEntity.TagsId'.
Data from the table showing I can have the same tag on different pictures:
MediaEntitiesId
TagEntitiesId
1B48E85B-F097-4216-9B7A-0BA34E69CBFF
CF581257-F176-4CDF-BF34-09013DCEAA27
CE33F03F-5C80-492B-88C6-3C40B9BADC6C
CF581257-F176-4CDF-BF34-09013DCEAA27
523178A1-C7F8-4A69-9578-6A599C1BEBD5
0C45C9D1-7576-4C62-A495-F5EF268E9DF8
I don't see where this unique constaint comes in. How can I set up a proper many-to-many relationship?
I suspect the issue you may be running into is with the detached Media and associated Tags you are sending in. You are telling EF to apply an 'Update' to the media, but the DbContext will have no idea about the state of the Tags attached. Assuming some tags may have been newly attached, others are existing relationships. If the Context isn't tracking any of these Tags, it would treat them all as inserts, resulting in index violations (many to many) or duplicate data (many to one / one to many)
When dealing with associations like this, it is generally simpler to define more atomic actions like: AddTag(mediaId, tagId) and RemoveTag(mediaId, tagId)
If you are applying tag changes along with potential media field updates in a single operation I would recommend rather than passing entire entity graphs back and forth, to use a viewModel/DTO for the tag containing a collection of TagIds, from that apply your tag changes against the media server side after determining which tags have been added and removed.
I.e.:
public void updateMedia(MediaViewModel model)
{
using (var db = new MediaContext(_dbLocation))
{
var media = db.Medias.Include(x => x.Tags).Single(x => x.MediaId = model.MedialId);
// Ideally have a Timestamp/row version number to check...
if (media.RowVersion != model.RowVersion)
throw new StaleDataException("The media has been modified since the data was retrieved.");
// copy media fields across...
media.Name = model.Name;
// ... etc.
var existingTagIds = media.Tags
.Select(x => x.TagId)
.ToList();
var tagIdsToRemove = existingTagIds
.Except(model.TagIds)
.ToList();
var tagIdsToAdd = model.TagIds
.Except(existingTagIds)
.ToList();
if(tagIdsToRemove.Any())
media.Tags.RemoveRange(media.Tags.Where(x => tagIdsToRemove.Contains(x.TagId));
if(tagIdsToAdd.Any())
{
var tagsToAdd = db.Tags.Where(x => tagIdsToAdd.Contains(x.TagId)).ToList();
media.Tags.AddRange(tagsToAdd);
}
db.SaveChanges();
}
}
Using this approach the DbContext is never left guessing about the state of the media and associated tags. It helps guard against stale data overwrites and unintentional data tampering (if receiving data from web browsers or other unverifiable sources), and by using view models with the minimum required data, you improve performance by minimzing the amount of data sent over the wire and traps like lazy load hits by serializers.
I always explicitly create the join table. The Primary Key is the combination of the two 1:M FK attributes. I know EF is supposed to map automatically, but since it isn't, you can specify the structure you know you need.

Data always filled by null in dbcontext in .net core 3.1 api

Dears, I am using a dbfirst API . net core 3.1.
I created a migration file to apply stored procedure, and it is successfully created.
protected override void Up(MigrationBuilder migrationBuilder)
{
string procedure1 = #"CREATE PROCEDURE [dbo].[Spz_order] #client varchar(10) as
SELECT .....
having ... =#client";
migrationBuilder.Sql(procedure1);
}
Then I created a model or class to receive the data in it
[NotMapped]
public partial class categoryStock
{
public string model { get; }
public int quantity { get; }
}
Then a dbset
public virtual DbSet<categoryStock> Categories { get;}
Then in controller
[HttpGet("getCategoryStock")]
public async Task<List<categoryStock>> getCategoryStock(string cat)
{
using (_context)
{
if (_context.Categories != null)
{
var category =await _context.Categories
.FromSqlRaw("EXECUTE Spz_order {0}", cat)
.ToListAsync();
return category.ToList();
}
return new List<categoryStock>();
}
}
BUT i always get data by null, can any one help me to solve this issue
Thanks in advance
First, please check your database, make sure the data table contains records.
Second, please check your query statement in the stored procedure, make sure the query result is not null.
Third, please check the parameter value, make sure it is correct and you can find records according to the value.
Edit:
Perhaps the issue is related to the [NotMapped] attribute. The NotMapped attribute is used to specify that an entity or property is not to be mapped to a table or column in the database.
According to your code, I have created a model without using the NotMapped attribute, then, I can call the stored procedure and get the data.
Code as below:
public IActionResult Index()
{
var title = "G";
var param = new SqlParameter("#Title", "Rio");
//var movies = context.Movie
// .FromSqlRaw("GetMovies #Title", param)
// .ToList();
var movies = context.Movie
.FromSqlRaw("EXECUTE GetMovies {0}", title)
.ToList();
return View();
}
The screenshot:
So, try to remove the [NotMapped] attribute.

Asp.net MVC change css on publish

We have an MVC application that is deployed as 3 different versions in production (seperate business areas demand seperate DBs and UIs). The only problem is that a few users use more than 1 of these applications and because they look vsually similar people get confused as to which one they are using.
I use web.config transforms to change the app title but what would like to do is deliver each one with a different css file. Is there a way to:
Use transforms on publish to edit an existing css file?
or
Swap the css file for another on publish?
Any help pointing me in the right direction would be great.
Thanks
What I did in a similar case : I dynamicly put a class on the body <body class="theme-site#AppSettingParam"> and i have only one CSS with override like :
.theme-site1 { background-color:blue; }
.theme-site2 { background-color:red; }
if a user uses multiple sites, it does not re-download the css and i have only one css to maintain.
This isn't the answer you're looking for right now, but if you do reconsider using the same site with different configurations, take a look at what I did recently to dynamically change CSS etc:
I decided that each 'version' of the website would use a unique reference in the query string. Based on this, I'll find the correct content, load the paths into a Model and send it to the view.
Here's the controller:
public ActionResult Index()
{
List<string> listOfAcceptableRef = new List<string>() { "uniqueCode1", "uniqueCode2" };
string uniqueRef=null;
if (Request.QueryString["ref"]!=null)
policyRef = Request.QueryString["ref"].ToLower();
if (string.IsNullOrWhiteSpace(uniqueRef) || (!string.IsNullOrWhiteSpace(uniqueRef) && !listOfAcceptableRef.Contains(uniqueRef)))
{
throw new Exception("This reference key is unknown.");
//return RedirectToAction("KeyError");
}
return View(GetPageContext(uniqueRef));
}
Grab the reference code from the query string and then generate a model containing the relevant CSS paths from a context factory.
Here's my model:
public class PageContext
{
public string Ref { get; set; }
public string TabId { get; set; }
public string TabName { get; set; }
public string SiteTitle { get; set; }
public string CssPath { get; set; }
public PageContext()
{
Products = new List<ProductInfo>();
}
}
And my context factory:
public class ContextFactory<T>
{
private ContextFactory()
{
}
static readonly Dictionary<string, Type> _dict = new Dictionary<string, Type>()
{
{ "uniqueRef1", Type.GetType("The.Full.Page.Namespace.UniqueSite1Context")},
{ "uniqueRef2", Type.GetType("The.Full.Page.Namespace.UniqueSite2Context")}
};
public static bool Create(string reference, out T context)
{
context = default(T);
Type type = null;
if (_dict.TryGetValue(reference, out type))
{
context = (T)Activator.CreateInstance(type);
return true;
}
return false;
}
}
And the actual context instance with the CSS paths etc:
public class UniqueSite1Context : PageContext
{
public UniqueSite1Context()
{
this.Ref = "uniqueSite1";
this.CssPath = "Content/UniqueSite1Context.css";
this.DisclaimerPath = "Content/UniqueSite1Context.pdf";
this.SiteTitle = "UniqueSite1";
}
}
After all that, just render the CSS using the model's paths:
#section Styles {
#{
string path = Url.Content("~") + Model.CssPath;
<link href="#path" rel="stylesheet" type="text/css" />
}
}
Architecturally speaking, you could extend this (or rather the concept of it) to use different logic and data contexts based on which 'site' the user goes to.
Have you considered transform files? They change the settings in your web.config file when you publish, based on the publish profile selected. Typically the profiles available by default is "Debug" and "Release", but you could add more like "Site1" and "Site2" with different CSS paths.
Take a look at how they work here. (I haven't used them myself so I can't help much in the way of code examples).

Get EF 4 database first object property annotation

We are using EF4 database first approach to create all the entities as found in the context class. I'm now trying to add a display name attribute to one of the objects' properties as follows:
[MetadataType(typeof(OpportunityMetaData))]
public partial class Opportunity : EntityObject
{
}
public class OpportunityMetaData
{
[Display(Name = "Worked By")]
public int WorkedById { get; set; }
}
Then on a test page, using reflection, I'm trying to get an output that says "Worked By", as follows:
var attrType = typeof(DisplayNameAttribute);
var property = typeof(Opportunity).GetProperty("WorkedById");
Response.Write(((DisplayNameAttribute)property.GetCustomAttributes(attrType, false).FirstOrDefault()).DisplayName);
But this just gives Object Reference not set to an instance of an object. Alternatively, if I just Response.Write the property, it writes out "WorkedById" and not "Worked By".
Any help would be appreciated.
Its DisplayAttribute, not DisplayNameAttribute. Name is just a property on it.

Generate a form from a poco object

Say you had a config object
public class MyConfig{
public int PageSize{get;set;}
public string Title{get;set;}
}
and you want to automatically generate a asp.net form to edit the properties on this object.
Do you know of any frameworks to do this automagically?
I know of MS Dynamic data, but seems I need to have the whole stack (database, linq, objects) to get this up and running. So I was thinking of something simpler..
Sorry for jumping in late. There are several ways to use Dynamic Data with POCO.
Use the DynamicObjectDataSource which is found in Futures and Preview releases of Dynamic Data, starting with July 2008 Futures. When looking in a Preview release, it contains a Futures assembly, Microsoft.Web.DynamicData.dll.
When using ASP.NET 4.0 (now in Beta), you can call a new extension method, EnableDynamicData(). See the "SimpleDynamicDataSamples" project that comes with DD Preview 4 and later.
Here's an example from that code that uses an ObjectDataSource and the POCO class called "Product".
[MetadataType(typeof(Product.Metadata))]
public partial class Product {
public class Metadata {
[Required]
public string ProductName { get; set; }
[Range(0, 100)]
public decimal UnitPrice { get; set; }
}
}
public partial class ObjectDataSourceSample : System.Web.UI.Page {
protected void Page_Init() {
// Extension method syntax
ProductsList.EnableDynamicData(typeof(Product));
// Explicit syntax
// MetaTable table = MetaTable.CreateTable(typeof(Product));
// MetaTable.MapControl(ProductsList, table);
// ProductsList.ColumnsGenerator = new DefaultAutoFieldGenerator(table);
}
}
I was under the impression that you could modify the T4 templates used by dynamic data (Not sure if you can remove the data access part).
Have you looked at just using T4 on its own.

Resources