如何把Sqlserver 里的字段的说明,直接作为Lightswitch的lsml中的字段的DisplayName?
<EntityProperty Name="SortName" PropertyType=":String?">
<EntityProperty.Attributes>
<MaxLength Value="50"/>
<DisplayName Value="类型名"/>
<Description Value="类型名描述"/>
</EntityProperty.Attributes>
</EntityProperty>
我们要解决这个需求,可以有两种方法,一种是写一个解析lsml的工具,在Lightswitch向导,导入数据库完成后,再处理一次,把字段的信息填上。
另一种方面,在crack ls,在导入时,就解决这个问题,
说点题外话,有的代码生成工具,的确会自动把字段的说明,放到生成的代码里,这点还是不错的。
这里,客观来说,前一种方法要好得多。因为,未来如果LS发生改变,我们不需要改变什么,而且,也是一个半自有版权的东西。
但出于好奇,我还是想直接从LS入手。
因为,如果找到LS如何处理lsml的代码,可以省掉许多力气。
可是,问题来了。
事实上,我这个工作,前些天,有一天我研究了大半天,没有取得进展,因为没有找到LS处理lsml 的代码,真是奇怪。
但今天,来我的贴子参观的朋友们有福了,因为我已经明白为什么那天没找到的原因。
可能是MS的LS团队,也认为lsml相关的部分是核心,所以,TMD,他们没有把相关的DLL放到VS中的IDE下的lightswich和VS根目录下的LS中。妈的,真不清楚是为什么。
MS分别把重要的几个库放到:
C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.LightSwitch.Design.CodeGen.Internal\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.LightSwitch.Design.CodeGen.Internal.dll
// Microsoft.LightSwitch.Design.CodeGen.Internal, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
D:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\
见图:
今天先写到这,如果有朋友,感兴趣,也来尽快参与反向分析工作。
应当是接近了。
我的断点打在:
而且,目前是在关闭LS工程时,触发的。
先到这里,等有进展,再给出来。
// Microsoft.LightSwitch.Designers.DataSourceDesigner.Implementation.Wizard.SelectEntitiesWizardPage
private void LoadTree(object context)
{
ISelectableEntityViewModel viewModel = context as ISelectableEntityViewModel;
SelectEntitiesWizardPage.TreeLoaded method = new SelectEntitiesWizardPage.TreeLoaded(this.OnTreeLoaded);
bool flag = this.LoadTreeSync(viewModel);
this._uiThreadDispatcher.Invoke(DispatcherPriority.Normal, method, flag);
}
// Microsoft.LightSwitch.Designers.DataSourceDesigner.Implementation.ViewModel.SelectableEntityTypeViewModel
public bool LoadTreeViewModel()
{
bool result;
try
{
this.SelectEntitiesWaitMessage = this.LoadingEntitiesWaitMessage;
this.SelectEntitiesWaitMessageVisibility = Visibility.Visible;
this.SelectEntitiesTreeModel = null;
this.StorageModel = this.RetrieveStorageModel();
lock (this.StorageModel)
{
this.SortStorageModel(this.StorageModel);
this.SelectEntitiesTreeModel = this.LoadDataEntities(this.StorageModel);
List<ITreeViewModel> list = new List<ITreeViewModel>();
foreach (ITreeViewModel current in this.SelectEntitiesTreeModel)
{
if (current.Children.Count == 0)
{
list.Add(current);
}
}
foreach (ITreeViewModel current2 in list)
{
this.SelectEntitiesTreeModel.Remove(current2);
}
if (this.SelectEntitiesTreeModel.Count == 0)
{
this.SetSelectEntitiesErrorMessage(new string[]
{
this.EmptyTreeErrorMessage
});
result = true;
}
else
{
this.HideSelectEntitiesWaitMessage();
result = true;
}
}
}
catch (DataAttachException ex)
{
this.SetSelectEntitiesErrorMessage(ex.Errors);
this.ErrorOccurredInTreeLoad = true;
result = false;
}
return result;
}
// Microsoft.LightSwitch.Designers.DataSourceDesigner.Implementation.ViewModel.DatabaseViewModel
protected override IProviderStorageModel RetrieveStorageModel()
{
EFDataAttachService eFDataAttachService = this.CreateDataProviderService() as EFDataAttachService;
if (this.dataConnectionProperties != null)
{
return eFDataAttachService.ProvideStorageModel();
}
if (this._connectionProperties != null)
{
if (this.dataConnectionProperties == null)
{
this.dataConnectionProperties = new DataConnectionProperties();
}
foreach (IConnectionPropertyDefinition current in this._connectionProperties)
{
this.SetConnectionProperty(current, this.dataConnectionProperties);
}
string value = (
from cp in this._connectionProperties
where cp.Name == "SafeConnectionString"
select cp).FirstOrDefault<ConnectionProperty>().Value;
string value2 = (
from cp in this.ConnectionProperties
where cp.Name == "ConnectionStringGuid"
select cp).FirstOrDefault<IConnectionPropertyDefinition>().Value;
Guid connectionStringGuid = DataAttachUtilities.ConvertStringToGuid(value2);
this.dataConnectionProperties.EncryptedConnectionString = this.ConnectionManager.GetEncryptedConnectionString(connectionStringGuid, value);
return eFDataAttachService.ProvideStorageModel();
}
return null;
}
// Microsoft.LightSwitch.Designers.DataSourceDesigner.Implementation.Providers.EF.EFDataAttachService
public IProviderStorageModel ProvideStorageModel()
{
IProviderStorageModel result;
try
{
string value = (
from cp in this.ConnectionProperties
where cp.Name == "ProviderInvariantName"
select cp).FirstOrDefault<ConnectionProperty>().Value;
string value2 = (
from cp in this.ConnectionProperties
where cp.Name == "SafeConnectionString"
select cp).FirstOrDefault<ConnectionProperty>().Value;
string value3 = (
from cp in this.ConnectionProperties
where cp.Name == "ConnectionStringGuid"
select cp).FirstOrDefault<ConnectionProperty>().Value;
Guid connectionStringGuid = DataAttachUtilities.ConvertStringToGuid(value3);
this._connectionStringGuid = connectionStringGuid;
ProviderStorageModel providerStorageModel = new ProviderStorageModel();
IEnumerable<string> enumerable;
EntityContainer entityContainer = EFHelper.RunEdmGen(this.ConnectionManager.GetConnectionString(connectionStringGuid, value2), value, EFHelper.CreateTablesOnlyFilter(), out enumerable);
EntityContainer entityContainer2 = EFHelper.RunEdmGen(this.ConnectionManager.GetConnectionString(connectionStringGuid, value2), value, EFHelper.CreateViewsOnlyFilter(), out enumerable);
providerStorageModel.StorageModelEntities.AddRange(ModelFragmentLoader.GetStorageModelEntities(entityContainer, ProviderStorageModelType.Table));
providerStorageModel.StorageModelEntities.AddRange(ModelFragmentLoader.GetStorageModelEntities(entityContainer2, ProviderStorageModelType.View));
this._storageModel = providerStorageModel;
result = providerStorageModel;
}
catch (DataAttachException)
{
throw;
}
catch (DataException ex)
{
throw new DataAttachException(ex.InnerException.Message, ex);
}
catch (Exception ex2)
{
throw new DataAttachException(ex2.Message, ex2);
}
return result;
}
这个最关键的部分,第一次析时,忘记了这段代码,所以后来不得不重来了一次。也好,加深的印象。
// Microsoft.LightSwitch.Designers.DataSourceDesigner.Implementation.Providers.EF.EFHelper
public static EntityContainer RunEdmGen(string connectionString, string providerInvariantName, IEnumerable<EntityStoreSchemaFilterEntry> filters, out IEnumerable<string> warningMessages)
{
EntityStoreSchemaGenerator entityStoreSchemaGenerator = new EntityStoreSchemaGenerator(providerInvariantName, connectionString, "Tbd");
IList<EdmSchemaError> source = entityStoreSchemaGenerator.GenerateStoreMetadata(filters);
IEnumerable<EdmSchemaError> enumerable =
from error in source
where error.Severity == EdmSchemaErrorSeverity.Error
select error;
int num = enumerable.Count<EdmSchemaError>();
if (num != 0)
{
string[] array = new string[num];
int num2 = 0;
foreach (EdmSchemaError current in enumerable)
{
if (current.Severity == EdmSchemaErrorSeverity.Error)
{
EFHelper.LoggingService.LogToCategory(EFHelper.LogCategory, LogEventType.Error, null, null, null, null, current.Message);
array[num2] = current.Message;
num2++;
}
}
throw new DataAttachException(array);
}
warningMessages =
from warning in source
where warning.Severity == EdmSchemaErrorSeverity.Warning
select warning.Message;
return entityStoreSchemaGenerator.EntityContainer;
}
// System.Data.Metadata.Edm.EntityType
private object _memberSqlLock = new object();
internal EntityType(string name, string namespaceName, DataSpace dataSpace, IEnumerable<string> keyMemberNames, IEnumerable<EdmMember> members) : base(name, namespaceName, dataSpace)
{
if (members != null)
{
EntityTypeBase.CheckAndAddMembers(members, this);
}
if (keyMemberNames != null)
{
base.CheckAndAddKeyMembers(keyMemberNames);
}
}
另外,许多人不明白为什么界面要自动化,老程序员都会给你答案:
因为界面太容易出bug,如果手工写,不论你多仔细,UI的bug,都会占你整个产品的80%以上。
如果,UI人员,只需要把界面风格和框架做好,如果数据出错,就是数据库的schama设计人员的错,当然是一个好的结局。
==============================
昨天回顾了一下代码,想到,最开始看到的那段,才是要找到,那段代码,解释了一个通用问题,也就是如何解析xml文件的,微软的MEF模型,其实不过是xml+schema自动解析xml 的一个实现。
也就是说,lightswitch中,根本没有刻意的解析lsml的代码,真是一个坏消息。
不过,既然是这样,也有好的一面,我们也可以直接利用.net框架,还解析lsml代码,并且转换成我们想要的实体类。
糟糕的是,昨天根到那,我没有太在意,现在还要重新去找。
-------------
首先,我们用找到MEF的EntityType类的构造函数:
System.Data.Metadata.Edm.EntityType
如图所示下断:
我们找到这段最重要的代码:
// Microsoft.LightSwitch.Designers.DataSourceDesigner.Implementation.Providers.EF.EFHelper
public static EntityContainer RunEdmGen(string connectionString, string providerInvariantName, IEnumerable<EntityStoreSchemaFilterEntry> filters, out IEnumerable<string> warningMessages)
{
EntityStoreSchemaGenerator entityStoreSchemaGenerator = new EntityStoreSchemaGenerator(providerInvariantName, connectionString, "Tbd");
IList<EdmSchemaError> source = entityStoreSchemaGenerator.GenerateStoreMetadata(filters);
IEnumerable<EdmSchemaError> enumerable =
from error in source
where error.Severity == EdmSchemaErrorSeverity.Error
select error;
int num = enumerable.Count<EdmSchemaError>();
if (num != 0)
{
string[] array = new string[num];
int num2 = 0;
foreach (EdmSchemaError current in enumerable)
{
if (current.Severity == EdmSchemaErrorSeverity.Error)
{
EFHelper.LoggingService.LogToCategory(EFHelper.LogCategory, LogEventType.Error, null, null, null, null, current.Message);
array[num2] = current.Message;
num2++;
}
}
throw new DataAttachException(array);
}
warningMessages =
from warning in source
where warning.Severity == EdmSchemaErrorSeverity.Warning
select warning.Message;
return entityStoreSchemaGenerator.EntityContainer;
}
好了,本文告一段落,目的已达成。
我们找到了解析lsml最关键的代码。
对于xml 文件解析来说,最关键的就是找到Schema部分,了解过编译器过是学过记算机语言学的人,都理解是怎么回事。
计算机,在解析xml时,好象很神奇,它事先不清楚xml的内容,为什么能“理解”它呢?其它与编译原理是同一套理论,没有任何区别。
现在我们找到了,MEF的底层,如何操作schema的部分所在位置,也找到了调用的代码,后面可以根据自己的想法来尝试了。
在结束前,这里提供与本文相关的ApplicationDefinition.lsml
最重要的是这段:
<ModelFragment xmlns="http://schemas.microsoft.com/LightSwitch/2010/xaml/model" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<EntityType Name="GiftAddRecord">
。。。