Add or remove column mapping at runtime for linq2db - linq2db

I have a scenario where if certain features are deployed then a number of columns will be there in some tables otherwise won't so the mapping of Entities and Columns is not static. I need to add/remove the mapping at runtime. Is there any way?

Prepare a new MappingSchema and pass to DataConnection constructor.
Consider you have the following class:
[Table]
class SampleClass
{
[Column] public int Id { get; set; }
[Column] public int Value { get; set; }
}
To remove column from full object materialization, do the following:
var ms = new MappingSchema();
ms.GetFluentMappingBuilder()
.Entity<SampleClass>().Property(e => e.Value).IsNotColumn();
// cache somewhere this schema
using (var db = new DataConnection(ms))
{
var result = db.GetTable<SampleClass>().ToArray();
}
Remember, better to cache this new MappingSchema and reuse. Otherwise you will never have cache hit and you'll lose performance.

Related

EF 6.0 - Map complex type to same table as parent property

I have looked at the following:
Entity framework code first map multiple complex types of the same type to a table
Given the example code:
[Table("TXLifeRequest", Schema = "txlife")]
public partial class TXLifeRequest
{
public virtual OLI_LU_BOOLEAN PendingResponseOK { get; set; }
...
}
[Table("OLI_LU_BOOLEAN", Schema = "txlife")]
public partial class OLI_LU_BOOLEAN {
public string tc { get; set; }
public string Value { get; set; }
}
I would like to structure the database so that the OLI_LU_BOOLEAN is not in a new table, rather to be two new columns in the TXLifeRequest table as something like TXLifeRequest.PendingResponseOK_tc and PendingResponseOK _Value.
There is no fluent code in the existing context. Is there a way to do this either by fluent or attrubutes so that the class structure is intact but the tables are combined?
Update:
I have tried the following but it creates a new table TXLifeRequest1 for all of the OLI_LU_BOOLEAN properties. How would I specify these as properties of same table?
modelBuilder.ComplexType<OLI_LU_BOOLEAN>()
CreateTable("imsparamed.TXLifeRequest1",
c => new
{
Id = c.Int(nullable: false, identity: true),
PendingResponseOK_Value = c.String(),
PendingResponseOK_Id = c.Int(nullable: false)
})
The solution is to create a complex type:
modelBuilder.ComplexType<OLI_LU_BOOLEAN>().Ignore(i => i.Value);

Updating many-to-many relationship entity framework

I have problem with updating entites that have many-to many relationship. Below my User and category class:
public class User : IEntity
{
[Key]
public virtual long Id { get; set; }
private ICollection<Category> _availableCategories;
public virtual ICollection<Category> AvailableCategories
{
get { return _availableCategories ?? (_availableCategories = new List<Category>()); }
set { _availableCategories = value; }
}
}
public class Category : IEntity
{
[Key]
public long Id { get; set; }
/// <summary>
/// Full name or description of a category
/// </summary>
[StringLength(255)]
public string FullName { get; set; }
}
This is code snippet from my repository
public override void Edit(User user)
{
var dbUser = _context.Users.Include(x => x.AvailableCategories)
.Single(x => x.Id == user.Id);
var categories = _context.Categories;
dbUser.AvailableCategories.Clear();
foreach (var cat in user.AvailableCategories)
{
dbUser.AvailableCategories.Add(cat);
}
_context.Entry(dbUser).State = EntityState.Modified;
}
However the categories don't get updated. What EF does is insert empty rows into category table and sets relations to this new rows with user.
How can I update User so that I change only categories that already exist in the database?
User that I pass to Edit method has AvailableCategories with only Ids set (rest of properties are empty).
When you're doing something like posting back M2M relationships, you either must post the full object, as in every single property on those objects, or simply post a list of ids and then use those to query the associated objects back from the database. Otherwise, Entity Framework understands your purpose to be to update the properties on the objects as well, in this case with empty values.
Obviously the first option is quite unwieldy, so the second way is the preferred and standard way. Generally, for this, you'd want to use a view model so you could have a property like the following, that you would post into:
public List<long> SelectedCategories { get; set; }
But, if you insist on using the entity directly, you can get much the same result by simply doing:
var selectedCategories = user.AvailableCategories.Select(m => m.Id)
Once you have the ids:
var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
And then finally set that on your user:
dbUser.AvailableCategories = newAvailableCategories;
I notice you are also adding the user.AvailableCategories directly into dbUser.AvailableCategories. I've noticed when binding back complex objects from an MVC view that DB Entities are no longer attached to the DbContext. If you look at the entity, you can verify by checking dbContext.Entry(cat).State is "detached" (or something unexpected) I believe.
You must query those entities back out of the dbContext (possibly by using the returned cat.Id's). Or otherwise manually set the entities as "unchanged". And then add those "non-detached" items into dbUser.AvailableCategories. Please see Chris's answer as it shows with specific code how to get this done.
Also, I might use a linking entity. Possibly something like this:
public class UserCategory
{
public User User {get;set;}
public Category Category {get;set;}
}
And add it to DB context. Also, drop the linking lists in your current User and Category class. This way you can manipulate the UserCategory class (and DbSet) to manage your many-to-many relationship.

ASP.NET MVC Webgrid display multiple columns in a single row

I have a Asp.net MVC grid.
My problem is I need to display multiple columns in a single row.
For example:
Name Date Compensation
Id USD - 99999
Grade INR - 99999
The above layout is a single row in the grid.
All the columns (Name, Id, Grade, Curency1, Amount1, Currency2, Amount2 ) are available in a single record as separate columns. Here Currency1 means USD and Currency2 means INR.
Any ideas how to do this. I am using a strongly typed model and EF6.
I think the best way to do this would be to create a separate 'type' and model for each multi-faceted column, then try to display this type in the webgrid (which I show is possible in the latter part of my example).
For example:
Create a new 'type' (or 'column') class called CompensationColumn:
...
using System.Web.Mvc;
namespace yourproject.Columns // I put this in its own namespace/folder - you don't have to
{
public class CompensationColumn
{
public string Currency1 { get; set; }
public int Amount1 { get; set; }
public string Currency2 { get; set; }
public int Amount2 { get; set; }
public CompensationColumn(string currency_1, int amount_1, string currency_2, int amount_2)
{
Currency1 = currency_1;
Amount1 = amount_1;
Currency2 = currency_2;
Amount2 = amount_2;
}
}
}
Then create a file called CompensationColumn.cshtml in the yourproject/Shared/EditorTemplates folder (if the Shared folder doesn't exist you can also create a your view/DisplayTemplates folder). Define how this column will look, as if it was a custom 'type' (modify this to your liking):
#model yourproject.Columns.CompensationColumn
#if (Model != null)
{
#Model.Currency1<text> - </text>#Model.Amount1<text><p/></text>
#Model.Currency2<text> - </text>#Model.Amount2
}
else
{
}
Then in your Models folder, create a partial class to extend your current EF table model (file name shouldn't matter). I am going to assume your table is 'employee_table'. I am also adding Metadata for the model in this class as it is a good place to put it if you are using a database-first design:
using System.Web.Mvc;
using yourproject.Columns;
namespace yourproject.Models
{
[MetadataType(typeof(EmployeeModelMetaData))] // This links the metadata class below
public partial class employee_table // This should be the EF class name
{
[DisplayName("Compensation")]
public CompensationColumn Compensation { get; set; } // Here we add a new field for your row
}
public class EmployeeModelMetaData
{
// copy your EF class fields here and decorate them with dataannotations. This is helpful
// if you are using a database-first design as it won't get overwritten when db changes.
[DisplayName("Id")]
public int emp_id { get; set; }
[DisplayName("Amount")]
[DisplayFormat(DataFormatString = "{0:c}", ApplyFormatInEditMode = true)]
public int emp_amount1 { get; set; }
// etc . . .
}
}
I make a few assumptions here about a database-first design, but you should be able to figure out how to adapt it to a code-first design if needed.
If you also need to edit elements this column type together, then you would need to create a model binder, but I'm not going there since you only mentioned displaying it.
To get the display template to display in the webgrid, you will need to format: the columns of the webgrid. In your view with an IEnumerable model (e.g. your Index view):
#{
var grid = new WebGrid(Model);
List<WebGridColumn> columns = new List<WebGridColumn>();
WebGridColumn col = grid.Column(columnName: "Col3", header: "Compensation", format: (item) =>
{
yourproject.Columns.CompensationColumn c = item.Compensation; return Html.DisplayFor(model => c);
} );
columns.Add(col);
}
#grid.GetHtml(columns: columns)
This last snippet I adapted from Frédéric Blondel's code here

How to get all information aggregated with nhibernate

Take a look the classes below:
public class Produt
{
public virtual int id { get; set; }
public virtual string name { get; set; }
[ScriptIgnore]
public virtual Unit unit { get; set; }
}
public class Unit
{
public virtual int id { get; set; }
public virtual string name { get; set; }
public virtual IList<Produt> produts { get; set; }
}
After this, the mappings:
public partial class ProdutMap : ClassMap<Produt>
{
public ProdutMap()
{
Id(x => x.id).GeneratedBy.Identity();
Map(x => x.name).Length(100).Not.Nullable();
References(x => x.unit, "idUnit").Cascade.All().LazyLoad();
}
}
public partial class UnitMap : ClassMap<Unit>
{
public UnitMap()
{
Id(x => x.id).GeneratedBy.Identity();
Map(x => x.name).Length(100).Not.Nullable();
HasMany(x => x.produts).Cascade.All().KeyColumns.Add("idUnit").LazyLoad();
}
}
Now, imagine that I want to execute this query:
SELECT produt.id, produt.name, unit.name FROM Produt, Unit WHERE produt.idUnit = unit.id
with nhibernate? How to do? Something help?
P.S. The [ScriptIgnore] is because I had problems with circular references. My classes are not only these. This is just an example.
Just fetch Products
The simplest way to do this is to just fetch a list of Products. Product already contains all of the information you need because it has a reference to Unit.
// NOTE: This fetches ALL products. You really should limit this.
var products = session.Query<Product>();
foreach (var product in products)
Console.WriteLine("Product: {0}, Unit: {1}", product.Name, product.Unit.Name);
On each iteration of this loop, if product.Unit points to a unit that NHibernate has not fetched yet, NHibernate will lazily execute another query to fetch that unit.
Sometimes you are not able to use lazy loading - perhaps you need to close the NHibernate session before iterating over the results. Also, for performance it would be better to reduce the number of round-trips to the database. We can fix these problems by changing our query like so:
var products = session.Query<Product>().Fetch(x => x.Unit);
Flatten your results
If for some reason you need flattened result objects where you don't have to dig through nested objects to get the data you need, you can use "projections" to do this. With LINQ, this looks like:
var productInfos = session.Query<Product>().Select(x => new
{
ProductId = x.Id,
ProductName = x.Name,
UnitName = x.Unit.Name
});
This is also useful if you need to limit the columns returned by NHibernate - for example, if one of the column contains huge BLOBs that you want to avoid fetching.
Besides LINQ, NHibernate has several different ways to execute queries: native SQL, HQL, Criteria, and QueryOver. For Criteria or QueryOver, the AliasToBean result transformer will help you when executing these types of queries. See this related question for an example using AliasToBean in a Criteria query: NHibernate - Only retrieve specific columns when using Critera queries?

DeleteOnNull Error

I've got a set of DB objects sitting in an EntitySet on my main object definition. This handles additions and updates fine, but I found the removing items from the list didn't result in the database records being deleted, so I had to create a method in the data repository object to delete the records as the data object doesn't have access to the data-context in which it is being used.
I was looking to see if I could bring this delete into the main object and I found the DeleteOnNull attribute to the association, but when I use it, I get an error "DeleteOnNull can only be true for singleton association members mapped to non-nullable foreign key columns". My code is:
private EntitySet<UserSite> _userSites = new EntitySet<UserSite>();
[Association(Name = "User_UserSites", Storage = "_userSites", ThisKey = "UserID", OtherKey = "UserID", DeleteOnNull=true)]
public IList<UserSite> UserSites { get { return _userSites; } set { } }
my usersite object is
[Table(Name="UserSite")]
public class UserSite
{
[Column]//(IsPrimaryKey = true)]
public int UserID { get; set; }
[Column]//(IsPrimaryKey = true)]
public string Site { get; set; }
[Column]
public bool DefaultSite { get; set; }
[Column(IsPrimaryKey = true, AutoSync = AutoSync.OnInsert)]
public int UniqueID { get; set; }
}
Can I use DeleteOnNull to keep all my data update methods within my main user object, or do I have to handle the deletes at the repository level?
DeleteOnNull is only for singleton associations. So you can put it on UserSite.User but not on User.UserSites. It's still not quite as automatic as you'd like it to be, though. There is an example here.
It's hard for LINQ to SQL to infer the behavior you want, because it can't guess if you want composition or aggregation, so it chooses the safe guess (aggregation).

Resources